diff options
Diffstat (limited to 'src/python/widget/scrollarea.py')
-rw-r--r-- | src/python/widget/scrollarea.py | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/python/widget/scrollarea.py b/src/python/widget/scrollarea.py new file mode 100644 index 0000000..d22877f --- /dev/null +++ b/src/python/widget/scrollarea.py @@ -0,0 +1,240 @@ +# -*- 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 . import slider +from base import HVStretchNode, Orientation +from . import skin + +class ScrollPane(avg.DivNode): + + def __init__(self, contentNode, parent=None, **kwargs): + + super(ScrollPane, self).__init__(crop=True, **kwargs) + self.registerInstance(self, parent) + + self.appendChild(contentNode) + self._contentNode = contentNode + + def setContentPos(self, pos): + + def constrain(pos, limit): + if limit > 0: + # Content larger than container + if pos > limit: + pos = limit + elif pos < 0: + pos = 0 + else: + # Content smaller than container + if pos > 0: + pos = 0 + elif pos < limit: + pos = limit + return pos + + maxPos = self.getMaxContentPos() + pos = avg.Point2D(pos) + pos.x = constrain(pos.x, maxPos.x) + pos.y = constrain(pos.y, maxPos.y) + self._contentNode.pos = -pos + + def getContentPos(self): + return -self._contentNode.pos + contentpos = property(getContentPos, setContentPos) + + def getContentSize(self): + return self._contentNode.size + + def setContentSize(self, size): + self._contentNode.size = size + self.setContentPos(-self._contentNode.pos) # Recheck constraints. + contentsize = property(getContentSize, setContentSize) + + def getMaxContentPos(self): + maxPos = avg.Point2D(self._contentNode.size - self.size) + if maxPos.x < 0: + maxPos.x = 0 + if maxPos.y < 0: + maxPos.y = 0 + return maxPos + + +class ScrollArea(avg.DivNode): + + PRESSED = avg.Publisher.genMessageID() + RELEASED = avg.Publisher.genMessageID() + CONTENT_POS_CHANGED = avg.Publisher.genMessageID() + + def __init__(self, contentNode, size, skinObj=skin.Skin.default, enabled=True, + scrollBars=(Orientation.HORIZONTAL, Orientation.VERTICAL), + parent=None, **kwargs): + + super(ScrollArea, self).__init__(**kwargs) + self.registerInstance(self, parent) + self.cfg = skinObj.defaultScrollAreaCfg + + self.publish(self.PRESSED) + self.publish(self.RELEASED) + self.publish(self.CONTENT_POS_CHANGED) + + self.__scrollPane = ScrollPane(contentNode=contentNode, parent=self) + + if "borderBmp" in self.cfg: + endsExtent = self.cfg["borderEndsExtent"] + self._borderNode = HVStretchNode(src=self.cfg["borderBmp"], + endsExtent=endsExtent, sensitive=False, parent=self) + else: + self._borderNode = None + + sensitiveScrollBars = self.cfg["sensitiveScrollBars"] + + if Orientation.HORIZONTAL in scrollBars: + self._hScrollBar = slider.ScrollBar(sensitive=sensitiveScrollBars, + parent=self, skinObj=skinObj) + self._hScrollBar.subscribe(slider.Slider.THUMB_POS_CHANGED, + self.__onHThumbMove) + else: + self._hScrollBar = None + + if Orientation.VERTICAL in scrollBars: + self._vScrollBar = slider.ScrollBar(orientation=Orientation.VERTICAL, + sensitive=sensitiveScrollBars, parent=self, skinObj=skinObj) + self._vScrollBar.subscribe(slider.Slider.THUMB_POS_CHANGED, + self.__onVThumbMove) + else: + self._vScrollBar = None + + self.subscribe(self.SIZE_CHANGED, self.__positionNodes) + self.size = size + + self.__enabled = True + if not(enabled): + self.setEnabled(False) + + self.recognizer = gesture.DragRecognizer( + eventNode=self.__scrollPane, + detectedHandler=self.__onDragStart, + moveHandler=self.__onDragMove, + upHandler=self.__onDragUp, + friction=self.cfg["friction"] + ) + + def getContentSize(self): + return self.__scrollPane._contentNode.size + + def setContentSize(self, size): + self.__scrollPane.contentsize = size + self.__positionNodes(self.size) + contentsize = property(getContentSize, setContentSize) + + def getContentPos(self): + return self.__scrollPane.contentpos + + def setContentPos(self, pos): + self.__scrollPane.contentpos = pos + self.__positionNodes(self.size) + self.__positionThumbs(avg.Point2D(pos)) + self.notifySubscribers(self.CONTENT_POS_CHANGED, [self.__scrollPane.contentpos]) + contentpos = property(getContentPos, setContentPos) + + def getEnabled(self): + return self.__enabled + + def setEnabled(self, enabled): + if enabled and not(self.__enabled): + self.recognizer.enable(True) + elif not(enabled) and self.__enabled: + self.recognizer.enable(False) + + if self._vScrollBar: + self._vScrollBar.enabled = enabled + if self._hScrollBar: + self._hScrollBar.enabled = enabled + self.__enabled = enabled + enabled = property(getEnabled, setEnabled) + + def __onHThumbMove(self, thumbPos): + self.__scrollPane.contentpos = (thumbPos, self.__scrollPane.contentpos.y) + self.notifySubscribers(self.CONTENT_POS_CHANGED, [self.__scrollPane.contentpos]) + + def __onVThumbMove(self, thumbPos): + self.__scrollPane.contentpos = (self.__scrollPane.contentpos.x, thumbPos) + self.notifySubscribers(self.CONTENT_POS_CHANGED, [self.__scrollPane.contentpos]) + + def __onDragStart(self): + self.__dragStartPos = self.__scrollPane.contentpos + self.notifySubscribers(self.PRESSED, []) + + def __onDragMove(self, offset): + contentpos = self.__dragStartPos - offset + self.__scrollPane.contentpos = contentpos + self.__positionThumbs(contentpos) + self.notifySubscribers(self.CONTENT_POS_CHANGED, [self.__scrollPane.contentpos]) + + def __onDragUp(self, offset): + self.__onDragMove(offset) + self.notifySubscribers(self.RELEASED, []) + + def __positionNodes(self, size): + paneSize = size + if self._borderNode: + self._borderNode.size = size + + margins = self.cfg["margins"] + if self._hScrollBar: + paneSize -= (0, margins[0]+margins[2]) + if self._vScrollBar: + paneSize -= (margins[1]+margins[3], 0) + self.__scrollPane.pos = (margins[0], margins[1]) + self.__scrollPane.size = paneSize + + + if self._hScrollBar: + self._hScrollBar.pos = (0, size.y-self._hScrollBar.height) + self._hScrollBar.width = self.__scrollPane.width + + if self.__scrollPane.contentsize.x <= self.__scrollPane.width: + self._hScrollBar.range = (0, self.__scrollPane.width) + self._hScrollBar.enabled = False + else: + self._hScrollBar.range = (0, self.__scrollPane.contentsize.x) + self._hScrollBar.enabled = True + self._hScrollBar.thumbExtent = self.__scrollPane.width + + if self._vScrollBar: + self._vScrollBar.pos = (size.x-self._vScrollBar.width, 0) + self._vScrollBar.height = self.__scrollPane.height + + if self.__scrollPane.contentsize.y <= self.__scrollPane.height: + self._vScrollBar.range = (0, self.__scrollPane.height) + self._vScrollBar.enabled = False + else: + self._vScrollBar.range = (0, self.__scrollPane.contentsize.y) + self._vScrollBar.enabled = True + self._vScrollBar.thumbExtent = self.__scrollPane.height + + def __positionThumbs(self, contentPos): + if self._hScrollBar: + self._hScrollBar.thumbPos = contentPos.x + if self._vScrollBar: + self._vScrollBar.thumbPos = contentPos.y + |