diff --git a/direct/src/gui/Button.py b/direct/src/gui/Button.py index 3282a8f39a..683eb23287 100644 --- a/direct/src/gui/Button.py +++ b/direct/src/gui/Button.py @@ -13,11 +13,8 @@ class Button(DirectObject): width = None, drawOrder = getDefaultDrawOrder(), font = getDefaultFont(), - height = None, - left = None, - right = None, - bottom = None, - top = None): + pos = (0, 0), + geomRect = None): self.name = name self.width = width # if no label given, use the button name @@ -43,19 +40,10 @@ class Button(DirectObject): elif (isinstance(label, NodePath)): # If it's a NodePath, assume it's a little texture card. - if height != None and width != None: - self.l1 = Label.modelLabel(label, height, width, - scale = scale, - drawOrder = drawOrder) - elif left != None and right != None and \ - bottom != None and top != None: - self.l1 = Label.modelLabel(label, left, right, bottom, top, - scale = scale, - drawOrder = drawOrder) - else: - self.l1 = Label.modelLabel(label, 1, 1, - scale = scale, - drawOrder = drawOrder) + self.l1 = Label.modelLabel(label, + geomRect = geomRect, + scale = scale, + drawOrder = drawOrder) if width == None: width = self.l1.getWidth() / scale @@ -73,6 +61,7 @@ class Button(DirectObject): self.button = GuiButton.GuiButton(self.name, self.l1, self.l2, self.l3, self.l3, self.l1) self.button.setDrawOrder(drawOrder) + self.setPos(pos[0], pos[1]) self.managed = 0 return None diff --git a/direct/src/gui/GuiGlobals.py b/direct/src/gui/GuiGlobals.py index 1efa1caa24..e505727875 100644 --- a/direct/src/gui/GuiGlobals.py +++ b/direct/src/gui/GuiGlobals.py @@ -6,16 +6,27 @@ import GuiManager guiMgr = GuiManager.GuiManager.getPtr(base.win, base.mak.node(), base.render2d.node()) -font = loader.loadModelNode("models/fonts/ttf-comic") +font = None +panel = None drawOrder = 100 def getDefaultFont(): + global font + if font == None: + font = loader.loadModelNode("models/fonts/ttf-comic") return font def setDefaultFont(newFont): global font font = newFont +def getDefaultPanel(): + return panel + +def setDefaultPanel(newPanel): + global panel + panel = newPanel + def getDefaultDrawOrder(): return drawOrder diff --git a/direct/src/gui/Label.py b/direct/src/gui/Label.py index 280a268bee..6aa0422b3a 100644 --- a/direct/src/gui/Label.py +++ b/direct/src/gui/Label.py @@ -113,7 +113,8 @@ def textLabelAndText(string, style, return (label, text) -def modelLabel(model, height, width, bottom = None, top = None, +def modelLabel(model, + geomRect = None, scale = 0.1, drawOrder = getDefaultDrawOrder()): @@ -124,13 +125,24 @@ def modelLabel(model, height, width, bottom = None, top = None, mi.setScale(scale) mi.setBin('fixed', drawOrder) - if top == None or bottom == None: + if geomRect == None: + geomRect = (1, 1) + + if len(geomRect) == 2: # If we got only two parameters, it's height and width. - label = GuiLabel.makeModelLabel(topnode, height * scale, width * scale) - else: + label = GuiLabel.makeModelLabel(topnode, + geomRect[0] * scale, + geomRect[1] * scale) + elif len(geomRect) == 4: # If we got four, they're left, right, bottom, top. - label = GuiLabel.makeModelLabel(topnode, height * scale, width * scale, - bottom * scale, top * scale) + label = GuiLabel.makeModelLabel(topnode, + geomRect[0] * scale, + geomRect[1] * scale, + geomRect[2] * scale, + geomRect[3] * scale) + else: + raise ValueError + label.setDrawOrder(drawOrder) return label diff --git a/direct/src/gui/ListBox.py b/direct/src/gui/ListBox.py index cab2f15d1d..318efc73b1 100644 --- a/direct/src/gui/ListBox.py +++ b/direct/src/gui/ListBox.py @@ -26,14 +26,12 @@ class ListBox(DirectObject): arrowScale = 0.1 self.up = Button.Button(name + '-up', arrow, - left = -1, right = 1, - bottom = 0, top = 0.5, + geomRect = (-1, 1, 0, 0.5), scale = arrowScale, drawOrder = drawOrder) arrow.setR(180) self.down = Button.Button(name + '-down', arrow, - left = -1, right = 1, - bottom = -0.5, top = 0, + geomRect = (-1, 1, -0.5, 0), scale = arrowScale, drawOrder = drawOrder) arrow.removeNode() diff --git a/direct/src/gui/OnscreenPanel.py b/direct/src/gui/OnscreenPanel.py new file mode 100644 index 0000000000..9c814b102d --- /dev/null +++ b/direct/src/gui/OnscreenPanel.py @@ -0,0 +1,216 @@ +from ShowBaseGlobal import * +from GuiGlobals import * +import PandaObject +import Button +import types + +class OnscreenPanel(PandaObject.PandaObject, NodePath): + """OnscreenPanel: + + This class defines the basic interface to a user-interface panel + that pops up within the Panda window, overlaying whatever graphics + we may have already. + + """ + + AllPanels = {} + + def __init__(self, panelName): + self.panelName = panelName + self.panelSetup = 0 + + # initialize our NodePath essence. + NodePath.__init__(self, aspect2d.attachNewNode(panelName)) + + def findPanel(self, uniqueName): + """findPanel(self, 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 OnscreenPanel.AllPanels.has_key(uniqueName): + return OnscreenPanel.AllPanels[uniqueName] + return None + + def cleanupPanel(self, uniqueName): + """cleanupPanel(self, 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 OnscreenPanel.AllPanels.has_key(uniqueName): + OnscreenPanel.AllPanels[uniqueName].cleanup() + del OnscreenPanel.AllPanels[uniqueName] + + def makePanel(self, + rect = (-0.5, 0.5, -0.5, 0.5), + bg = (1, 1, 1, 1), + geom = getDefaultPanel(), + geomRect = (-0.5, 0.5, -0.5, 0.5), + drawOrder = 0, + font = getDefaultFont(), + support3d = 0): + """makePanel() + + Initializes the geometry to render the panel with the + specified parameters. This should only be called once, and + generally in the __init__ function. + + """ + + assert not self.panelSetup + + # Clean up any previously existing panel with the same unique + # name. We don't allow any two panels with the same name to + # coexist. + uniqueName = self.getUniqueName() + self.cleanupPanel(uniqueName) + + # Store this panel in our map of all open panels. + OnscreenPanel.AllPanels[uniqueName] = self + + self.panelSetup = 1 + self.panelDrawOrder = drawOrder + self.panelFont = font + + self.panelButtons = [] + + centerX = (rect[0] + rect[1]) / 2 + centerY = (rect[2] + rect[3]) / 2 + self.setPos(centerX, 0, centerY) + + if isinstance(geom, types.StringType): + # If 'geom' is a string, it's the name of a model to load. + self.panelGeom = loader.loadModelOnce(geom) + self.panelGeom.reparentTo(self) + else: + # Otherwise, it's a model to instance. + self.panelGeom = geom.instanceTo(self) + + # Scale and position the geometry to move it to fill up our + # desired rectangle. + + gCenterX = (geomRect[0] + geomRect[1]) / 2 + gCenterY = (geomRect[2] + geomRect[3]) / 2 + + + self.panelGeom.setPos(-gCenterX, 0, -gCenterY) + self.panelGeom.setScale((rect[1] - rect[0]) / (geomRect[1] - geomRect[0]), 1, + (rect[3] - rect[2]) / (geomRect[3] - geomRect[2])) + + if bg[3] != 1: + self.panelGeom.setTransparency(1) + self.panelGeom.setColor(bg[0], bg[1], bg[2], bg[3]) + + self.setBin('fixed', self.panelDrawOrder) + + if support3d: + # If we're supposed to support 3-d geometry in front of + # the panel, set a few extra attributes to ensure the + # panel geometry fills up the depth buffer as it goes, + # making it safe to put 3-d geometry in front of it. + dw = DepthWriteTransition() + self.panelGeom.setY(10) + self.panelGeom.arc().setTransition(dw, 2) + + # Set up the panel as its own mouse region so mouse clicks on + # the panel don't inadvertently drive the toon around. + self.panelRegion = MouseWatcherRegion(uniqueName, 0, 0, 0, 0) + self.panelRegion.setRelative(self.panelGeom, + geomRect[0], geomRect[1], + geomRect[2], geomRect[3]) + base.mouseWatcher.node().addRegion(self.panelRegion) + + def cleanup(self): + """cleanup(self): + + Removes the panel and frees all the resources associated with + it. This must be called to safely remove the panel from the + screen. + + The return value is true if the panel was cleaned up, or false + if it had already been cleaned up. + + """ + if not self.panelSetup: + return 0 + + if not self.isEmpty(): + self.removeNode() + + for button in self.panelButtons: + button.cleanup() + self.ignore(button.getName() + '-down-rollover') + + base.mouseWatcher.node().removeRegion(self.panelRegion) + self.panelSetup = 0 + return 1 + + def makeButton(self, name, + func = None, + manage = 1, + label = None, + scale = 0.1, + width = None, + drawOrder = None, + font = None, + pos = (0, 0), + geomRect = None): + """makeButton(self, ...) + + Creates a button on the panel. The return value is the button + itself. The position of the button is relative to the panel, + where (0, 0) is the direct center. + + The button will automatically be managed (i.e. made visible) + unless manage is set to 0. + + If func is specified, it is the name of a function that will + be called when the button is clicked. In this case, a hook + will automatically be assigned for the button, and removed + when cleanup() is called. + """ + + if label == None: + label = name + if drawOrder == None: + drawOrder = self.panelDrawOrder + 10 + if font == None: + font = self.panelFont + + buttonName = self.getUniqueName() + '-' + name + + button = Button.Button(buttonName, + label = label, + scale = scale, + width = width, + drawOrder = drawOrder, + font = font, + pos = pos, + geomRect = geomRect) + + self.panelButtons.append(button) + + if manage: + button.manage(self) + + if func != None: + self.accept(buttonName + '-down-rollover', func) + + + def getUniqueName(self): + """getUniqueName(self): + + Returns a name unique to this panel. If no two instances of + the same panel will ever be in existence at once, this can + simply be the panel name. + + """ + + return self.panelName +