tuikit/widgets/scrollbar.py
author Radek Brich <radek.brich@devl.cz>
Sat, 21 Feb 2015 12:01:57 +0100
changeset 118 8c7970520632
parent 76 tuikit/scrollbar.py@fa5301e58eca
child 119 dd91747504dd
permissions -rw-r--r--
Add mouse events, event demo.

# -*- 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