diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py index 818cc3f896..fe417cfd9f 100644 --- a/direct/src/gui/DirectOptionMenu.py +++ b/direct/src/gui/DirectOptionMenu.py @@ -7,11 +7,13 @@ in-depth explanation and an example of how to use this class. __all__ = ['DirectOptionMenu'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from . import DirectGuiGlobals as DGG from .DirectButton import * from .DirectLabel import * from .DirectFrame import * + class DirectOptionMenu(DirectButton): """ DirectOptionMenu(parent) - Create a DirectButton which pops up a @@ -72,6 +74,10 @@ class DirectOptionMenu(DirectButton): self.popupMenu = None self.selectedIndex = None self.highlightedIndex = None + if 'item_text_scale' in kw: + self._prevItemTextScale = kw['item_text_scale'] + else: + self._prevItemTextScale = (1,1) # A big screen encompassing frame to catch the cancel clicks self.cancelFrame = self.createcomponent( 'cancelframe', (), None, @@ -218,27 +224,27 @@ class DirectOptionMenu(DirectButton): self.popupMenu.setZ( self, self.minZ + (self.selectedIndex + 1)*self.maxHeight) # Make sure the whole popup menu is visible - pos = self.popupMenu.getPos(render2d) - scale = self.popupMenu.getScale(render2d) + pos = self.popupMenu.getPos(ShowBaseGlobal.render2d) + scale = self.popupMenu.getScale(ShowBaseGlobal.render2d) # How are we doing relative to the right side of the screen maxX = pos[0] + fb[1] * scale[0] if maxX > 1.0: # Need to move menu to the left - self.popupMenu.setX(render2d, pos[0] + (1.0 - maxX)) + self.popupMenu.setX(ShowBaseGlobal.render2d, pos[0] + (1.0 - maxX)) # How about up and down? minZ = pos[2] + fb[2] * scale[2] maxZ = pos[2] + fb[3] * scale[2] if minZ < -1.0: # Menu too low, move it up - self.popupMenu.setZ(render2d, pos[2] + (-1.0 - minZ)) + self.popupMenu.setZ(ShowBaseGlobal.render2d, pos[2] + (-1.0 - minZ)) elif maxZ > 1.0: # Menu too high, move it down - self.popupMenu.setZ(render2d, pos[2] + (1.0 - maxZ)) + self.popupMenu.setZ(ShowBaseGlobal.render2d, pos[2] + (1.0 - maxZ)) # Also display cancel frame to catch clicks outside of the popup self.cancelFrame.show() # Position and scale cancel frame to fill entire window - self.cancelFrame.setPos(render2d, 0, 0, 0) - self.cancelFrame.setScale(render2d, 1, 1, 1) + self.cancelFrame.setPos(ShowBaseGlobal.render2d, 0, 0, 0) + self.cancelFrame.setScale(ShowBaseGlobal.render2d, 1, 1, 1) def hidePopupMenu(self, event = None): """ Put away popup and cancel frame """ @@ -247,6 +253,7 @@ class DirectOptionMenu(DirectButton): def _highlightItem(self, item, index): """ Set frame color of highlighted item, record index """ + self._prevItemTextScale = item['text_scale'] item['frameColor'] = self['highlightColor'] item['frameSize'] = (self['highlightScale'][0]*self.minX, self['highlightScale'][0]*self.maxX, self['highlightScale'][1]*self.minZ, self['highlightScale'][1]*self.maxZ) item['text_scale'] = self['highlightScale'] @@ -256,7 +263,7 @@ class DirectOptionMenu(DirectButton): """ Clear frame color, clear highlightedIndex """ item['frameColor'] = frameColor item['frameSize'] = (self.minX, self.maxX, self.minZ, self.maxZ) - item['text_scale'] = (1,1) + item['text_scale'] = self._prevItemTextScale self.highlightedIndex = None def selectHighlightedIndex(self, event = None): diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index ea4894909f..6c514647af 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -543,6 +543,8 @@ class ShowBase(DirectObject.DirectObject): del ShowBaseGlobal.base self.aspect2d.node().removeAllChildren() + self.render2d.node().removeAllChildren() + self.aspect2d.reparent_to(self.render2d) # [gjeon] restore sticky key settings if self.config.GetBool('disable-sticky-keys', 0): @@ -1107,8 +1109,12 @@ class ShowBase(DirectObject.DirectObject): 2-d objects and gui elements that are superimposed over the 3-d geometry in the window. """ + # We've already created aspect2d in ShowBaseGlobal, for the + # benefit of creating DirectGui elements before ShowBase. + from . import ShowBaseGlobal + ## This is the root of the 2-D scene graph. - self.render2d = NodePath('render2d') + self.render2d = ShowBaseGlobal.render2d # Set up some overrides to turn off certain properties which # we probably won't need for 2-d objects. @@ -1139,7 +1145,6 @@ class ShowBase(DirectObject.DirectObject): ## aspect2d, which scales things back to the right aspect ## ratio along the X axis (Z is still from -1 to 1) self.aspect2d = ShowBaseGlobal.aspect2d - self.aspect2d.reparentTo(self.render2d) aspectRatio = self.getAspectRatio() self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index 4808648735..0fdbf0104e 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -26,7 +26,8 @@ cvMgr = ConfigVariableManager.getGlobalPtr() pandaSystem = PandaSystem.getGlobalPtr() # This is defined here so GUI elements can be instantiated before ShowBase. -aspect2d = NodePath(PGTop("aspect2d")) +render2d = NodePath("render2d") +aspect2d = render2d.attachNewNode(PGTop("aspect2d")) hidden = NodePath("hidden") # Set direct notify categories now that we have config diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index d4a70d6054..800d4ee1f6 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -1343,6 +1343,8 @@ release_all_shader_buffers() { ++bci) { BufferContext *bc = (BufferContext *)(*bci); + ((ShaderBuffer *)bc->_object)->clear_prepared(this); + bc->_object = nullptr; _released_shader_buffers.push_back(bc); } diff --git a/tests/gui/test_DirectOptionMenu.py b/tests/gui/test_DirectOptionMenu.py new file mode 100644 index 0000000000..f83ada1352 --- /dev/null +++ b/tests/gui/test_DirectOptionMenu.py @@ -0,0 +1,73 @@ +from direct.gui.DirectOptionMenu import DirectOptionMenu +import pytest + + +def test_menu_destroy(): + menu = DirectOptionMenu(items=["item1", "item2"]) + menu.destroy() + + +def test_showPopupMenu(): + menu = DirectOptionMenu() + + # Showing an option menu without items will raise an exception + with pytest.raises(Exception): + menu.showPopupMenu() + + menu["items"] = ["item1", "item2"] + menu.showPopupMenu() + assert not menu.popupMenu.isHidden() + assert not menu.cancelFrame.isHidden() + + menu.hidePopupMenu() + assert menu.popupMenu.isHidden() + assert menu.cancelFrame.isHidden() + + +def test_index(): + menu = DirectOptionMenu(items=["item1", "item2"]) + assert menu.index("item1") == 0 + assert menu.index("item2") == 1 + + +def test_set_get(): + menu = DirectOptionMenu(items=["item1", "item2"]) + menu.set(1, False) + assert menu.selectedIndex == 1 + assert menu.get() == "item2" + assert menu["text"] == "item2" + + +def test_initialitem(): + # initialitem by string + menuByStr = DirectOptionMenu(items=["item1", "item2"], initialitem="item2") + assert menuByStr.get() == "item2" + assert menuByStr["text"] == "item2" + + # initialitem by Index + menuByIdx = DirectOptionMenu(items=["item1", "item2"], initialitem=1) + assert menuByIdx.get() == "item2" + assert menuByIdx["text"] == "item2" + + +def test_item_text_scale(): + highlightScale = (2, 2) + unhighlightScale = (0.5, 0.5) + menu = DirectOptionMenu( + items=["item1", "item2"], + item_text_scale=unhighlightScale, + highlightScale=highlightScale) + + # initial scale + item = menu.component("item0") + + item_text_scale = 0.8 + assert item["text_scale"] == unhighlightScale + + # highlight scale + menu._highlightItem(item, 0) + assert item["text_scale"] == highlightScale + + # back to initial scale + menu._unhighlightItem(item, item["frameColor"]) + assert item["text_scale"] == unhighlightScale