1 # -*- coding: utf-8 -*- |
1 # -*- coding: utf-8 -*- |
2 |
2 |
3 from tuikit.layout import AnchorLayout, OffsetLayout |
3 from tuikit.layout import AnchorLayout, OffsetLayout |
4 from tuikit.scrollbar import VScrollbar |
4 from tuikit.scrollbar import VScrollbar, HScrollbar |
5 from tuikit.common import Borders |
5 from tuikit.common import Borders |
6 |
6 |
7 |
7 |
8 class ScrollView(AnchorLayout): |
8 class OffsetView(AnchorLayout): |
|
9 def __init__(self): |
|
10 AnchorLayout.__init__(self) |
|
11 self._default_size.update(20, 20) |
|
12 self._inner = OffsetLayout() |
|
13 AnchorLayout.add(self, self._inner, halign='fill', valign='fill', margin=Borders(r=1)) |
|
14 |
|
15 def add(self, widget, **kwargs): |
|
16 self._inner.add(widget, **kwargs) |
|
17 |
|
18 |
|
19 class Scrolling: |
|
20 def __init__(self): |
|
21 self.vscroll = VScrollbar() |
|
22 self.vscroll.add_handler('change', self._on_vscroll_change) |
|
23 AnchorLayout.add(self, self.vscroll, halign='right', valign='fill') |
|
24 |
|
25 self.hscroll = HScrollbar() |
|
26 self.hscroll.add_handler('change', self._on_hscroll_change) |
|
27 AnchorLayout.add(self, self.hscroll, halign='fill', valign='bottom') |
|
28 |
|
29 def on_resize(self, ev): |
|
30 self._update_scroll_max() |
|
31 |
|
32 def _connect_child(self, widget): |
|
33 widget.add_handler('sizereq', self._on_child_sizereq) |
|
34 widget.add_handler('spotmove', self._on_child_spotmove) |
|
35 widget.add_handler('scrollreq', self._on_child_scrollreq) |
|
36 |
|
37 def _on_child_sizereq(self, ev): |
|
38 self._update_scroll_max() |
|
39 |
|
40 def _on_child_spotmove(self, ev): |
|
41 child = ev.originator |
|
42 # x |
|
43 spot_x = child.x - self._inner.offset.x + child.spot.x |
|
44 if spot_x < self.hscroll.scroll_pos: |
|
45 self.hscroll.scroll_pos = spot_x |
|
46 if spot_x > (self._inner.width - 1) + self.hscroll.scroll_pos: |
|
47 self.hscroll.scroll_pos = spot_x - (self._inner.width - 1) |
|
48 # y |
|
49 spot_y = child.y - self._inner.offset.y + child.spot.y |
|
50 if spot_y < self.vscroll.scroll_pos: |
|
51 self.vscroll.scroll_pos = spot_y |
|
52 if spot_y > (self._inner.height - 1) + self.vscroll.scroll_pos: |
|
53 self.vscroll.scroll_pos = spot_y - (self._inner.height - 1) |
|
54 |
|
55 def _on_child_scrollreq(self, ev): |
|
56 new_scroll_pos = self.vscroll.scroll_pos + ev.data |
|
57 if new_scroll_pos > self.vscroll.scroll_max: |
|
58 self.vscroll.scroll_pos = self.vscroll.scroll_max |
|
59 elif new_scroll_pos < 0: |
|
60 self.vscroll.scroll_pos = 0 |
|
61 else: |
|
62 self.vscroll.scroll_pos = new_scroll_pos |
|
63 |
|
64 def _on_vscroll_change(self, ev): |
|
65 self._inner.offset.y = - self.vscroll.scroll_pos |
|
66 |
|
67 def _on_hscroll_change(self, ev): |
|
68 self._inner.offset.x = - self.hscroll.scroll_pos |
|
69 |
|
70 def _update_scroll_max(self): |
|
71 max_width = 0 |
|
72 max_height = 0 |
|
73 for child in self._inner.children: |
|
74 child_width = child.x - self._inner.offset.x + child.sizereq.w |
|
75 if child_width > max_width: |
|
76 max_width = child_width |
|
77 child_height = child.y - self._inner.offset.y + child.sizereq.h |
|
78 if child_height > max_height: |
|
79 max_height = child_height |
|
80 max_width += 1 |
|
81 if max_width < self._inner.width: |
|
82 self.hscroll.hide() |
|
83 else: |
|
84 self.hscroll.scroll_max = max_width - self._inner.width |
|
85 self.hscroll.show() |
|
86 if max_height < self._inner.height: |
|
87 self.vscroll.hide() |
|
88 else: |
|
89 self.vscroll.scroll_max = max_height - self._inner.height |
|
90 self.vscroll.show() |
|
91 |
|
92 |
|
93 class ScrollView(OffsetView, Scrolling): |
9 """Scrolling view |
94 """Scrolling view |
10 |
95 |
11 Shows scrollbars when needed. |
96 Shows scrollbars when needed. |
12 Scrolling area is determined according to child widget's size request. |
97 Scrolling area is determined according to child widget's size request. |
13 |
98 |
14 """ |
99 """ |
15 |
100 |
16 def __init__(self): |
101 def __init__(self): |
17 AnchorLayout.__init__(self) |
102 OffsetView.__init__(self) |
18 self._default_size.update(20, 20) |
103 Scrolling.__init__(self) |
19 |
|
20 self.inner = OffsetLayout() |
|
21 AnchorLayout.add(self, self.inner, halign='fill', valign='fill', margin=Borders(r=1)) |
|
22 |
|
23 self.vscroll = VScrollbar() |
|
24 self.vscroll.add_handler('change', self._on_vscroll_change) |
|
25 AnchorLayout.add(self, self.vscroll, halign='right', valign='fill') |
|
26 |
104 |
27 def add(self, widget, **kwargs): |
105 def add(self, widget, **kwargs): |
28 self.inner.add(widget, **kwargs) |
106 OffsetView.add(self, widget, **kwargs) |
29 widget.add_handler('sizereq', self._on_child_sizereq) |
107 self._connect_child(widget) |
30 widget.add_handler('spotmove', self._on_child_spotmove) |
|
31 |
108 |
32 def on_resize(self, ev): |
|
33 self._update_vscroll_max() |
|
34 |
|
35 def _on_vscroll_change(self, ev): |
|
36 self.inner.offset.y = - self.vscroll.scroll_pos |
|
37 |
|
38 def _on_child_sizereq(self, ev): |
|
39 self._update_vscroll_max() |
|
40 |
|
41 def _on_child_spotmove(self, ev): |
|
42 child = ev.originator |
|
43 spot = child.y - self.inner.offset.y + child.spot.y |
|
44 if spot < self.vscroll.scroll_pos: |
|
45 self.vscroll.scroll_pos = spot |
|
46 if spot > (self.height - 1) + self.vscroll.scroll_pos: |
|
47 self.vscroll.scroll_pos = spot - (self.height - 1) |
|
48 |
|
49 def _update_vscroll_max(self): |
|
50 max_height = 0 |
|
51 for child in self.inner.children: |
|
52 child_height = child.y - self.inner.offset.y + child.sizereq.h |
|
53 if child_height > max_height: |
|
54 max_height = child_height |
|
55 if max_height < self.height: |
|
56 self.vscroll.hide() |
|
57 else: |
|
58 self.vscroll.scroll_max = max_height - self.height |
|
59 self.vscroll.show() |
|
60 |
|