diff options
Diffstat (limited to 'src/python/widget/slider.py')
-rw-r--r-- | src/python/widget/slider.py | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/python/widget/slider.py b/src/python/widget/slider.py new file mode 100644 index 0000000..1e63f0a --- /dev/null +++ b/src/python/widget/slider.py @@ -0,0 +1,371 @@ +# -*- coding: utf-8 -*- +# libavg - Media Playback Engine. +# Copyright (C) 2003-2014 Ulrich von Zadow +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Current versions can be found at www.libavg.de + +from libavg import avg, gesture +from base import SwitchNode, HStretchNode, VStretchNode, Orientation +from . import skin + +import math + + +class ScrollBarTrack(SwitchNode): + + def __init__(self, bmp, endsExtent, disabledBmp, orientation=Orientation.HORIZONTAL, + **kwargs): + + super(ScrollBarTrack, self).__init__(nodeMap=None, **kwargs) + if orientation == Orientation.HORIZONTAL: + StretchNode = HStretchNode + else: + StretchNode = VStretchNode + self.__enabledNode = StretchNode(src=bmp, endsExtent=endsExtent, parent=self) + self.__disabledNode = StretchNode(src=disabledBmp, endsExtent=endsExtent, + parent=self) + + self.setNodeMap({ + "ENABLED": self.__enabledNode, + "DISABLED": self.__disabledNode + }) + self.visibleid = "ENABLED" + + +class ScrollBarThumb(SwitchNode): + + def __init__(self, upBmp, downBmp, disabledBmp, endsExtent, + orientation=Orientation.HORIZONTAL, minExtent=-1, + **kwargs): + + super(ScrollBarThumb, self).__init__(nodeMap=None, **kwargs) + + if orientation == Orientation.HORIZONTAL: + StretchNode = HStretchNode + else: + StretchNode = VStretchNode + self.__upNode = StretchNode(src=upBmp, endsExtent=endsExtent, minExtent=minExtent) + self.__downNode = StretchNode(src=downBmp, endsExtent=endsExtent, + minExtent=minExtent) + self.__disabledNode = StretchNode(src=disabledBmp, endsExtent=endsExtent, + minExtent=minExtent) + + self.setNodeMap({ + "UP": self.__upNode, + "DOWN": self.__downNode, + "DISABLED": self.__disabledNode + }) + self.visibleid = "UP" + + +class SliderThumb(SwitchNode): + + def __init__(self, upBmp, downBmp, disabledBmp, **kwargs): + upNode = avg.ImageNode() + upNode.setBitmap(upBmp) + downNode = avg.ImageNode() + downNode.setBitmap(downBmp) + disabledNode = avg.ImageNode() + disabledNode.setBitmap(disabledBmp) + nodeMap = {"UP": upNode, "DOWN": downNode, "DISABLED": disabledNode} + super(SliderThumb, self).__init__(nodeMap=nodeMap, visibleid="UP", **kwargs) + + +class ProgressBar(avg.DivNode): + + def __init__(self, orientation, skinObj=skin.Skin.default, height=0, width=0, + range=(0.,1.), value=0.0, parent=None, **kwargs): + super(ProgressBar, self).__init__(**kwargs) + self.registerInstance(self, parent) + if orientation == Orientation.HORIZONTAL: + cfg = skinObj.defaultProgressBarCfg["horizontal"] + else: + cfg = skinObj.defaultProgressBarCfg["vertical"] + + self._orientation = orientation + + trackBmp = cfg["trackBmp"] + self._trackNode = ScrollBarTrack(bmp=trackBmp, disabledBmp=trackBmp, + endsExtent=cfg["trackEndsExtent"], orientation=self._orientation) + self.appendChild(self._trackNode) + + thumbUpBmp = cfg["thumbUpBmp"] + endsExtent=cfg["thumbEndsExtent"] + + self.__thumbNode = ScrollBarThumb(orientation=self._orientation, + upBmp=thumbUpBmp, downBmp=thumbUpBmp, disabledBmp=thumbUpBmp, + endsExtent=endsExtent) + self.appendChild(self.__thumbNode) + + self.__range = range + self.__value = value + + if orientation == Orientation.HORIZONTAL: + self.size = (width, trackBmp.getSize().y) + else: + self.size = (trackBmp.getSize().x, height) + self._positionNodes() + + def getRange(self): + return self.__range + + def setRange(self, range): + self.__range = (float(range[0]), float(range[1])) + self._positionNodes() + range = property(getRange, setRange) + + def getValue(self): + return self.__value + + def setValue(self, value): + self._positionNodes(value) + value = property(getValue, setValue) + + def _positionNodes(self, newValue=None): + if newValue is not None: + self.__value = float(newValue) + if self.__value < self.__range[0]: + self.__value = self.__range[0] + if self.__value > self.__range[1]: + self.__value = self.__range[1] + self._trackNode.size = self.size + + effectiveRange = math.fabs(self.__range[1] - self.__range[0]) + normValue = ((self.__value-self.__range[0])/effectiveRange) + if self._orientation == Orientation.HORIZONTAL: + self.__thumbNode.width = normValue*self.size.x + else: + self.__thumbNode.height = normValue*self.size.y + + +class SliderBase(avg.DivNode): + + THUMB_POS_CHANGED = avg.Publisher.genMessageID() + PRESSED = avg.Publisher.genMessageID() + RELEASED = avg.Publisher.genMessageID() + + def __init__(self, orientation, cfg, enabled=True, height=0, width=0, range=(0.,1.), + thumbPos=0.0, parent=None, **kwargs): + super(SliderBase, self).__init__(**kwargs) + self.registerInstance(self, parent) + + self.publish(SliderBase.THUMB_POS_CHANGED) + self.publish(SliderBase.PRESSED) + self.publish(SliderBase.RELEASED) + + self._orientation = orientation + + trackBmp = cfg["trackBmp"] + trackDisabledBmp = cfg["trackDisabledBmp"] + self._trackNode = ScrollBarTrack(bmp=trackBmp, disabledBmp=trackDisabledBmp, + endsExtent=cfg["trackEndsExtent"], orientation=self._orientation) + self.appendChild(self._trackNode) + + self._initThumb(cfg) + + self._range = range + self._thumbPos = thumbPos + + self.subscribe(self.SIZE_CHANGED, lambda newSize: self._positionNodes()) + if orientation == Orientation.HORIZONTAL: + self.size = (width, trackBmp.getSize().y) + else: + self.size = (trackBmp.getSize().x, height) + if not(enabled): + self.setEnabled(False) + + self.__recognizer = gesture.DragRecognizer(self._thumbNode, friction=-1, + detectedHandler=self.__onDragStart, moveHandler=self.__onDrag, + upHandler=self.__onUp) + + def getRange(self): + return self._range + + def setRange(self, range): + self._range = (float(range[0]), float(range[1])) + self._positionNodes() + + # range[1] > range[0]: Reversed scrollbar. + range = property(getRange, setRange) + + def getThumbPos(self): + return self._thumbPos + + def setThumbPos(self, thumbPos): + self._positionNodes(thumbPos) + + thumbPos = property(getThumbPos, setThumbPos) + + def getEnabled(self): + return self._trackNode.visibleid != "DISABLED" + + def setEnabled(self, enabled): + if enabled: + if self._trackNode.visibleid == "DISABLED": + self._trackNode.visibleid = "ENABLED" + self._thumbNode.visibleid = "UP" + self.__recognizer.enable(True) + else: + if self._trackNode.visibleid != "DISABLED": + self._trackNode.visibleid = "DISABLED" + self._thumbNode.visibleid = "DISABLED" + self.__recognizer.enable(False) + + enabled = property(getEnabled, setEnabled) + + def _positionNodes(self, newSliderPos=None): + if newSliderPos is not None: + self._thumbPos = float(newSliderPos) + self._trackNode.size = self.size + + self._constrainSliderPos() + + pixelRange = self._getScrollRangeInPixels() + if self._getSliderRange() == 0: + thumbPixelPos = 0 + else: + thumbPixelPos = (((self._thumbPos-self._range[0])/self._getSliderRange())* + pixelRange) + if self._orientation == Orientation.HORIZONTAL: + self._thumbNode.x = thumbPixelPos + else: + self._thumbNode.y = thumbPixelPos + + def __onDragStart(self): + self._thumbNode.visibleid = "DOWN" + self.__dragStartPos = self._thumbPos + self.notifySubscribers(Slider.PRESSED, []) + + def __onDrag(self, offset): + pixelRange = self._getScrollRangeInPixels() + if pixelRange == 0: + normalizedOffset = 0 + else: + if self._orientation == Orientation.HORIZONTAL: + normalizedOffset = offset.x/pixelRange + else: + normalizedOffset = offset.y/pixelRange + oldThumbPos = self._thumbPos + self._positionNodes(self.__dragStartPos + normalizedOffset*self._getSliderRange()) + if self._thumbPos != oldThumbPos: + self.notifySubscribers(Slider.THUMB_POS_CHANGED, [self._thumbPos]) + + def __onUp(self, offset): + self.__onDrag(offset) + self._thumbNode.visibleid = "UP" + self.notifySubscribers(Slider.RELEASED, []) + + +class Slider(SliderBase): + + def __init__(self, orientation=Orientation.HORIZONTAL, skinObj=skin.Skin.default, + **kwargs): + if orientation == Orientation.HORIZONTAL: + cfg = skinObj.defaultSliderCfg["horizontal"] + else: + cfg = skinObj.defaultSliderCfg["vertical"] + super(Slider, self).__init__(orientation, cfg, **kwargs) + + def _initThumb(self, cfg): + thumbUpBmp = cfg["thumbUpBmp"] + thumbDownBmp = skin.getBmpFromCfg(cfg, "thumbDownBmp", "thumbUpBmp") + thumbDisabledBmp = skin.getBmpFromCfg(cfg, "thumbDisabledBmp", "thumbUpBmp") + self._thumbNode = SliderThumb(upBmp=thumbUpBmp, downBmp=thumbDownBmp, + disabledBmp=thumbDisabledBmp) + self.appendChild(self._thumbNode) + + def _getScrollRangeInPixels(self): + if self._orientation == Orientation.HORIZONTAL: + return self.size.x - self._thumbNode.size.x + else: + return self.size.y - self._thumbNode.size.y + + def _getSliderRange(self): + return self._range[1] - self._range[0] + + def _constrainSliderPos(self): + rangeMin = min(self._range[0], self._range[1]) + rangeMax = max(self._range[0], self._range[1]) + self._thumbPos = max(rangeMin, self._thumbPos) + self._thumbPos = min(rangeMax, self._thumbPos) + + +class ScrollBar(SliderBase): + + def __init__(self, orientation=Orientation.HORIZONTAL, skinObj=skin.Skin.default, + thumbExtent=0.1, **kwargs): + self.__thumbExtent = thumbExtent + if orientation == Orientation.HORIZONTAL: + cfg = skinObj.defaultScrollBarCfg["horizontal"] + else: + cfg = skinObj.defaultScrollBarCfg["vertical"] + super(ScrollBar, self).__init__(orientation=orientation, cfg=cfg, **kwargs) + + def _initThumb(self, cfg): + thumbUpBmp = cfg["thumbUpBmp"] + thumbDownBmp = skin.getBmpFromCfg(cfg, "thumbDownBmp", "thumbUpBmp") + thumbDisabledBmp = skin.getBmpFromCfg(cfg, "thumbDisabledBmp", "thumbUpBmp") + endsExtent=cfg["thumbEndsExtent"] + + self._thumbNode = ScrollBarThumb(orientation=self._orientation, + upBmp=thumbUpBmp, downBmp=thumbDownBmp, + disabledBmp=thumbDisabledBmp, endsExtent=endsExtent) + self.appendChild(self._thumbNode) + + def getThumbExtent(self): + return self.__thumbExtent + + def setThumbExtent(self, thumbExtent): + self.__thumbExtent = float(thumbExtent) + self._positionNodes() + + thumbExtent = property(getThumbExtent, setThumbExtent) + + def _getScrollRangeInPixels(self): + if self._orientation == Orientation.HORIZONTAL: + return self.size.x - self._thumbNode.width + else: + return self.size.y - self._thumbNode.height + + def _positionNodes(self, newSliderPos=None): + effectiveRange = math.fabs(self._range[1] - self._range[0]) + if self._orientation == Orientation.HORIZONTAL: + thumbExtent = (self.__thumbExtent/effectiveRange)*self.size.x + self._thumbNode.width = thumbExtent + else: + thumbExtent = (self.__thumbExtent/effectiveRange)*self.size.y + self._thumbNode.height = thumbExtent + super(ScrollBar, self)._positionNodes(newSliderPos) + if self._range[1] < self._range[0]: + # Reversed (upside-down) scrollbar + if self._orientation == Orientation.HORIZONTAL: + self._thumbNode.x -= thumbExtent + else: + self._thumbNode.y -= thumbExtent + + def _getSliderRange(self): + if self._range[1] > self._range[0]: + return self._range[1] - self._range[0] - self.__thumbExtent + else: + return self._range[1] - self._range[0] + self.__thumbExtent + + def _constrainSliderPos(self): + rangeMin = min(self._range[0], self._range[1]) + rangeMax = max(self._range[0], self._range[1]) + self._thumbPos = max(rangeMin, self._thumbPos) + self._thumbPos = min(rangeMax-self.__thumbExtent, self._thumbPos) + |