# $Id: ButtonBase.py,v 1.3.2.6 2006/09/16 11:28:20 marcusva Exp $ # # Copyright (c) 2006, Marcus von Appen # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """An abstract widget for button type widget implementations.""" from pygame import time as PygameTime from pygame import K_SPACE, K_KP_ENTER, K_RETURN from Bin import Bin from Constants import * import base class ButtonBase (Bin): """ ButtonBase () -> ButtonBase An abstract base class that implements basic button logic. The ButtonBase class is an abstract class, that implements a minimal set of events and methods to make it suitable for button type widgets. It implements the most important mouse signal types and how to handle them.. Default action (invoked by activate()): The ButtonBase emulates a SIG_CLICKED event and runs the connected callbacks. Mnemonic action (invoked by activate_mnemonic()): The ButtonBase invokes the activate_mnemonic() method of its child (if any). Signals: SIG_MOUSEDOWN - Invoked, when a mouse button is pressed on the ButtonBase. SIG_MOUSEUP - Invoked, when a mouse button is released on the ButtonBase. SIG_MOUSEMOVE - Invoked, when the mouse moves over the ButtonBase. SIG_CLICKED - Invoked, when the left mouse button is pressed AND released over the ButtonBase. """ def __init__ (self): Bin.__init__ (self) # Internal click detector. self.__click = False # Signals, the button listens to. self._signals[SIG_MOUSEDOWN] = [] self._signals[SIG_MOUSEUP] = [] self._signals[SIG_MOUSEMOVE] = [] self._signals[SIG_KEYDOWN] = None # Dummy for keyboard activation. self._signals[SIG_CLICKED] = [] def activate_mnemonic (self, mnemonic): """B.activate_mnemonic (...) -> bool Activates the mnemonic of the ButtonBase its child. """ if self.child: return self.child.activate_mnemonic (mnemonic) return False def activate (self): """B.activate () -> None Activates the ButtonBase default action. Activates the ButtonBase default action. This usually means a click, emulated by setting the state to STATE_ACTIVE, forcing an update, setting the state back to STATE_NORMAL and running the attached callbacks for the SIG_CLICKED event. """ if not self.sensitive: return self.lock () self.focus = True self.state = STATE_ACTIVE self.unlock () PygameTime.delay (50) self.state = STATE_NORMAL self.run_signal_handlers (SIG_CLICKED) def notify (self, event): """B.notify (...) -> None Notifies the ButtonBase about an event. """ if not self.sensitive: return if event.signal in SIGNALS_MOUSE: eventarea = self.rect_to_client () if event.signal == SIG_MOUSEDOWN: if eventarea.collidepoint (event.data.pos): self.focus = True # The button only acts upon left clicks. if event.data.button == 1: self.__click = True self.state = STATE_ACTIVE self.run_signal_handlers (SIG_MOUSEDOWN, event.data) event.handled = True elif event.signal == SIG_MOUSEUP: if eventarea.collidepoint (event.data.pos): self.run_signal_handlers (SIG_MOUSEUP, event.data) if event.data.button == 1: if self.state == STATE_ACTIVE: self.state = STATE_ENTERED else: self.state = STATE_NORMAL # Check for a previous left click. if self.__click: self.__click = False self.run_signal_handlers (SIG_CLICKED) event.handled = True elif event.data.button == 1: # Reset the 'clicked' state for the button, if the mouse # button 1 is released at another location. self.__click = False self.state = STATE_NORMAL elif event.signal == SIG_MOUSEMOVE: if eventarea.collidepoint (event.data.pos): if not self.__click: self.state = STATE_ENTERED else: self.state = STATE_ACTIVE self.run_signal_handlers (SIG_MOUSEMOVE, event.data) self.entered = True event.handled = True else: self.entered = False elif (event.signal == SIG_KEYDOWN) and self.focus: if event.data.key in (K_SPACE, K_KP_ENTER, K_RETURN): # Activate the focused button, if the user presses # space, return or enter. self.activate () event.handled = True Bin.notify (self, event)