From dba1abc3a447d5c3ac7ee1b88229903553a43377 Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Thu, 11 Oct 2001 17:46:21 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/gui/DirectDialog.py | 316 ++++++++++++++++++++++++----- direct/src/gui/DirectGui.py | 1 + direct/src/gui/DirectGuiBase.py | 10 +- direct/src/gui/DirectGuiGlobals.py | 3 +- direct/src/gui/DirectGuiTest.py | 28 +++ direct/src/gui/OnscreenText.py | 13 +- 6 files changed, 310 insertions(+), 61 deletions(-) diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index 69bb84218d..857bbe1744 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -4,61 +4,152 @@ from DirectButton import * class DirectDialog(DirectFrame): def __init__(self, parent = guiTop, **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 + 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 + 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 - ('pad', (0.4, 0.4), None), - ('text', '', None), - ('text_align', TMALIGNCENTER, None), - ('geom', getDefaultDialogGeom(), None), - ('relief', None, None), - ('buttons', [], INITOPT), - ('buttonOffset', 0.2, None), - ('button_pad', (.1,.1), None), - ('button_relief', RAISED, None), - ('button_frameSize', (-1,1,-.2,.9), None), - ('command', None, None), - ('extraArgs', [], None), + ('pad', (0.1, 0.1), None), + ('text', '', None), + ('text_align', TMALIGNLEFT, None), + ('text_scale', 0.06, None), + ('image', getDefaultDialogGeom(), None), + ('relief', None, None), + ('buttonTextList', [], INITOPT), + ('buttonGeomList', [], INITOPT), + ('buttonImageList', [], INITOPT), + ('buttonValueList', [], INITOPT), + ('button_borderWidth',(.01,.01), None), + ('button_pad', (.01,.01), None), + ('button_relief', RAISED, None), + ('button_text_scale' , 0.06, None), + ('buttonSize', None, INITOPT), + ('topPad', 0.06, INITOPT), + ('midPad', 0.12, INITOPT), + ('buttonPadSF', 1.05, INITOPT), + ('command', None, None), + ('extraArgs', [], None), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs, dynamicGroups = ("button",)) # Initialize superclasses DirectFrame.__init__(self, parent) - - self.numButtons = len(self['buttons']) + # Determine number of buttons + self.numButtons = max(len(self['buttonTextList']), + len(self['buttonGeomList']), + len(self['buttonImageList']), + len(self['buttonValueList'])) + # Create buttons self.buttonList = [] - for button in self['buttons']: - name = button + 'Button' + index = 0 + for i in range(self.numButtons): + name = 'Button' + `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 button = self.createcomponent( name, (), "button", DirectButton, (self,), - text = button, - command = lambda s = self, b = button: s.__buttonCommand(b) + text = text, + geom = geom, + image = image, + frameSize = self['buttonSize'], + command = lambda s = self, v = value: s.buttonCommand(v) ) self.buttonList.append(button) - self.numButtons += 1 + + # Update dialog when everything has been initialised + self.postInitialiseFuncList.append(self.configureDialog) self.initialiseoptions(DirectDialog) + def configureDialog(self): # Position buttons and text - # Get size of text - text = self.component('text0') - bounds = text.getTightBounds() pad = self['pad'] - l = bounds[0][0] - pad[0] - r = bounds[1][0] + pad[0] - b = bounds[0][2] - pad[1] - t = bounds[1][2] + pad[1] - text.setPos((l+r)/2.0, (b+t)/2.0) - # Move buttons - if self['button_frameSize']: - frameSize = self['button_frameSize'] - bl = frameSize[0] - br = frameSize[1] - bb = frameSize[2] - bt = frameSize[3] + bpad = self['button_pad'] + image = self.component('image0') + # 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)/2.0 + zOffset = -(b+t)/2.0 + # 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) + # 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: - # Get bounds of union of buttons + # Or get bounds of union of buttons bl = br = bb = bt = 0 for button in self.buttonList: bounds = button.stateNodePath[0].getTightBounds() @@ -66,41 +157,156 @@ class DirectDialog(DirectFrame): br = max(br, bounds[1][0]) bb = min(bb, bounds[0][2]) bt = max(bt, bounds[1][2]) - bWidth = br - bl - bSpacing = 1.1 * bWidth + 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 - index = 0 - if self.numButtons == 0: - return - bPos = bSpacing * (self.numButtons - 1)/2.0 - for button in self.buttonList: - button.setPos(bPos + index * bSpacing, 0, - b - self['buttonOffset'] - bt) - index += 1 - # Resize frame - b = b - self['buttonOffset'] - bHeight - pad[1] - frame = self.component('geom0') - frame.setScale(r - l, 1, t - b) - frame.setPos((l+r)/2.0, 0.0, (b+t)/2.0) + bWidth = br - bl + # Add pad between buttons + bSpacing = self['buttonPadSF'] * bWidth + if self.numButtons != 0: + bPos = -bSpacing * (self.numButtons - 1)/2.0 + 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: + bPos = 0 + bMax = 0 + # Resize frame to fit text and buttons + l = min(bPos + bl, l) - pad[0] + r = max(bMax + br, r) + pad[0] + # 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)/2.0, 0.0,(b+t)/2.0) self.resetFrameSize() - def __buttonCommand(self, button): + def buttonCommand(self, value): if self['command']: - self['command'](button) + self['command'](value) def destroy(self): DirectFrame.destroy(self) +class OKDialog(DirectDialog): + def __init__(self, parent = guiTop, **kw): + # Inherits from DirectFrame + optiondefs = ( + # Define type of DirectGuiWidget + ('buttonTextList', ['OK'], INITOPT), + ('buttonValueList', [1], 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 = guiTop, **kw): + # Inherits from DirectFrame + optiondefs = ( + # Define type of DirectGuiWidget + ('buttonTextList', ['OK','Cancel'], INITOPT), + ('buttonValueList', [1,-1], 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 = guiTop, **kw): # Inherits from DirectFrame optiondefs = ( # Define type of DirectGuiWidget - ('buttons', ['Yes', 'No'], INITOPT), - ('text', 'Yes or No?', None), + ('buttonTextList', ['Yes', 'No'], INITOPT), + ('buttonValueList', [1,0], INITOPT), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs) - apply(DirectDialog.__init__, (self, parent), kw) + DirectDialog.__init__(self, parent) self.initialiseoptions(YesNoDialog) + +class YesNoCancelDialog(DirectDialog): + def __init__(self, parent = guiTop, **kw): + # Inherits from DirectFrame + optiondefs = ( + # Define type of DirectGuiWidget + ('buttonTextList', ['Yes', 'No', 'Cancel'], INITOPT), + ('buttonValueList', [1,0,-1], 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 = guiTop, **kw): + # Inherits from DirectFrame + optiondefs = ( + # Define type of DirectGuiWidget + ('buttonTextList', ['Retry','Cancel'], INITOPT), + ('buttonValueList', [1,-1], INITOPT), + ) + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + DirectDialog.__init__(self, parent) + self.initialiseoptions(RetryCancelDialog) + +class TTOkCancelDialog(DirectDialog): + def __init__(self, parent = guiTop, **kw): + # Inherits from DirectDialog + buttons = loader.loadModelOnce( + 'phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), + buttons.find('**/ChtBx_OKBtn_DN'), + buttons.find('**/ChtBx_OKBtn_Rllvr')) + okTextList = ('', 'OK', 'OK') + cancelImageList = (buttons.find('**/CloseBtn_UP'), + buttons.find('**/CloseBtn_DN'), + buttons.find('**/CloseBtn_Rllvr')) + cancelTextList = ('', 'Cancel', 'Cancel') + optiondefs = ( + # Define type of DirectGuiWidget + ('buttonTextList', [okTextList, cancelTextList], INITOPT), + ('buttonImageList', [okImageList, cancelImageList], INITOPT), + ('buttonValueList', [1,-1], INITOPT), + ('button_pad', (0,0), None), + ('button_relief', None, None), + ('button_text_pos', (0,-0.1), None), + ('buttonSize', (-.05,.05,-.05,.05), None), + ) + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + DirectDialog.__init__(self, parent) + self.initialiseoptions(TTOkCancelDialog) + diff --git a/direct/src/gui/DirectGui.py b/direct/src/gui/DirectGui.py index 33cfa9831b..6709022d1f 100644 --- a/direct/src/gui/DirectGui.py +++ b/direct/src/gui/DirectGui.py @@ -15,3 +15,4 @@ from DirectButton import * from DirectEntry import * from DirectLabel import * from DirectScrolledList import * +from DirectDialog import * diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 6d0e80aecf..6f68ffc103 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -95,6 +95,8 @@ class DirectGuiBase(PandaObject.PandaObject): self.guiId = 'guiObject' # List of all active hooks self._hookDict = {} + # List of all post initialization functions + self.postInitialiseFuncList = [] # To avoid doing things redundantly during initialisation self.fInit = 1 # Mapping from each megawidget option to a list of information @@ -261,7 +263,8 @@ class DirectGuiBase(PandaObject.PandaObject): self.postInitialiseFunc() def postInitialiseFunc(self): - pass + for func in self.postInitialiseFuncList: + func() def isinitoption(self, option): """ @@ -758,11 +761,14 @@ class DirectGuiWidget(DirectGuiBase, NodePath): # Bind destroy hook self.bind(DESTROY, self.destroy) + + # Update frame when everything has been initialized + self.postInitialiseFuncList.append(self.frameInitialiseFunc) # Call option initialization functions self.initialiseoptions(DirectGuiWidget) - def postInitialiseFunc(self): + def frameInitialiseFunc(self): # Now allow changes to take effect self.updateFrameStyle() if not self['frameSize']: diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index 1eba2d66d8..388f0ae823 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -96,7 +96,8 @@ def setDefaultFont(newFont): def getDefaultDialogGeom(): global defaultDialogGeom if defaultDialogGeom == None: - defaultDialogGeom = loader.loadModelOnce('phase_3/models/props/panel') + defaultDialogGeom = loader.loadModelOnce( + 'phase_3/models/gui/dialog_box_gui') return defaultDialogGeom def setDefaultDialogGeom(newDialogGeom): diff --git a/direct/src/gui/DirectGuiTest.py b/direct/src/gui/DirectGuiTest.py index 95e626d12e..553ca8b4e1 100644 --- a/direct/src/gui/DirectGuiTest.py +++ b/direct/src/gui/DirectGuiTest.py @@ -96,6 +96,34 @@ de1 = DirectEntry(initialText = 'Hello, how are you?', scale = 0.0707855, ) +# DIRECT DIALOG EXAMPLE +def printDialogValue(value): + print 'Value:', value + +simpleDialog = YesNoDialog(text = 'Simple', + command = printDialogValue) + +customValues = YesNoDialog(text = 'Simple', + buttonValueList = ['Yes', 'No'], + command = printDialogValue) + + +fancyDialog = YesNoDialog(text = 'Testing Direct Dialog', + geom = smiley, + geom_scale = .1, + geom_pos = (-0.3,0,0), + command = printDialogValue) + +toontownDialog = TTOkCancelDialog(text = 'Exit Toontown?', + command = printDialogValue) + +customDialog = DirectDialog(text = 'Pick a number', + buttonTextList = map(str, range(10)), + buttonValueList = range(10), + command = printDialogValue) + + + # NOTE: There are some utility functions which help you get size # of a direct gui widget. These can be used to position and scale an # image after you've created the entry. scale = (width/2, 1, height/2) diff --git a/direct/src/gui/OnscreenText.py b/direct/src/gui/OnscreenText.py index e80db15aca..d79f4a3207 100644 --- a/direct/src/gui/OnscreenText.py +++ b/direct/src/gui/OnscreenText.py @@ -179,7 +179,8 @@ class OnscreenText(PandaObject, NodePath): # We'd rather do it here, on the text itself, rather than on # our NodePath, so we have one fewer transforms in the scene # graph. - mat = Mat4.scaleMat(scale[0], 1, scale[1]) * Mat4.translateMat(pos[0], 0, pos[1]) + mat = Mat4.scaleMat( + scale[0], 1, scale[1]) * Mat4.translateMat(pos[0], 0, pos[1]) textNode.setTransform(mat) textNode.setBin('fixed') @@ -259,7 +260,10 @@ class OnscreenText(PandaObject, NodePath): Position the onscreen text in 2d screen space """ self.pos = (x, y) - mat = Mat4.scaleMat(self.scale[0], 1, self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1]) + mat = Mat4.scaleMat( + self.scale[0], + 1, + self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1]) self.textNode.setTransform(mat) def getPos(self): @@ -278,7 +282,10 @@ class OnscreenText(PandaObject, NodePath): self.scale = (sx, sx) else: self.scale = (sx, sy) - mat = Mat4.scaleMat(self.scale[0], 1, self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1]) + mat = Mat4.scaleMat( + self.scale[0], + 1, + self.scale[1]) * Mat4.translateMat(self.pos[0], 0, self.pos[1]) self.textNode.setTransform(mat) def getScale(self):