|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 from tuikit.core.widget import Widget |
|
4 from tuikit.core.signal import Signal |
|
5 |
|
6 |
|
7 class Scrollbar(Widget): |
|
8 |
|
9 """Abstract base class for scrollbars.""" |
|
10 |
|
11 def __init__(self): |
|
12 Widget.__init__(self) |
|
13 |
|
14 # Scrolling range is 0 .. _scroll_max |
|
15 self._scroll_max = self._get_length() - 3 |
|
16 # Current position of scrollbar in scrolling range. |
|
17 self._scroll_pos = 0 |
|
18 # Current position of thumb on scrollbar - used for draw. |
|
19 self._thumb_pos = 0 |
|
20 # Auxilliary variable, True when user holds mouse on thumb. |
|
21 self._dragging = False |
|
22 # Auxilliary variable, 'up' or 'down' depending on last move direction. |
|
23 self._move = None |
|
24 |
|
25 #: delay before continuous scrolling when user holds mouse on scrollbar arrow |
|
26 self.scroll_delay = 0.150 |
|
27 #: interval for continuous scrolling |
|
28 self.scroll_interval = 0.030 |
|
29 |
|
30 # change event is emitted when user moves scrollbar (even programmatically) |
|
31 self.sig_changed = Signal() |
|
32 |
|
33 @property |
|
34 def scroll_max(self): |
|
35 """Maximum for scrolling position.""" |
|
36 return self._scroll_max |
|
37 |
|
38 @scroll_max.setter |
|
39 def scroll_max(self, value): |
|
40 if value < 0: |
|
41 value = 0 |
|
42 self._scroll_max = value |
|
43 if self._scroll_pos > value: |
|
44 self._scroll_pos = value |
|
45 self.sig_changed() |
|
46 self._update_thumb_pos() |
|
47 |
|
48 @property |
|
49 def scroll_pos(self): |
|
50 """Scrolling position. |
|
51 |
|
52 Integer number between 0 and 'max'. |
|
53 |
|
54 """ |
|
55 return self._scroll_pos |
|
56 |
|
57 @scroll_pos.setter |
|
58 def scroll_pos(self, value): |
|
59 if self._scroll_pos != value: |
|
60 self._scroll_pos = value |
|
61 self._update_thumb_pos() |
|
62 self.sig_changed() |
|
63 |
|
64 def move_up(self): |
|
65 """Move scrolling position up/left.""" |
|
66 if self._scroll_pos > 0: |
|
67 self.scroll_pos = self._scroll_pos - 1 |
|
68 self._move = 'up' |
|
69 |
|
70 def move_down(self): |
|
71 """Move scrolling position down/right.""" |
|
72 if self._scroll_pos < self._scroll_max: |
|
73 self.scroll_pos = self._scroll_pos + 1 |
|
74 self._move = 'down' |
|
75 |
|
76 def drag(self, mouse_pos): |
|
77 """Scroll using mouse drag on thumb. |
|
78 |
|
79 Args: |
|
80 mouse_pos: new position of mouse, in range 0 .. self._get_length() |
|
81 |
|
82 """ |
|
83 new_pos = int(round((mouse_pos - 1) / (self._get_length() - 3) * self._scroll_max)) |
|
84 if new_pos < 0: |
|
85 new_pos = 0 |
|
86 if new_pos > self._scroll_max: |
|
87 new_pos = self._scroll_max |
|
88 if self._scroll_pos != new_pos: |
|
89 self.scroll_pos = new_pos |
|
90 |
|
91 def _get_length(self): |
|
92 """Return length of scrollbar. |
|
93 |
|
94 This will be widget height for vertical scrollbar, |
|
95 width for horizontal scrollbar. |
|
96 |
|
97 """ |
|
98 raise NotImplementedError() |
|
99 |
|
100 def _update_thumb_pos(self): |
|
101 """Update value of internal variable _thumb_pos.""" |
|
102 self._thumb_pos = 0 |
|
103 if self._scroll_max and self._scroll_pos <= self._scroll_max: |
|
104 self._thumb_pos = int(round(self._scroll_pos / self._scroll_max * (self._get_length() - 3))) |
|
105 #self.redraw() |
|
106 |
|
107 def _continuous_scroll(self): |
|
108 if self._move == 'up': |
|
109 self.move_up() |
|
110 if self._move == 'down': |
|
111 self.move_down() |
|
112 self.add_timeout(self.scroll_interval, self._continuous_scroll) |
|
113 |
|
114 |
|
115 class VScrollbar(Scrollbar): |
|
116 def __init__(self): |
|
117 Scrollbar.__init__(self) |
|
118 self.sizereq.update(1, 20) |
|
119 |
|
120 def draw(self, buffer): |
|
121 Widget.draw(self, buffer) |
|
122 ug = ev.driver.unigraph |
|
123 ev.driver.pushcolor('normal') |
|
124 ev.driver.putch(ev.x, ev.y, ug.get_char('sb_up')) |
|
125 for i in range(1, self.height - 1): |
|
126 ev.driver.putch(ev.x, ev.y + i, ug.get_char('sb_vtrack')) |
|
127 ev.driver.putch(ev.x, ev.y + 1 + self._thumb_pos, ug.get_char('sb_thumb')) |
|
128 ev.driver.putch(ev.x, ev.y + self.height - 1, ug.get_char('sb_down')) |
|
129 ev.driver.popcolor() |
|
130 |
|
131 def on_mousedown(self, ev): |
|
132 self._dragging = False |
|
133 self._move = None |
|
134 # arrow buttons |
|
135 if ev.wy == 0 or ev.wy == self.height - 1: |
|
136 if ev.wy == 0: |
|
137 self.move_up() |
|
138 else: |
|
139 self.move_down() |
|
140 self.add_timeout(self.scroll_delay, self._continuous_scroll) |
|
141 return |
|
142 # thumb bar |
|
143 if ev.wy == 1 + self._thumb_pos: |
|
144 self._dragging = True |
|
145 return |
|
146 |
|
147 def on_mousemove(self, ev): |
|
148 if self._dragging: |
|
149 self.drag(ev.wy) |
|
150 |
|
151 def on_mouseup(self, ev): |
|
152 if self._dragging: |
|
153 self.drag(ev.wy) |
|
154 self._dragging = False |
|
155 return |
|
156 if self._move: |
|
157 self.remove_timeout(self._continuous_scroll) |
|
158 self._move = None |
|
159 return |
|
160 |
|
161 def _get_length(self): |
|
162 return self.height |
|
163 |
|
164 |
|
165 class HScrollbar(Scrollbar): |
|
166 def __init__(self): |
|
167 Scrollbar.__init__(self) |
|
168 self.sizereq.update(20, 1) |
|
169 |
|
170 def on_draw(self, ev): |
|
171 ug = ev.driver.unigraph |
|
172 ev.driver.pushcolor('normal') |
|
173 ev.driver.putch(ev.x, ev.y, ug.get_char('sb_left')) |
|
174 for i in range(1, self.width - 1): |
|
175 ev.driver.putch(ev.x + i, ev.y, ug.get_char('sb_htrack')) |
|
176 ev.driver.putch(ev.x + 1 + self._thumb_pos, ev.y, ug.get_char('sb_thumb')) |
|
177 ev.driver.putch(ev.x + self.width - 1, ev.y, ug.get_char('sb_right')) |
|
178 ev.driver.popcolor() |
|
179 |
|
180 def on_mousedown(self, ev): |
|
181 self._dragging = False |
|
182 self._move = None |
|
183 # arrow buttons |
|
184 if ev.wx == 0 or ev.wx == self.width - 1: |
|
185 if ev.wx == 0: |
|
186 self.move_up() |
|
187 else: |
|
188 self.move_down() |
|
189 self.add_timeout(self.scroll_delay, self._continuous_scroll) |
|
190 return |
|
191 # thumb bar |
|
192 if ev.wx == 1 + self._thumb_pos: |
|
193 self._dragging = True |
|
194 return |
|
195 |
|
196 def on_mousemove(self, ev): |
|
197 if self._dragging: |
|
198 self.drag(ev.wx) |
|
199 |
|
200 def on_mouseup(self, ev): |
|
201 if self._dragging: |
|
202 self.drag(ev.wx) |
|
203 self._dragging = False |
|
204 return |
|
205 if self._move: |
|
206 self.remove_timeout(self._continuous_scroll) |
|
207 self._move = None |
|
208 return |
|
209 |
|
210 def _get_length(self): |
|
211 return self.width |
|
212 |