diff options
Diffstat (limited to 'src/python/widget/button.py')
-rw-r--r-- | src/python/widget/button.py | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/src/python/widget/button.py b/src/python/widget/button.py new file mode 100644 index 0000000..f938e46 --- /dev/null +++ b/src/python/widget/button.py @@ -0,0 +1,443 @@ +# -*- 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 +# +# Original author of this file is Henrik Thoms + +from libavg import avg, statemachine, player, gesture + +from base import SwitchNode, HVStretchNode +from . import skin + +class _ButtonBase(avg.DivNode): + + PRESSED = avg.Publisher.genMessageID() + RELEASED = avg.Publisher.genMessageID() + + def __init__(self, parent=None, **kwargs): + super(_ButtonBase, self).__init__(**kwargs) + self.registerInstance(self, parent) + self.publish(self.PRESSED) + self.publish(self.RELEASED) + + def _setActiveArea(self, upNode, activeAreaNode, fatFingerEnlarge): + if fatFingerEnlarge: + if activeAreaNode: + raise(RuntimeError( + "Button: Can't specify both fatFingerEnlarge and activeAreaNode")) + size = upNode.size + minSize = 20*player.getPixelsPerMM() + size = avg.Point2D(max(minSize, size.x), max(minSize, size.y)) + activeAreaNode = avg.RectNode(size=size, opacity=0, parent=self) + #Here we need to store a 'hard' reference to the active area node + #because the tap recognizer won't keep it + self.__activeAreaNode = activeAreaNode + else: + if activeAreaNode == None: + activeAreaNode = self + else: + self.appendChild(activeAreaNode) + + self._tapRecognizer = gesture.TapRecognizer(activeAreaNode, + possibleHandler=self._onDown, + detectedHandler=self._onTap, + failHandler=self._onTapFail) + + +class Button(_ButtonBase): + + CLICKED = avg.Publisher.genMessageID() + + def __init__(self, upNode, downNode, disabledNode=None, activeAreaNode=None, + enabled=True, fatFingerEnlarge=False, **kwargs): + super(Button, self).__init__(**kwargs) + + if disabledNode == None: + disabledNode = upNode + + nodeMap = { + "UP": upNode, + "DOWN": downNode, + "DISABLED": disabledNode + } + self.__switchNode = SwitchNode(nodeMap=nodeMap, visibleid="UP", parent=self) + self.publish(self.CLICKED) + + self.__stateMachine = statemachine.StateMachine("Button", "UP") + self.__stateMachine.addState("UP", ("DOWN", "DISABLED"), + enterFunc=self._enterUp, leaveFunc=self._leaveUp) + self.__stateMachine.addState("DOWN", ("UP", "DISABLED"), + enterFunc=self._enterDown, leaveFunc=self._leaveDown) + self.__stateMachine.addState("DISABLED", ("UP",), + enterFunc=self._enterDisabled, leaveFunc=self._leaveDisabled) + + self._setActiveArea(upNode, activeAreaNode, fatFingerEnlarge) + + if not(enabled): + self.setEnabled(False) + + def getEnabled(self): + return self.__stateMachine.state != "DISABLED" + + def setEnabled(self, enabled): + if enabled: + if self.__stateMachine.state == "DISABLED": + self.__stateMachine.changeState("UP") + else: + if self.__stateMachine.state != "DISABLED": + self.__stateMachine.changeState("DISABLED") + + enabled = property(getEnabled, setEnabled) + + def _getState(self): + return self.__stateMachine.state + + def _onDown(self): + self.__stateMachine.changeState("DOWN") + self.notifySubscribers(self.PRESSED, []) + + def _onTap(self): + self.__stateMachine.changeState("UP") + self.notifySubscribers(self.CLICKED, []) + self.notifySubscribers(self.RELEASED, []) + + def _onTapFail(self): + self.__stateMachine.changeState("UP") + self.notifySubscribers(self.RELEASED, []) + + def _enterUp(self): + self.__setActiveNode() + + def _leaveUp(self): + pass + + def _enterDown(self): + self.__setActiveNode() + + def _leaveDown(self): + pass + + def _enterDisabled(self): + self.__setActiveNode() + self._tapRecognizer.enable(False) + + def _leaveDisabled(self): + self._tapRecognizer.enable(True) + + def __setActiveNode(self): + self.__switchNode.visibleid = self.__stateMachine.state + + +class BmpButton(Button): + + def __init__(self, upSrc, downSrc, disabledSrc=None, **kwargs): + upNode = avg.ImageNode(href=upSrc) + downNode = avg.ImageNode(href=downSrc) + if disabledSrc != None: + disabledNode = avg.ImageNode(href=disabledSrc) + else: + disabledNode = None + super(BmpButton, self).__init__(upNode=upNode, downNode=downNode, + disabledNode=disabledNode, **kwargs) + + +class TextButton(Button): + + def __init__(self, text, skinObj=skin.Skin.default, **kwargs): + size = avg.Point2D(kwargs["size"]) + cfg = skinObj.defaultTextButtonCfg + + self.wordsNodes = [] + + upNode = self.__createStateNode(size, cfg, "upBmp", text, "font") + downNode = self.__createStateNode(size, cfg, "downBmp", text, "downFont") + if "disabledBmp" in cfg: + disabledNode = self.__createStateNode(size, cfg, "disabledBmp", text, + "disabledFont") + else: + disabledNode = None + + super(TextButton, self).__init__(upNode=upNode, downNode=downNode, + disabledNode=disabledNode, **kwargs) + + def __createStateNode(self, size, cfg, bmpName, text, fontStyleName): + stateNode = avg.DivNode(size=size) + endsExtent = eval(cfg["endsExtent"], {}, {}) + HVStretchNode(size=size, src=cfg[bmpName], endsExtent=endsExtent, + parent=stateNode) + words = avg.WordsNode(text=text, fontstyle=cfg[fontStyleName], parent=stateNode) + words.pos = (round((size.x-words.size.x)/2), round((size.y-words.size.y)/2)) + self.wordsNodes.append(words) + return stateNode + + def getText(self): + return self.wordsNodes[0].text + + def setText(self, text): + for node in self.wordsNodes: + node.text = text + node.pos = (self.size-node.size)/2 + + text = property(getText, setText) + + +class ToggleButton(_ButtonBase): + + TOGGLED = avg.Publisher.genMessageID() + + def __init__(self, uncheckedUpNode, uncheckedDownNode, checkedUpNode, checkedDownNode, + uncheckedDisabledNode=None, checkedDisabledNode=None, activeAreaNode=None, + enabled=True, fatFingerEnlarge=False, checked=False, **kwargs): + super(ToggleButton, self).__init__(**kwargs) + nodeMap = { + "UNCHECKED_UP": uncheckedUpNode, + "UNCHECKED_DOWN": uncheckedDownNode, + "CHECKED_UP": checkedUpNode, + "CHECKED_DOWN": checkedDownNode, + "UNCHECKED_DISABLED": uncheckedDisabledNode, + "CHECKED_DISABLED": checkedDisabledNode, + } + if uncheckedDisabledNode == None: + nodeMap["UNCHECKED_DISABLED"] = uncheckedUpNode + if checkedDisabledNode == None: + nodeMap["CHECKED_DISABLED"] = checkedUpNode + self.__switchNode = SwitchNode(nodeMap=nodeMap, visibleid="UNCHECKED_UP", + parent=self) + + self.publish(ToggleButton.TOGGLED) + + self.__stateMachine = statemachine.StateMachine("ToggleButton", "UNCHECKED_UP") + self.__stateMachine.addState("UNCHECKED_UP", ("UNCHECKED_DOWN", + "UNCHECKED_DISABLED"), enterFunc=self._enterUncheckedUp, + leaveFunc=self._leaveUncheckedUp) + self.__stateMachine.addState("UNCHECKED_DOWN", ("UNCHECKED_UP", + "UNCHECKED_DISABLED", "CHECKED_UP"), enterFunc=self._enterUncheckedDown, + leaveFunc=self._leaveUncheckedDown) + self.__stateMachine.addState("CHECKED_UP", ("CHECKED_DOWN", "CHECKED_DISABLED"), + enterFunc=self._enterCheckedUp, leaveFunc=self._leaveCheckedUp) + self.__stateMachine.addState("CHECKED_DOWN", ("CHECKED_UP", "UNCHECKED_UP", + "CHECKED_DISABLED"), enterFunc=self._enterCheckedDown, + leaveFunc=self._leaveCheckedDown) + self.__stateMachine.addState("UNCHECKED_DISABLED", ("UNCHECKED_UP",), + enterFunc=self._enterUncheckedDisabled, + leaveFunc=self._leaveUncheckedDisabled) + self.__stateMachine.addState("CHECKED_DISABLED", ("CHECKED_UP", ), + enterFunc=self._enterCheckedDisabled, + leaveFunc=self._leaveCheckedDisabled) + + self._setActiveArea(uncheckedUpNode, activeAreaNode, fatFingerEnlarge) + + if not enabled: + self.__stateMachine.changeState("UNCHECKED_DISABLED") + if checked: + self.setChecked(True) + + def getEnabled(self): + return (self.__stateMachine.state != "CHECKED_DISABLED" and + self.__stateMachine.state != "UNCHECKED_DISABLED") + + def setEnabled(self, enabled): + if enabled: + if self.__stateMachine.state == "CHECKED_DISABLED": + self.__stateMachine.changeState("CHECKED_UP") + elif self.__stateMachine.state == "UNCHECKED_DISABLED": + self.__stateMachine.changeState("UNCHECKED_UP") + else: + if (self.__stateMachine.state == "CHECKED_UP" or + self.__stateMachine.state == "CHECKED_DOWN") : + self.__stateMachine.changeState("CHECKED_DISABLED") + elif (self.__stateMachine.state == "UNCHECKED_UP" or + self.__stateMachine.state == "UNCHECKED_DOWN") : + self.__stateMachine.changeState("UNCHECKED_DISABLED") + + enabled = property(getEnabled, setEnabled) + + def getChecked(self): + return (self.__stateMachine.state != "UNCHECKED_UP" and + self.__stateMachine.state != "UNCHECKED_DOWN" and + self.__stateMachine.state != "UNCHECKED_DISABLED") + + def setChecked(self, checked): + oldEnabled = self.getEnabled() + if checked: + if self.__stateMachine.state == "UNCHECKED_DISABLED": + self.__stateMachine.changeState("UNCHECKED_UP") + if self.__stateMachine.state == "UNCHECKED_UP": + self.__stateMachine.changeState("UNCHECKED_DOWN") + if self.__stateMachine.state != "CHECKED_UP": + self.__stateMachine.changeState("CHECKED_UP") + if not oldEnabled: + self.__stateMachine.changeState("CHECKED_DISABLED") + else: + if self.__stateMachine.state == "CHECKED_DISABLED": + self.__stateMachine.changeState("CHECKED_UP") + if self.__stateMachine.state == "CHECKED_UP": + self.__stateMachine.changeState("CHECKED_DOWN") + if self.__stateMachine.state != "UNCHECKED_UP": + self.__stateMachine.changeState("UNCHECKED_UP") + if not oldEnabled: + self.__stateMachine.changeState("UNCHECKED_DISABLED") + + checked = property(getChecked, setChecked) + + def _getState(self): + return self.__stateMachine.state + + def _enterUncheckedUp(self): + self.__setActiveNode() + + def _leaveUncheckedUp(self): + pass + + def _enterUncheckedDown(self): + self.__setActiveNode() + + def _leaveUncheckedDown(self): + pass + + def _enterCheckedUp(self): + self.__setActiveNode() + + def _leaveCheckedUp(self): + pass + + def _enterCheckedDown(self): + self.__setActiveNode() + + def _leaveCheckedDown(self): + pass + + def _enterUncheckedDisabled(self): + self.__setActiveNode() + self._tapRecognizer.enable(False) + + def _leaveUncheckedDisabled(self): + self._tapRecognizer.enable(True) + + def _enterCheckedDisabled(self): + self.__setActiveNode() + self._tapRecognizer.enable(False) + + def _leaveCheckedDisabled(self): + self._tapRecognizer.enable(True) + + def _onDown(self): + if self.__stateMachine.state == "UNCHECKED_UP": + self.__stateMachine.changeState("UNCHECKED_DOWN") + elif self.__stateMachine.state == "CHECKED_UP": + self.__stateMachine.changeState("CHECKED_DOWN") + self.notifySubscribers(self.PRESSED, []) + + def _onTap(self): + if self.__stateMachine.state == "UNCHECKED_DOWN": + self.__stateMachine.changeState("CHECKED_UP") + self.notifySubscribers(ToggleButton.TOGGLED, [True]) + elif self.__stateMachine.state == "CHECKED_DOWN": + self.__stateMachine.changeState("UNCHECKED_UP") + self.notifySubscribers(ToggleButton.TOGGLED, [False]) + self.notifySubscribers(self.RELEASED, []) + + def _onTapFail(self): + if self.__stateMachine.state == "UNCHECKED_DOWN": + self.__stateMachine.changeState("UNCHECKED_UP") + elif self.__stateMachine.state == "CHECKED_DOWN": + self.__stateMachine.changeState("CHECKED_UP") + self.notifySubscribers(self.RELEASED, []) + + def __setActiveNode(self): + self.__switchNode.visibleid = self.__stateMachine.state + + +class CheckBox(ToggleButton): + + def __init__(self, text="", skinObj=skin.Skin.default, **kwargs): + self.cfg = skinObj.defaultCheckBoxCfg + + uncheckedUpNode = self.__createImageNode(self.cfg["uncheckedUpBmp"]) + uncheckedDownNode = self.__createImageNode(self.cfg["uncheckedDownBmp"]) + uncheckedDisabledNode = self.__createImageNode(self.cfg["uncheckedDisabledBmp"]) + checkedUpNode = self.__createImageNode(self.cfg["checkedUpBmp"]) + checkedDownNode = self.__createImageNode(self.cfg["checkedDownBmp"]) + checkedDisabledNode = self.__createImageNode(self.cfg["checkedDisabledBmp"]) + + super(CheckBox, self).__init__(uncheckedUpNode=uncheckedUpNode, + uncheckedDownNode=uncheckedDownNode, + uncheckedDisabledNode=uncheckedDisabledNode, + checkedUpNode=checkedUpNode, + checkedDownNode=checkedDownNode, + checkedDisabledNode=checkedDisabledNode, + **kwargs) + self.textNode = avg.WordsNode(pos=(20,0), text=text, fontstyle=self.cfg["font"], + parent=self) + + def _enterUncheckedUp(self): + self.textNode.fontstyle = self.cfg["font"] + super(CheckBox, self)._enterUncheckedUp() + + def _enterUncheckedDown(self): + self.textNode.fontstyle = self.cfg["downFont"] + super(CheckBox, self)._enterUncheckedDown() + + def _enterCheckedUp(self): + self.textNode.fontstyle = self.cfg["font"] + super(CheckBox, self)._enterCheckedUp() + + def _enterCheckedDown(self): + self.textNode.fontstyle = self.cfg["downFont"] + super(CheckBox, self)._enterCheckedDown() + + def _enterUncheckedDisabled(self): + self.textNode.fontstyle = self.cfg["disabledFont"] + super(CheckBox, self)._enterUncheckedDisabled() + + def _enterCheckedDisabled(self): + self.textNode.fontstyle = self.cfg["disabledFont"] + super(CheckBox, self)._enterCheckedDisabled() + + def __createImageNode(self, bmp): + node = avg.ImageNode() + node.setBitmap(bmp) + return node + + +class BmpToggleButton(ToggleButton): + def __init__(self, uncheckedUpSrc, uncheckedDownSrc, checkedUpSrc, checkedDownSrc, + uncheckedDisabledSrc=None, checkedDisabledSrc=None, **kwargs): + uncheckedUpNode = avg.ImageNode(href=uncheckedUpSrc) + uncheckedDownNode = avg.ImageNode(href=uncheckedDownSrc) + checkedUpNode = avg.ImageNode(href=checkedUpSrc) + checkedDownNode = avg.ImageNode(href=checkedDownSrc) + + if uncheckedDisabledSrc != None: + uncheckedDisabledNode = avg.ImageNode(href=uncheckedDisabledSrc) + else: + uncheckedDisabledNode = None + if checkedDisabledSrc != None: + checkedDisabledNode = avg.ImageNode(href=checkedDisabledSrc) + else: + checkedDisabledNode = None + + super(BmpToggleButton, self).__init__(uncheckedUpNode=uncheckedUpNode, + uncheckedDownNode=uncheckedDownNode, + checkedUpNode=checkedUpNode, + checkedDownNode=checkedDownNode, + uncheckedDisabledNode=uncheckedDisabledNode, + checkedDisabledNode=checkedDisabledNode, + **kwargs) + |