mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-25 04:32:33 -06:00
409 lines
16 KiB
Python
409 lines
16 KiB
Python
"""Undocumented Module"""
|
|
|
|
__all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
|
|
|
|
from pandac.PandaModules import *
|
|
import DirectGuiGlobals as DGG
|
|
from DirectFrame import *
|
|
from DirectButton import *
|
|
import types
|
|
|
|
def findDialog(uniqueName):
|
|
"""findPanel(string uniqueName)
|
|
|
|
Returns the panel whose uniqueName is given. This is mainly
|
|
useful for debugging, to get a pointer to the current onscreen
|
|
panel of a particular type.
|
|
"""
|
|
if uniqueName in DirectDialog.AllDialogs:
|
|
return DirectDialog.AllDialogs[uniqueName]
|
|
return None
|
|
|
|
def cleanupDialog(uniqueName):
|
|
"""cleanupPanel(string uniqueName)
|
|
|
|
Cleans up (removes) the panel with the given uniqueName. This
|
|
may be useful when some panels know about each other and know
|
|
that opening panel A should automatically close panel B, for
|
|
instance.
|
|
"""
|
|
if uniqueName in DirectDialog.AllDialogs:
|
|
# calling cleanup() will remove it out of the AllDialogs dict
|
|
# This way it will get removed from the dict even it we did
|
|
# not clean it up using this interface (ie somebody called
|
|
# self.cleanup() directly
|
|
DirectDialog.AllDialogs[uniqueName].cleanup()
|
|
|
|
class DirectDialog(DirectFrame):
|
|
|
|
AllDialogs = {}
|
|
PanelIndex = 0
|
|
|
|
def __init__(self, parent = None, **kw):
|
|
"""
|
|
DirectDialog(kw)
|
|
|
|
Creates a popup dialog to alert and/or interact with user.
|
|
Some of the main keywords that can be used to customize the dialog:
|
|
Keyword Definition
|
|
------- ----------
|
|
text Text message/query displayed to user
|
|
geom Geometry to be displayed in dialog
|
|
buttonTextList List of text to show on each button
|
|
buttonGeomList List of geometry to show on each button
|
|
buttonImageList List of images to show on each button
|
|
buttonValueList List of values sent to dialog command for
|
|
each button. If value is [] then the
|
|
ordinal rank of the button is used as
|
|
its value
|
|
buttonHotKeyList List of hotkeys to bind to each button.
|
|
Typing hotkey is equivalent to pressing
|
|
the corresponding button.
|
|
suppressKeys Set to true if you wish to suppress keys
|
|
(i.e. Dialog eats key event), false if
|
|
you wish Dialog to pass along key event
|
|
buttonSize 4-tuple used to specify custom size for
|
|
each button (to make bigger then geom/text
|
|
for example)
|
|
pad Space between border and interior graphics
|
|
topPad Extra space added above text/geom/image
|
|
midPad Extra space added between text/buttons
|
|
sidePad Extra space added to either side of
|
|
text/buttons
|
|
buttonPadSF Scale factor used to expand/contract
|
|
button horizontal spacing
|
|
command Callback command used when a button is
|
|
pressed. Value supplied to command
|
|
depends on values in buttonValueList
|
|
|
|
Note: Number of buttons on the dialog depends upon the maximum
|
|
length of any button[Text|Geom|Image|Value]List specified.
|
|
Values of None are substituted for lists that are shorter
|
|
than the max length
|
|
"""
|
|
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('dialogName', 'DirectDialog_' + repr(DirectDialog.PanelIndex), DGG.INITOPT),
|
|
# Default position is slightly forward in Y, so as not to
|
|
# intersect the near plane, which is incorrectly set to 0
|
|
# in DX for some reason.
|
|
('pos', (0, 0.1, 0), None),
|
|
('pad', (0.1, 0.1), None),
|
|
('text', '', None),
|
|
('text_align', TextNode.ALeft, None),
|
|
('text_scale', 0.06, None),
|
|
('image', DGG.getDefaultDialogGeom(), None),
|
|
('relief', None, None),
|
|
('buttonTextList', [], DGG.INITOPT),
|
|
('buttonGeomList', [], DGG.INITOPT),
|
|
('buttonImageList', [], DGG.INITOPT),
|
|
('buttonValueList', [], DGG.INITOPT),
|
|
('buttonHotKeyList', [], DGG.INITOPT),
|
|
('button_borderWidth', (.01, .01), None),
|
|
('button_pad', (.01, .01), None),
|
|
('button_relief', DGG.RAISED, None),
|
|
('button_text_scale', 0.06, None),
|
|
('buttonSize', None, DGG.INITOPT),
|
|
('topPad', 0.06, DGG.INITOPT),
|
|
('midPad', 0.12, DGG.INITOPT),
|
|
('sidePad', 0., DGG.INITOPT),
|
|
('buttonPadSF', 1.1, DGG.INITOPT),
|
|
# Alpha of fade screen behind dialog
|
|
('fadeScreen', 0, None),
|
|
('command', None, None),
|
|
('extraArgs', [], None),
|
|
('sortOrder', NO_FADE_SORT_INDEX, None),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs, dynamicGroups = ("button",))
|
|
|
|
# Initialize superclasses
|
|
DirectFrame.__init__(self, parent)
|
|
|
|
#if not self['dialogName']:
|
|
# self['dialogName'] = 'DirectDialog_' + repr(DirectDialog.PanelIndex)
|
|
|
|
# Clean up any previously existing panel with the same unique
|
|
# name. We don't allow any two panels with the same name to
|
|
# coexist.
|
|
cleanupDialog(self['dialogName'])
|
|
# Store this panel in our map of all open panels.
|
|
DirectDialog.AllDialogs[self['dialogName']] = self
|
|
DirectDialog.PanelIndex += 1
|
|
|
|
# Determine number of buttons
|
|
self.numButtons = max(len(self['buttonTextList']),
|
|
len(self['buttonGeomList']),
|
|
len(self['buttonImageList']),
|
|
len(self['buttonValueList']))
|
|
# Create buttons
|
|
self.buttonList = []
|
|
index = 0
|
|
for i in range(self.numButtons):
|
|
name = 'Button' + repr(i)
|
|
try:
|
|
text = self['buttonTextList'][i]
|
|
except IndexError:
|
|
text = None
|
|
try:
|
|
geom = self['buttonGeomList'][i]
|
|
except IndexError:
|
|
geom = None
|
|
try:
|
|
image = self['buttonImageList'][i]
|
|
except IndexError:
|
|
image = None
|
|
try:
|
|
value = self['buttonValueList'][i]
|
|
except IndexError:
|
|
value = i
|
|
self['buttonValueList'].append(i)
|
|
try:
|
|
hotKey = self['buttonHotKeyList'][i]
|
|
except IndexError:
|
|
hotKey = None
|
|
button = self.createcomponent(
|
|
name, (), "button",
|
|
DirectButton, (self,),
|
|
text = text,
|
|
geom = geom,
|
|
image = image,
|
|
suppressKeys = self['suppressKeys'],
|
|
frameSize = self['buttonSize'],
|
|
command = lambda s = self, v = value: s.buttonCommand(v)
|
|
)
|
|
self.buttonList.append(button)
|
|
|
|
# Update dialog when everything has been initialised
|
|
self.postInitialiseFuncList.append(self.configureDialog)
|
|
self.initialiseoptions(DirectDialog)
|
|
|
|
def configureDialog(self):
|
|
# Set up hot key bindings
|
|
bindList = zip(self.buttonList, self['buttonHotKeyList'],
|
|
self['buttonValueList'])
|
|
for button, hotKey, value in bindList:
|
|
if ((type(hotKey) == types.ListType) or
|
|
(type(hotKey) == types.TupleType)):
|
|
for key in hotKey:
|
|
button.bind('press-' + key + '-', self.buttonCommand,
|
|
extraArgs = [value])
|
|
self.bind('press-' + key + '-', self.buttonCommand,
|
|
extraArgs = [value])
|
|
|
|
else:
|
|
button.bind('press-' + hotKey + '-', self.buttonCommand,
|
|
extraArgs = [value])
|
|
self.bind('press-' + hotKey + '-', self.buttonCommand,
|
|
extraArgs = [value])
|
|
# Position buttons and text
|
|
pad = self['pad']
|
|
if self.hascomponent('image0'):
|
|
image = self.component('image0')
|
|
else:
|
|
image = None
|
|
# Get size of text/geom without image (for state 0)
|
|
if image:
|
|
image.reparentTo(hidden)
|
|
bounds = self.stateNodePath[0].getTightBounds()
|
|
if image:
|
|
image.reparentTo(self.stateNodePath[0])
|
|
l = bounds[0][0]
|
|
r = bounds[1][0]
|
|
b = bounds[0][2]
|
|
t = bounds[1][2]
|
|
# Center text and geom around origin
|
|
# How far is center of text from origin?
|
|
xOffset = -(l+r)*0.5
|
|
zOffset = -(b+t)*0.5
|
|
# Update bounds to reflect text movement
|
|
l += xOffset
|
|
r += xOffset
|
|
b += zOffset
|
|
t += zOffset
|
|
# Offset text and geom to center
|
|
if self['text']:
|
|
self['text_pos'] = (self['text_pos'][0] + xOffset,
|
|
self['text_pos'][1] + zOffset)
|
|
if self['geom']:
|
|
self['geom_pos'] = Point3(self['geom_pos'][0] + xOffset,
|
|
self['geom_pos'][1],
|
|
self['geom_pos'][2] + zOffset)
|
|
if self.numButtons != 0:
|
|
bpad = self['button_pad']
|
|
# Get button size
|
|
if self['buttonSize']:
|
|
# Either use given size
|
|
buttonSize = self['buttonSize']
|
|
bl = buttonSize[0]
|
|
br = buttonSize[1]
|
|
bb = buttonSize[2]
|
|
bt = buttonSize[3]
|
|
else:
|
|
# Or get bounds of union of buttons
|
|
bl = br = bb = bt = 0
|
|
for button in self.buttonList:
|
|
bounds = button.stateNodePath[0].getTightBounds()
|
|
bl = min(bl, bounds[0][0])
|
|
br = max(br, bounds[1][0])
|
|
bb = min(bb, bounds[0][2])
|
|
bt = max(bt, bounds[1][2])
|
|
bl -= bpad[0]
|
|
br += bpad[0]
|
|
bb -= bpad[1]
|
|
bt += bpad[1]
|
|
# Now resize buttons to match largest
|
|
for button in self.buttonList:
|
|
button['frameSize'] = (bl, br, bb, bt)
|
|
# Must compensate for scale
|
|
scale = self['button_scale']
|
|
# Can either be a Vec3 or a tuple of 3 values
|
|
if (isinstance(scale, Vec3) or
|
|
(type(scale) == types.ListType) or
|
|
(type(scale) == types.TupleType)):
|
|
sx = scale[0]
|
|
sz = scale[2]
|
|
elif ((type(scale) == types.IntType) or
|
|
(type(scale) == types.FloatType)):
|
|
sx = sz = scale
|
|
else:
|
|
sx = sz = 1
|
|
bl *= sx
|
|
br *= sx
|
|
bb *= sz
|
|
bt *= sz
|
|
# Position buttons
|
|
# Calc button width and height
|
|
bHeight = bt - bb
|
|
bWidth = br - bl
|
|
# Add pad between buttons
|
|
bSpacing = self['buttonPadSF'] * bWidth
|
|
bPos = -bSpacing * (self.numButtons - 1)*0.5
|
|
index = 0
|
|
for button in self.buttonList:
|
|
button.setPos(bPos + index * bSpacing, 0,
|
|
b - self['midPad'] - bpad[1] - bt)
|
|
index += 1
|
|
bMax = bPos + bSpacing * (self.numButtons - 1)
|
|
else:
|
|
bpad = 0
|
|
bl = br = bb = bt = 0
|
|
bPos = 0
|
|
bMax = 0
|
|
bpad = (0, 0)
|
|
bHeight = bWidth = 0
|
|
# Resize frame to fit text and buttons
|
|
l = min(bPos + bl, l) - pad[0]
|
|
r = max(bMax + br, r) + pad[0]
|
|
sidePad = self['sidePad']
|
|
l -= sidePad
|
|
r += sidePad
|
|
# reduce bottom by pad, button height and 2*button pad
|
|
b = min(b - self['midPad'] - bpad[1] - bHeight - bpad[1], b) - pad[1]
|
|
t = t + self['topPad'] + pad[1]
|
|
self['image_scale'] = (r - l, 1, t - b)
|
|
# Center frame about text and buttons
|
|
self['image_pos'] = ((l+r)*0.5, 0.0, (b+t)*0.5)
|
|
self.resetFrameSize()
|
|
|
|
def show(self):
|
|
if self['fadeScreen']:
|
|
base.transitions.fadeScreen(self['fadeScreen'])
|
|
self.setBin('gui-popup', 0)
|
|
NodePath.show(self)
|
|
|
|
def hide(self):
|
|
if self['fadeScreen']:
|
|
base.transitions.noTransitions()
|
|
NodePath.hide(self)
|
|
|
|
def buttonCommand(self, value, event = None):
|
|
if self['command']:
|
|
self['command'](value, *self['extraArgs'])
|
|
|
|
def setMessage(self, message):
|
|
self['text'] = message
|
|
self.configureDialog()
|
|
|
|
def cleanup(self):
|
|
# Remove this panel out of the AllDialogs list
|
|
uniqueName = self['dialogName']
|
|
if uniqueName in DirectDialog.AllDialogs:
|
|
del DirectDialog.AllDialogs[uniqueName]
|
|
self.destroy()
|
|
|
|
def destroy(self):
|
|
if self['fadeScreen']:
|
|
base.transitions.noTransitions()
|
|
for button in self.buttonList:
|
|
button.destroy()
|
|
DirectFrame.destroy(self)
|
|
|
|
class OkDialog(DirectDialog):
|
|
def __init__(self, parent = None, **kw):
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('buttonTextList', ['OK'], DGG.INITOPT),
|
|
('buttonValueList', [DGG.DIALOG_OK], DGG.INITOPT),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs)
|
|
DirectDialog.__init__(self, parent)
|
|
self.initialiseoptions(OkDialog)
|
|
|
|
class OkCancelDialog(DirectDialog):
|
|
def __init__(self, parent = None, **kw):
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('buttonTextList', ['OK','Cancel'], DGG.INITOPT),
|
|
('buttonValueList', [DGG.DIALOG_OK, DGG.DIALOG_CANCEL], DGG.INITOPT),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs)
|
|
DirectDialog.__init__(self, parent)
|
|
self.initialiseoptions(OkCancelDialog)
|
|
|
|
class YesNoDialog(DirectDialog):
|
|
def __init__(self, parent = None, **kw):
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('buttonTextList', ['Yes', 'No'], DGG.INITOPT),
|
|
('buttonValueList', [DGG.DIALOG_YES, DGG.DIALOG_NO], DGG.INITOPT),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs)
|
|
DirectDialog.__init__(self, parent)
|
|
self.initialiseoptions(YesNoDialog)
|
|
|
|
class YesNoCancelDialog(DirectDialog):
|
|
def __init__(self, parent = None, **kw):
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('buttonTextList', ['Yes', 'No', 'Cancel'], DGG.INITOPT),
|
|
('buttonValueList', [DGG.DIALOG_YES, DGG.DIALOG_NO, DGG.DIALOG_CANCEL],
|
|
DGG.INITOPT),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs)
|
|
DirectDialog.__init__(self, parent)
|
|
self.initialiseoptions(YesNoCancelDialog)
|
|
|
|
class RetryCancelDialog(DirectDialog):
|
|
def __init__(self, parent = None, **kw):
|
|
# Inherits from DirectFrame
|
|
optiondefs = (
|
|
# Define type of DirectGuiWidget
|
|
('buttonTextList', ['Retry','Cancel'], DGG.INITOPT),
|
|
('buttonValueList', [DGG.DIALOG_RETRY, DGG.DIALOG_CANCEL], DGG.INITOPT),
|
|
)
|
|
# Merge keyword options with default options
|
|
self.defineoptions(kw, optiondefs)
|
|
DirectDialog.__init__(self, parent)
|
|
self.initialiseoptions(RetryCancelDialog)
|
|
|