Update FixedLayout. Add demo launcher.
from tuikit.core.container import Container
from tuikit.layouts.offset import OffsetLayout
#from tuikit.layout import AnchorLayout, OffsetLayout
#from tuikit.scrollbar import VScrollbar, HScrollbar
#from tuikit.common import Borders
class Viewport(Container):
"""Viewport for partial view on widgets.
Viewport is special type of Container, which shows partial view
on its managed widgets. This view can be moved to show different part
and thus allow "scrolling" over the content. Complete view with scrollbars
is implemented in ScrollView.
"""
def __init__(self):
Container.__init__(self, OffsetLayout)
class Scrolling:
def __init__(self):
self.vscroll = VScrollbar()
self.vscroll.add_handler('change', self._on_vscroll_change)
AnchorLayout.add(self, self.vscroll, halign='right', valign='fill')
self.hscroll = HScrollbar()
self.hscroll.add_handler('change', self._on_hscroll_change)
AnchorLayout.add(self, self.hscroll, halign='fill', valign='bottom')
def on_resize(self, ev):
self._update_scroll_max()
def _connect_child(self, widget):
widget.add_handler('sizereq', self._on_child_sizereq)
widget.add_handler('spotmove', self._on_child_spotmove)
widget.add_handler('scrollreq', self._on_child_scrollreq)
def _on_child_sizereq(self, ev):
self._update_scroll_max()
def _on_child_spotmove(self, ev):
child = ev.originator
# x
spot_x = child.x - self._inner.offset.x + child.spot.x
if spot_x < self.hscroll.scroll_pos:
self.hscroll.scroll_pos = spot_x
if spot_x > (self._inner.width - 1) + self.hscroll.scroll_pos:
self.hscroll.scroll_pos = spot_x - (self._inner.width - 1)
# y
spot_y = child.y - self._inner.offset.y + child.spot.y
if spot_y < self.vscroll.scroll_pos:
self.vscroll.scroll_pos = spot_y
if spot_y > (self._inner.height - 1) + self.vscroll.scroll_pos:
self.vscroll.scroll_pos = spot_y - (self._inner.height - 1)
def _on_child_scrollreq(self, ev):
new_scroll_pos = self.vscroll.scroll_pos + ev.data
if new_scroll_pos > self.vscroll.scroll_max:
self.vscroll.scroll_pos = self.vscroll.scroll_max
elif new_scroll_pos < 0:
self.vscroll.scroll_pos = 0
else:
self.vscroll.scroll_pos = new_scroll_pos
def _on_vscroll_change(self, ev):
self._inner.offset.y = - self.vscroll.scroll_pos
def _on_hscroll_change(self, ev):
self._inner.offset.x = - self.hscroll.scroll_pos
def _update_scroll_max(self):
max_width = 0
max_height = 0
for child in self._inner.children:
posx, posy = child.hint_value('position')
child_width = posx + child.sizereq.w
if child_width > max_width:
max_width = child_width
child_height = posy + child.sizereq.h
if child_height > max_height:
max_height = child_height
max_width += 1
self.hscroll.scroll_max = max_width - self._inner.width
self.vscroll.scroll_max = max_height - self._inner.height
hscroll_hide = max_width < self._inner.width
vscroll_hide = max_height < self._inner.height
self._toggle_scrollers(hscroll_hide, vscroll_hide)
def _toggle_scrollers(self, hscroll_hide, vscroll_hide):
if hscroll_hide:
self.hscroll.hide()
else:
self.hscroll.show()
if vscroll_hide:
self.vscroll.hide()
else:
self.vscroll.show()
# self._inner.need_resize()
class ScrollView(OffsetView, Scrolling):
"""Scrolling view
Shows scrollbars when needed.
Scrolling area is determined according to child widget's size request.
"""
def __init__(self):
OffsetView.__init__(self)
Scrolling.__init__(self)
def add(self, widget, **kwargs):
OffsetView.add(self, widget, **kwargs)
self._connect_child(widget)
def _toggle_scrollers(self, hscroll_hide, vscroll_hide):
bpad = int(not hscroll_hide)
self.vscroll.update_hint('margin', b=bpad)
rpad = int(not vscroll_hide)
self.hscroll.update_hint('margin', r=rpad)
self._inner.update_hint('margin', b=bpad, r=rpad)
Scrolling._toggle_scrollers(self, hscroll_hide, vscroll_hide)