historical/toontown-classic.git/panda/direct/gui/DirectButton.py

169 lines
7.2 KiB
Python
Raw Normal View History

2024-01-16 11:20:27 -06:00
"""This module contains the DirectButton class."""
__all__ = ['DirectButton']
from panda3d.core import *
from . import DirectGuiGlobals as DGG
from .DirectFrame import *
class DirectButton(DirectFrame):
"""
DirectButton(parent) - Create a DirectGuiWidget which responds
to mouse clicks and execute a callback function if defined
"""
def __init__(self, parent = None, **kw):
# Inherits from DirectFrame
# A Direct Frame can have:
# - A background texture (pass in path to image, or Texture Card)
# - A midground geometry item (pass in geometry)
# - A foreground text Node (pass in text string or Onscreen Text)
# For a direct button:
# Each button has 4 states (ready, press, rollover, disabled)
# The same image/geom/text can be used for all four states or each
# state can have a different text/geom/image
# State transitions happen automatically based upon mouse interaction
# Responds to click event and calls command if None
optiondefs = (
# Define type of DirectGuiWidget
('pgFunc', PGButton, None),
('numStates', 4, None),
('state', DGG.NORMAL, None),
('relief', DGG.RAISED, None),
('invertedFrames', (1,), None),
# Command to be called on button click
('command', None, None),
('extraArgs', [], None),
# Which mouse buttons can be used to click the button
('commandButtons', (DGG.LMB,), self.setCommandButtons),
# Whether the button continuously runs the command if pressed
# The default repeat threshold is 0.01
# The default delay multiplier is 0.5
('repeat', False, None),
('repeatThreshold', 0.01, None),
('repeatDelay', 1.0, None),
('repeatDecay', 0.5, None),
# Sounds to be used for button events
('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound),
('clickSound', DGG.getDefaultClickSound(), self.setClickSound),
# Can only be specified at time of widget contruction
# Do the text/graphics appear to move when the button is clicked
('pressEffect', 1, DGG.INITOPT),
)
# Merge keyword options with default options
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectFrame.__init__(self, parent)
# If specifed, add scaling to the pressed state to make it
# look like the button is moving when you press it. We have
# to set up the node first, before we call initialise options;
# but we can't actually apply the scale until we have the
# bounding volume (which happens during initialise options).
pressEffectNP = None
if self['pressEffect']:
pressEffectNP = self.stateNodePath[1].attachNewNode('pressEffect', 1)
self.stateNodePath[1] = pressEffectNP
# Call option initialization functions
self.initialiseoptions(DirectButton)
# Now apply the scale.
if pressEffectNP:
bounds = self.getBounds()
centerX = (bounds[0] + bounds[1]) / 2
centerY = (bounds[2] + bounds[3]) / 2
# Make a matrix that scales about the point
mat = Mat4.translateMat(-centerX, 0, -centerY) * \
Mat4.scaleMat(0.98) * \
Mat4.translateMat(centerX, 0, centerY)
pressEffectNP.setMat(mat)
def setCommandButtons(self):
# Attach command function to specified buttons
# Left mouse button
if DGG.LMB in self['commandButtons']:
self.guiItem.addClickButton(MouseButton.one())
self.bind(DGG.B1CLICK, self.commandFunc)
if self['repeat']:
self.bind(DGG.B1PRESS, self.__repeat, extraArgs=[True])
self.bind(DGG.B1RELEASE, self.__repeat, extraArgs=[False])
else:
if self['repeat']:
self.unbind(DGG.B1PRESS)
self.unbind(DGG.B1RELEASE)
self.unbind(DGG.B1CLICK)
self.guiItem.removeClickButton(MouseButton.one())
# Middle mouse button
if DGG.MMB in self['commandButtons']:
self.guiItem.addClickButton(MouseButton.two())
self.bind(DGG.B2CLICK, self.commandFunc)
if self['repeat']:
self.bind(DGG.B2PRESS, self.__repeat, extraArgs=[True])
self.bind(DGG.B2RELEASE, self.__repeat, extraArgs=[False])
else:
if self['repeat']:
self.unbind(DGG.B2PRESS)
self.unbind(DGG.B2RELEASE)
self.unbind(DGG.B2CLICK)
self.guiItem.removeClickButton(MouseButton.two())
# Right mouse button
if DGG.RMB in self['commandButtons']:
self.guiItem.addClickButton(MouseButton.three())
self.bind(DGG.B3CLICK, self.commandFunc)
if self['repeat']:
self.bind(DGG.B3PRESS, self.__repeat, extraArgs=[True])
self.bind(DGG.B3RELEASE, self.__repeat, extraArgs=[False])
else:
if self['repeat']:
self.unbind(DGG.B3PRESS)
self.unbind(DGG.B3RELEASE)
self.unbind(DGG.B3CLICK)
self.guiItem.removeClickButton(MouseButton.three())
def commandFunc(self, event):
if self['command']:
# Pass any extra args to command
self['command'](*self['extraArgs'])
def __repeat(self, state, event):
if state and not taskMgr.hasTaskNamed(self.uniqueName('__repeatFunc')):
task = self.addTask(self.__repeatFunc, self.uniqueName('__repeatFunc'), extraArgs=[event], appendTask=True)
task.delayTime = self['repeatDelay']
task.prevTime = 0.0
elif not state:
self.removeTask(self.uniqueName('__repeatFunc'))
def __repeatFunc(self, event, task):
if task.time - task.prevTime < task.delayTime:
return task.cont
else:
self.commandFunc(event)
task.delayTime = max(self['repeatThreshold'], task.delayTime * self['repeatDecay'])
task.prevTime = task.time
return task.cont
def setClickSound(self):
clickSound = self['clickSound']
# Clear out sounds
self.guiItem.clearSound(DGG.B1PRESS + self.guiId)
self.guiItem.clearSound(DGG.B2PRESS + self.guiId)
self.guiItem.clearSound(DGG.B3PRESS + self.guiId)
if clickSound:
if DGG.LMB in self['commandButtons']:
self.guiItem.setSound(DGG.B1PRESS + self.guiId, clickSound)
if DGG.MMB in self['commandButtons']:
self.guiItem.setSound(DGG.B2PRESS + self.guiId, clickSound)
if DGG.RMB in self['commandButtons']:
self.guiItem.setSound(DGG.B3PRESS + self.guiId, clickSound)
def setRolloverSound(self):
rolloverSound = self['rolloverSound']
if rolloverSound:
self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound)
else:
self.guiItem.clearSound(DGG.ENTER + self.guiId)