tuikit/widgets/scrollbar.py
changeset 118 8c7970520632
parent 76 fa5301e58eca
child 119 dd91747504dd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/widgets/scrollbar.py	Sat Feb 21 12:01:57 2015 +0100
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+
+from tuikit.core.widget import Widget
+from tuikit.core.signal import Signal
+
+
+class Scrollbar(Widget):
+
+    """Abstract base class for scrollbars."""
+
+    def __init__(self):
+        Widget.__init__(self)
+
+        # Scrolling range is 0 .. _scroll_max
+        self._scroll_max = self._get_length() - 3
+        # Current position of scrollbar in scrolling range.
+        self._scroll_pos = 0
+        # Current position of thumb on scrollbar - used for draw.
+        self._thumb_pos = 0
+        # Auxilliary variable, True when user holds mouse on thumb.
+        self._dragging = False
+        # Auxilliary variable, 'up' or 'down' depending on last move direction.
+        self._move = None
+
+        #: delay before continuous scrolling when user holds mouse on scrollbar arrow
+        self.scroll_delay = 0.150
+        #: interval for continuous scrolling
+        self.scroll_interval = 0.030
+
+        # change event is emitted when user moves scrollbar (even programmatically)
+        self.sig_changed = Signal()
+
+    @property
+    def scroll_max(self):
+        """Maximum for scrolling position."""
+        return self._scroll_max
+
+    @scroll_max.setter
+    def scroll_max(self, value):
+        if value < 0:
+            value = 0
+        self._scroll_max = value
+        if self._scroll_pos > value:
+            self._scroll_pos = value
+            self.sig_changed()
+        self._update_thumb_pos()
+
+    @property
+    def scroll_pos(self):
+        """Scrolling position.
+
+        Integer number between 0 and 'max'.
+
+        """
+        return self._scroll_pos
+
+    @scroll_pos.setter
+    def scroll_pos(self, value):
+        if self._scroll_pos != value:
+            self._scroll_pos = value
+            self._update_thumb_pos()
+            self.sig_changed()
+
+    def move_up(self):
+        """Move scrolling position up/left."""
+        if self._scroll_pos > 0:
+            self.scroll_pos = self._scroll_pos - 1
+        self._move = 'up'
+
+    def move_down(self):
+        """Move scrolling position down/right."""
+        if self._scroll_pos < self._scroll_max:
+            self.scroll_pos = self._scroll_pos + 1
+        self._move = 'down'
+
+    def drag(self, mouse_pos):
+        """Scroll using mouse drag on thumb.
+
+        Args:
+            mouse_pos: new position of mouse, in range 0 .. self._get_length()
+
+        """
+        new_pos = int(round((mouse_pos - 1) / (self._get_length() - 3) * self._scroll_max))
+        if new_pos < 0:
+            new_pos = 0
+        if new_pos > self._scroll_max:
+            new_pos = self._scroll_max
+        if self._scroll_pos != new_pos:
+            self.scroll_pos = new_pos
+
+    def _get_length(self):
+        """Return length of scrollbar.
+
+        This will be widget height for vertical scrollbar,
+        width for horizontal scrollbar.
+
+        """
+        raise NotImplementedError()
+
+    def _update_thumb_pos(self):
+        """Update value of internal variable _thumb_pos."""
+        self._thumb_pos = 0
+        if self._scroll_max and self._scroll_pos <= self._scroll_max:
+            self._thumb_pos = int(round(self._scroll_pos / self._scroll_max * (self._get_length() - 3)))
+        #self.redraw()
+
+    def _continuous_scroll(self):
+        if self._move == 'up':
+            self.move_up()
+        if self._move == 'down':
+            self.move_down()
+        self.add_timeout(self.scroll_interval, self._continuous_scroll)
+
+
+class VScrollbar(Scrollbar):
+    def __init__(self):
+        Scrollbar.__init__(self)
+        self.sizereq.update(1, 20)
+
+    def draw(self, buffer):
+        Widget.draw(self, buffer)
+        ug = ev.driver.unigraph
+        ev.driver.pushcolor('normal')
+        ev.driver.putch(ev.x, ev.y, ug.get_char('sb_up'))
+        for i in range(1, self.height - 1):
+            ev.driver.putch(ev.x, ev.y + i, ug.get_char('sb_vtrack'))
+        ev.driver.putch(ev.x, ev.y + 1 + self._thumb_pos, ug.get_char('sb_thumb'))
+        ev.driver.putch(ev.x, ev.y + self.height - 1, ug.get_char('sb_down'))
+        ev.driver.popcolor()
+
+    def on_mousedown(self, ev):
+        self._dragging = False
+        self._move = None
+        # arrow buttons
+        if ev.wy == 0 or ev.wy == self.height - 1:
+            if ev.wy == 0:
+                self.move_up()
+            else:
+                self.move_down()
+            self.add_timeout(self.scroll_delay, self._continuous_scroll)
+            return
+        # thumb bar
+        if ev.wy == 1 + self._thumb_pos:
+            self._dragging = True
+            return
+
+    def on_mousemove(self, ev):
+        if self._dragging:
+            self.drag(ev.wy)
+
+    def on_mouseup(self, ev):
+        if self._dragging:
+            self.drag(ev.wy)
+            self._dragging = False
+            return
+        if self._move:
+            self.remove_timeout(self._continuous_scroll)
+            self._move = None
+            return
+
+    def _get_length(self):
+        return self.height
+
+
+class HScrollbar(Scrollbar):
+    def __init__(self):
+        Scrollbar.__init__(self)
+        self.sizereq.update(20, 1)
+
+    def on_draw(self, ev):
+        ug = ev.driver.unigraph
+        ev.driver.pushcolor('normal')
+        ev.driver.putch(ev.x, ev.y, ug.get_char('sb_left'))
+        for i in range(1, self.width - 1):
+            ev.driver.putch(ev.x + i, ev.y, ug.get_char('sb_htrack'))
+        ev.driver.putch(ev.x + 1 + self._thumb_pos, ev.y, ug.get_char('sb_thumb'))
+        ev.driver.putch(ev.x + self.width - 1, ev.y, ug.get_char('sb_right'))
+        ev.driver.popcolor()
+
+    def on_mousedown(self, ev):
+        self._dragging = False
+        self._move = None
+        # arrow buttons
+        if ev.wx == 0 or ev.wx == self.width - 1:
+            if ev.wx == 0:
+                self.move_up()
+            else:
+                self.move_down()
+            self.add_timeout(self.scroll_delay, self._continuous_scroll)
+            return
+        # thumb bar
+        if ev.wx == 1 + self._thumb_pos:
+            self._dragging = True
+            return
+
+    def on_mousemove(self, ev):
+        if self._dragging:
+            self.drag(ev.wx)
+
+    def on_mouseup(self, ev):
+        if self._dragging:
+            self.drag(ev.wx)
+            self._dragging = False
+            return
+        if self._move:
+            self.remove_timeout(self._continuous_scroll)
+            self._move = None
+            return
+
+    def _get_length(self):
+        return self.width
+