toontown-just-works/otp/margins/ClickablePopup.py
2024-07-07 18:08:39 -05:00

162 lines
5.4 KiB
Python

from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from otp.nametag import NametagGlobals
class ClickablePopup(PandaNode, DirectObject):
CS_NORMAL = 0
CS_CLICK = 1
CS_HOVER = 2
CS_DISABLED = 3
def __init__(self, cam=None):
PandaNode.__init__(self, 'popup')
DirectObject.__init__(self)
self.__mwn = NametagGlobals.mouseWatcher
self.__name = 'clickregion-%d' % id(self)
self.__cam = cam
self.__region = MouseWatcherRegion(self.__name, 0, 0, 0, 0)
self.__mwn.addRegion(self.__region)
self.__disabled = False
self.__clicked = False
self.__hovered = False
self.__onscreen = False
self.__clickState = 0
self.__clickArgs = []
self.__clickEvent = ''
self.accept(self.__getEvent(self.__mwn.getEnterPattern()), self.__mouseEnter)
self.accept(self.__getEvent(self.__mwn.getLeavePattern()), self.__mouseLeave)
self.accept(self.__getEvent(self.__mwn.getButtonDownPattern()), self.__buttonDown)
self.accept(self.__getEvent(self.__mwn.getButtonUpPattern()), self.__buttonUp)
def destroy(self):
self.__mwn.removeRegion(self.__region)
self.ignoreAll()
def setClickRegionEvent(self, event, clickArgs=[]):
if event is None:
# The caller is disabling us, so instead:
self.__disabled = True
self.__region.setActive(False)
self.__updateClickState()
else:
self.__clickEvent = event
self.__clickArgs = clickArgs
self.__disabled = False
self.__region.setActive(True)
self.__updateClickState()
def getClickState(self):
return self.__clickState
def clickStateChanged(self):
pass # Intended for subclasses.
def __getEvent(self, pattern):
return pattern.replace('%r', self.__name)
def __mouseEnter(self, region, extra):
self.__hovered = True
self.__updateClickState()
def __mouseLeave(self, region, extra):
self.__hovered = False
self.__updateClickState()
def __buttonDown(self, region, button):
if button == 'mouse1':
self.__clicked = True
self.__updateClickState()
def __buttonUp(self, region, button):
if button == 'mouse1':
self.__clicked = False
self.__updateClickState()
def __updateClickState(self):
if self.__disabled:
state = self.CS_DISABLED
elif self.__clicked:
state = self.CS_CLICK
elif self.__hovered:
state = self.CS_HOVER
else:
state = self.CS_NORMAL
if self.__clickState == state: return
oldState = self.__clickState
self.__clickState = state
if oldState == self.CS_NORMAL and state == self.CS_HOVER:
# Play rollover sound:
base.playSfx(NametagGlobals.rolloverSound)
elif state == self.CS_CLICK:
# Play click sound:
base.playSfx(NametagGlobals.clickSound)
elif oldState == self.CS_CLICK and state == self.CS_HOVER:
# Fire click event:
messenger.send(self.__clickEvent, self.__clickArgs)
self.clickStateChanged()
def updateClickRegion(self, left, right, bottom, top, offset=0):
transform = NodePath.anyPath(self).getNetTransform()
if self.__cam:
# We have a camera, so get its transform and move our net transform
# into the coordinate space of the camera:
camTransform = self.__cam.getNetTransform()
transform = camTransform.invertCompose(transform)
# We must discard the rotational component on our transform, thus:
transform = transform.setQuat(Quat())
# Next, we'll transform the frame into camspace:
mat = transform.getMat()
cTopLeft = mat.xformPoint(Point3(left, 0, top))
cBottomRight = mat.xformPoint(Point3(right, 0, bottom))
# Shift along the offset while in camspace, not worldspace.
if offset:
mid = mat.xformPoint(Point3(0,0,0))
length = mid.length()
shift = mid*(length - offset)/length - mid
cTopLeft += shift
cBottomRight += shift
if self.__cam:
# We must go further and project to screenspace:
lens = self.__cam.node().getLens()
sTopLeft = Point2()
sBottomRight = Point2()
if not (lens.project(Point3(cTopLeft), sTopLeft) and
lens.project(Point3(cBottomRight), sBottomRight)):
# Not on-screen! Disable the click region:
self.__region.setActive(False)
self.__onscreen = False
return
else:
# No cam; the "camspace" (actually just net transform) IS the
# screenspace transform.
sTopLeft = Point2(cTopLeft[0], cTopLeft[2])
sBottomRight = Point2(cBottomRight[0], cBottomRight[2])
sLeft, sTop = sTopLeft
sRight, sBottom = sBottomRight
self.__region.setFrame(sLeft, sRight, sBottom, sTop)
self.__region.setActive(not self.__disabled)
self.__onscreen = True
def stashClickRegion(self):
self.__region.setActive(False)
self.__onscreen = False
def isOnScreen(self):
return self.__onscreen