class Main(ShowBase, ConfigFSM): def __init__(self): self.loadingText = None self.saveFolderPath = './saves/' self.appName = appName ShowBase.__init__(self) self.textEngine = TextEngine(base.messenger.send) self.mapData = MapsModel() self.gameData = GameModel(self.saveFolderPath) ConfigFSM.__init__(self) self.disableMouse() self.callLoadingScreen() self.request("StartMenu") # self.request("RPGField") # self.request("Cutscene") def callLoadingScreen(self): self.loadingText = DirectFrame( frameSize=(base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), frameColor=(0, 0, 0, 1.0)) txt = OnscreenText("Loading...", 1, fg=(1, 1, 1, 1), pos=( 0, 0), align=TextNode.ACenter, scale=.07, mayChange=1) txt.reparentTo(self.loadingText) base.graphicsEngine.renderFrame() base.graphicsEngine.renderFrame() def removeLoadingScreen(self): if self.loadingText: self.loadingText.removeNode() self.loadingText = None
class Main(ShowBase, ConfigFSM): def __init__(self, appName): self.appName = appName self.loadingText = None ShowBase.__init__(self) ConfigFSM.__init__(self) self.request('StartMenu') def callLoadingScreen(self): self.loadingText = DirectFrame( frameSize=(base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), frameColor=(0, 0, 0, 1.0)) txt = OnscreenText("Loading...", 1, fg=(1, 1, 1, 1), pos=( 0, 0), align=TextNode.ACenter, scale=.07, mayChange=1) txt.reparentTo(self.loadingText) base.graphicsEngine.renderFrame() base.graphicsEngine.renderFrame() def removeLoadingScreen(self): if self.loadingText: self.loadingText.removeNode() self.loadingText = None
class GUIDialog: def __init__(self): self.mainFrame = DirectFrame(frameColor=(0, 0, 0, 0.5), borderWidth=(10, 10), frameSize=(-2, 2, -1, -0.45)) self.textIndex = 0 self.isCalling = False self.frameText = None self.frameActor = None self.frameChoices = None self.textList = [] self.hide() def setLabel(self, labelName): base.textEngine.setTextLabel(labelName) self.textList = base.textEngine.getDialogue() self.processLine(self.textList[self.textIndex]) def processLine(self, line): if line['type'] == 'text': self.dialogueLine(line) elif line['type'] == 'menu': self.choiceLine(line) def choiceLine(self, lineObj): self.setFrameText(lineObj['text']) self.setFrameChoices(lineObj) self.show() def dialogueLine(self, lineObj): self.setFrameActor(lineObj['actor']) self.setFrameText(lineObj['line']) self.show() def showMessage(self, txt): self.setFrameText(txt) self.show() def setFrameChoices(self, lineObj): base.resetButtons() if self.frameChoices: self.frameChoices.quit() optionsList = lineObj['options'] self.frameChoices = GUIDialogOptions(optionsList) self.frameChoices.reparentTo(self.mainFrame) taskMgr.add(self.frameChoices.readKeys, "readKeysTask") for option in optionsList: index = optionsList.index(option) base.accept("TextChoice-{}".format(index), self.chooseOption, [lineObj, index]) def destroyFrameChoices(self, optionsList): self.frameChoices.quit() self.frameChoices = None taskMgr.remove("readKeysTask") for option in optionsList: index = optionsList.index(option) base.ignore("TextChoice-{}".format(index)) def chooseOption(self, lineObj, index): base.textEngine.chooseFromMenu(lineObj, index) self.destroyFrameChoices(lineObj['options']) self.textList = base.textEngine.getDialogue() self.textIndex = 0 self.processLine(self.textList[self.textIndex]) def setFrameActor(self, text): if self.frameActor: self.frameActor.detach_node() self.frameActor = OnscreenText(text=text, pos=(-1.2, -0.55), scale=0.07, wordwrap=35, mayChange=False, align=TextNode.ALeft, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), shadowOffset=(0.05, 0.05)) self.frameActor.reparentTo(self.mainFrame) def setFrameText(self, text): if self.frameText: self.frameText.detach_node() self.frameText = OnscreenText(text=text, pos=(-1.2, -0.65), scale=0.07, wordwrap=35, mayChange=False, align=TextNode.ALeft, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), shadowOffset=(0.05, 0.05)) self.frameText.reparentTo(self.mainFrame) def show(self): self.mainFrame.show() def hide(self): self.mainFrame.hide() if self.frameText: self.frameText.detach_node() if self.frameActor: self.frameActor.detach_node() self.textIndex = 0 self.frameText = None def next(self): if not self.isCalling: self.isCalling = True taskMgr.doMethodLater(.1, self.nextText, 'Next Text') def nextText(self, task): if self.textIndex < len(self.textList) - 1: self.textIndex += 1 self.processLine(self.textList[self.textIndex]) else: self.hide() self.isCalling = False return task.done @property def isShowing(self): return self.frameText != None @property def isChoices(self): return self.frameChoices != None def reparentTo(self, target): self.mainFrame.reparentTo(target) def removeNode(self): self.mainFrame.removeNode()
class FileDialog(DirectObject): def __init__(self, title, initial_status, callback): self.frmLeft = -.7 self.frmBottom = -.12 self.frmRight = .7 self.frmTop = .12 self.frmWidth = self.frmRight - self.frmLeft self.frmHeight = self.frmTop - self.frmBottom self.title = title self.initial_status = initial_status self.callback = callback self.result = None self.done = 0 def activate(self): # create main dialog frame # Note: frame position references the CENTER of the frame self.frm = DirectFrame(pos=(0, 0, 0), frameSize=(self.frmLeft, self.frmRight, self.frmBottom, self.frmTop), relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameColor=(0.6, 0.6, 0.6, 1.0)) self.frm.reparentTo(base.aspect2d) # Note: wonderfully enough, label position on the other hand # references the CENTER of the LEFT border of the label! titleLabel = DirectLabel(text=self.title, scale=0.04, frameColor=(0.5, 0.5, 0.5, 1), relief=DGG.RIDGE, borderWidth=(0.01, 0.01), text_align=TextNode.ALeft) titleLabel.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .045) titleLabel.reparentTo(self.frm) sc = .04 self.dir_edit_field = DirectEntry( text='', # prompt text scale=sc, frameColor=(0.65, 0.65, 0.65, 1), command=self.setText, width=32, numLines=1, focus=1, focusInCommand=self.efFocusIn, ) self.dir_edit_field.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .13) self.dir_edit_field.reparentTo(self.frm) self.statusLabel = DirectLabel( text=self.initial_status, scale=0.04, frameColor=(0.6, 0.6, 0.6, 1), # relief = DGG.RIDGE, # borderWidth = (0.01, 0.01), text_align=TextNode.ALeft) self.statusLabel.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .2) self.statusLabel.reparentTo(self.frm) # call this if you need to run a form before your mainloop starts steppping the taskMgr def run(self): while self.done != 1: taskMgr.step() taskMgr.step( ) # once more to make the last output from the dialog visible # while we load a zone self.frm.removeNode() def end(self): self.frm.removeNode() # callback function to set text: called by the Panda3d taskMgr when the user # has entered something into the DirectEntry widget def setText(self, textEntered): print 'dir_edit_field::setText:', textEntered self.done = self.callback(textEntered) if self.done != 1: self.dir_edit_field['focus'] = 1 #clear the text def efFocusIn(self): print 'focus in' self.dir_edit_field.enterText('') def setStatus(self, status): self.statusLabel['text'] = status
class FileDialog(DirectObject): def __init__(self, title, initial_status, callback): self.frmLeft = -.7 self.frmBottom = -.12 self.frmRight = .7 self.frmTop = .12 self.frmWidth = self.frmRight - self.frmLeft self.frmHeight = self.frmTop - self.frmBottom self.title = title self.initial_status = initial_status self.callback = callback self.result = None self.done = 0 def activate(self): # create main dialog frame # Note: frame position references the CENTER of the frame self.frm = DirectFrame( pos = (0,0,0), frameSize = (self.frmLeft, self.frmRight,self.frmBottom, self.frmTop), relief = DGG.RIDGE, borderWidth = (0.01, 0.01), frameColor = (0.6, 0.6, 0.6, 1.0)) self.frm.reparentTo(base.aspect2d) # Note: wonderfully enough, label position on the other hand # references the CENTER of the LEFT border of the label! titleLabel = DirectLabel(text = self.title, scale = 0.04, frameColor = (0.5, 0.5, 0.5, 1), relief = DGG.RIDGE, borderWidth = (0.01, 0.01), text_align = TextNode.ALeft) titleLabel.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.045) titleLabel.reparentTo(self.frm) sc = .04 self.dir_edit_field = DirectEntry( text = '', # prompt text scale = sc, frameColor = (0.65, 0.65, 0.65, 1), command=self.setText, width = 32, numLines = 1, focus = 1, focusInCommand=self.efFocusIn, ) self.dir_edit_field.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.13) self.dir_edit_field.reparentTo(self.frm) self.statusLabel = DirectLabel(text = self.initial_status, scale = 0.04, frameColor = (0.6, 0.6, 0.6, 1), # relief = DGG.RIDGE, # borderWidth = (0.01, 0.01), text_align = TextNode.ALeft) self.statusLabel.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.2) self.statusLabel.reparentTo(self.frm) # call this if you need to run a form before your mainloop starts steppping the taskMgr def run(self): while self.done != 1: taskMgr.step() taskMgr.step() # once more to make the last output from the dialog visible # while we load a zone self.frm.removeNode() def end(self): self.frm.removeNode() # callback function to set text: called by the Panda3d taskMgr when the user # has entered something into the DirectEntry widget def setText(self, textEntered): print 'dir_edit_field::setText:', textEntered self.done = self.callback(textEntered) if self.done != 1: self.dir_edit_field['focus'] = 1 #clear the text def efFocusIn(self): print 'focus in' self.dir_edit_field.enterText('') def setStatus(self, status): self.statusLabel['text'] = status
class IMenu(metaclass=abc.ABCMeta): def __init__(self, frame=None): self.hasLoaded = False self.menuVerticalChoicesList = [ {"event": "Example-Event", "text": "Override this"}] self.menuVerticalChoice = [0] self.menuHorizontalChoices = [ {"event": "Example-Event", "text": "Override this"}] self.menuHorizontalChoice = [0] self.menuVerticalButtons = [] self.menuHorizontalButtons = [] self.isButtonUp = True self.init_frame(frame) def init_frame(self, frame): if frame: self.frameMain = frame else: self.frameMain = DirectFrame( frameSize=(base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), frameColor=(0, 0, 0, 0.0)) def show(self): self.frameMain.show() def hide(self): self.frameMain.hide() def quit(self): self.frameMain.removeNode() def readKeys(self, task): keysPressed = sum(base.directionMap.values()) if keysPressed == 0: self.isButtonUp = True if base.commandMap["confirm"]: self.menuVerticalEvent() base.messenger.send("playConfirm") elif base.commandMap["cancel"]: self.cancelCommand() base.messenger.send("playCancel") base.resetButtons() return task.cont if not self.isButtonUp: return task.cont if base.directionMap["up"]: self.navigateChoice(-1, self.menuVerticalChoice, self.menuVerticalChoicesList) self.isButtonUp = False elif base.directionMap["down"]: self.navigateChoice(1, self.menuVerticalChoice, self.menuVerticalChoicesList) self.isButtonUp = False elif base.directionMap["left"]: self.navigateChoice(-1, self.menuHorizontalChoice, self.menuHorizontalChoices) self.isButtonUp = False self.menuHorizontalEvent() elif base.directionMap["right"]: self.navigateChoice(1, self.menuHorizontalChoice, self.menuHorizontalChoices) self.isButtonUp = False self.menuHorizontalEvent() base.resetButtons() return task.cont def navigateChoice(self, value, choice, choiceList): choice[0] += value if choice[0] < 0: choice[0] = len(choiceList) - 1 elif choice[0] > len(choiceList) - 1: choice[0] = 0 self.updateCheckbuttons() def updateCheckbuttons(self): for btn in self.menuVerticalButtons: index = self.menuVerticalButtons.index(btn) isPressed = index == self.menuVerticalChoice[0] btn["indicatorValue"] = isPressed btn.setIndicatorValue() for btn in self.menuHorizontalButtons: index = self.menuHorizontalButtons.index(btn) isPressed = index == self.menuHorizontalChoice[0] btn["indicatorValue"] = isPressed btn.setIndicatorValue() def menuVerticalEvent(self): base.messenger.send( self.menuVerticalChoicesList[self.menuVerticalChoice[0]]["event"]) def menuHorizontalEvent(self): base.messenger.send( self.menuHorizontalChoices[self.menuHorizontalChoice[0]]["event"]) def createVerticalButtons(self): for btn in self.menuVerticalChoicesList: index = self.menuVerticalChoicesList.index(btn) self.createButton( btn['text'], index, [btn['event']]) @abc.abstractmethod def createButton(self, text, index, eventArgs): raise NotImplementedError('subclass must define this method') @abc.abstractmethod def cancelCommand(self): raise NotImplementedError('subclass must define this method')
class GuiButton2: def __init__(self, hugpos, offset, aspect, plane, name): self.enabled = True self.name = name self.frame = DirectFrame( relief = DGG.FLAT , frameColor = (0, 0, 0, 0) , scale = 1 , frameSize = (-0.3, 0.3, -0.3, 0.3) ) #32/600=0.05333 self.frame.setScale(0.0533) self.imageObject = OnscreenImage(image = name+".png", pos = (0, 0, 0)) self.imageObject.setTransparency(TransparencyAttrib.MAlpha) self.imageObject.setAlphaScale(1) self.imageObject.reparentTo(self.frame) self.hugpos = hugpos self.offset = offset self.redraw(aspect) def redraw(self, aspect, flag="wide"): if self.hugpos == "top": p = base.a2dTopLeft.getPos()#@UndefinedVariable p.setX(p.getX() + self.offset.getX() + 0.05) p.setZ(p.getZ() + self.offset.getZ() - GUI_TOP_OFFSET - 0.05) elif self.hugpos == "bottom": p = base.a2dBottomLeft.getPos()#@UndefinedVariable p.setX(p.getX() + self.offset.getX() + 0.05) p.setZ(p.getZ() + self.offset.getZ() + GUI_BOTTOM_OFFSET - 0.05) elif self.hugpos == "right": p = base.a2dBottomRight.getPos()#@UndefinedVariable p.setX(p.getX() + self.offset.getX() + 0.05) p.setZ(p.getZ() + self.offset.getZ() + GUI_BOTTOM_OFFSET - 0.05) self.frame.setPos(p) if flag == "wide": posx, posy = self.frame.getTightBounds() self.pos_min_x = posx.getX() / aspect self.pos_min_y = posx.getZ() self.pos_max_x = posy.getX() / aspect self.pos_max_y = posy.getZ() elif flag == "tall": posx, posy = self.frame.getTightBounds() self.pos_min_x = posx.getX() self.pos_min_y = posx.getZ() / aspect self.pos_max_x = posy.getX() self.pos_max_y = posy.getZ() / aspect def turnOn(self): self.imageObject.setImage(self.name+"_on.png")#@UndefinedVariable self.imageObject.setTransparency(TransparencyAttrib.MAlpha) def turnOff(self): self.imageObject.setImage(self.name+".png")#@UndefinedVariable self.imageObject.setTransparency(TransparencyAttrib.MAlpha) def enable(self): self.imageObject.setImage(self.name+".png")#@UndefinedVariable self.imageObject.setTransparency(TransparencyAttrib.MAlpha) self.enabled = True def disable(self): self.imageObject.setImage("empty.png")#@UndefinedVariable self.imageObject.setTransparency(TransparencyAttrib.MAlpha) self.enabled = False def removeNode(self): self.frame.removeNode() def setAbility(self, ability): self.name = ability self.imageObject.setImage(self.name+".png") self.imageObject.setTransparency(TransparencyAttrib.MAlpha) self.imageObject.setAlphaScale(1)
class DirectWindow( DirectFrame ): def __init__( self , pos = ( -.5, .5) , title = 'Title' , bgColor = (.5,.5,.5,1) , buttonColor = (1,1,1,1) #( .6, .6, .6, 1 ) #, minSize = ( .5, .5 ) #, maxSize = ( 1, 1 ) , minWindowSize = (0,0) , maxWindowSize = (10000,10000) , virtualSize = (1,1) , windowBorderTextureFiles = [ DEFAULT_TITLE_TEXTURE_LEFT , DEFAULT_TITLE_TEXTURE_CENTER , DEFAULT_TITLE_TEXTURE_RIGHT , DEFAULT_RESIZE_GEOM ] , windowBorderGeomFiles = [ DEFAULT_TITLE_GEOM_RIGHT ] , windowColors = [ ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) ] , borderSize = 0.01 , dragbarSize = 0.05 , parent=None): self.windowPos = pos self.minWindowSize = minWindowSize self.maxWindowSize = maxWindowSize self.virtualSize = virtualSize self.borderSize = borderSize self.dragbarSize = dragbarSize if parent is None: parent=aspect2d self.parent=parent self.previousSize = (10,10) self.collapsed = False # maybe we should check if aspect2d doesnt already contain the aspect2dMouseNode self.mouseNode = self.parent.attachNewNode( 'aspect2dMouseNode', sort = 999999 ) taskMgr.add( self.mouseNodeTask, 'mouseNodeTask' ) windowBorderTextures = list() for windowBorder in windowBorderTextureFiles: if windowBorder is not None: mdlFile = loader.loadTexture(windowBorder) windowBorderTextures.append(mdlFile) else: windowBorderTextures.append(None) windowBorderGeoms = list() for windowGeom in windowBorderGeomFiles: if windowGeom is not None: mdlFile = loader.loadModel(windowGeom) mdls = ( mdlFile.find('**/**-default'), mdlFile.find('**/**-click'), mdlFile.find('**/**-rollover'), mdlFile.find('**/**-disabled') ) windowBorderGeoms.append(mdls) else: windowBorderGeoms.append((None,None,None,None,),) # the main window we want to move around self.parentWindow = DirectFrame( parent=self.parent, pos=(self.windowPos[0], 0, self.windowPos[1]), #frameSize=# is defined in resize scale=(1, 1, -1), frameColor=bgColor, borderWidth=(0, 0), relief=DGG.FLAT, sortOrder=1, ) # header of the window (drag&drop with it) # the title part of the window, drag around to move the window self.headerParent = DirectButton( parent=self.parentWindow, pos=(0, 0, 0), #frameSize=# is defined in resize scale=(1, 1, self.dragbarSize), frameColor=(1, 1, 1, 1), borderWidth=(0, 0), relief=DGG.FLAT, ) self.headerParent.bind(DGG.B1PRESS,self.startWindowDrag) # images in the headerParent self.headerCenter = DirectFrame( parent=self.headerParent, pos=(0, 0, 1), #frameSize=# is defined in resize scale=(1,1,-1), frameColor=windowColors[1], frameTexture=windowBorderTextures[1], borderWidth=(0, 0), relief=DGG.FLAT, ) self.headerLeft = DirectFrame( parent=self.headerParent, pos=(0, 0, 1), frameSize=(0, self.dragbarSize, 0, 1), scale=(1,1,-1), frameColor=windowColors[0], frameTexture=windowBorderTextures[0], borderWidth=(0, 0), relief=DGG.FLAT, ) # collapse button self.headerRight = DirectButton( parent=self.headerParent, #pos=# is defined in resize frameSize=(0, self.dragbarSize, 0, 1), scale=(1,1,-1), frameColor=windowColors[2], #frameTexture=windowBorderTextures[2], borderWidth=(0, 0), relief=DGG.FLAT, command=self.toggleCollapsed, geom=windowBorderGeoms[0], geom_scale=(self.dragbarSize,1,1) ) # the resize button of the window self.resizeButton = DirectButton( parent=self.parentWindow, pos=(1-self.dragbarSize, 0, 1), frameSize=(0, 1, 0, 1), scale=(self.dragbarSize,1,-self.dragbarSize), frameColor=windowColors[3], frameTexture=windowBorderTextures[3], borderWidth=(0, 0), relief=DGG.FLAT, sortOrder=1, ) self.resizeButton.bind(DGG.B1PRESS,self.startResizeDrag) # text in the center of the window text = TextNode('WindowTitleTextNode') text.setText(title) text.setAlign(TextNode.ACenter) text.setTextColor( 0, 0, 0, 1 ) text.setShadow(0.05, 0.05) text.setShadowColor( 1, 1, 1, 1 ) self.textNodePath = self.headerCenter.attachNewNode(text) self.textNodePath.setPos(.5,0,.3) self.textNodePath.setScale(0.8*self.dragbarSize,1,0.8) if Y_INVERTED: scale = (1,1,-1) else: scale = (1,1,1) # the content part of the window, put stuff beneath # contentWindow.getCanvas() to put it into it self.contentWindow = DirectScrolledFrame( parent = self.parentWindow, #pos = # is defined in resize scale = scale, canvasSize = (0,self.virtualSize[0],0,self.virtualSize[1]), frameColor = buttonColor, relief = DGG.RAISED, borderWidth = (0,0), verticalScroll_frameSize = [0,self.dragbarSize,0,1], verticalScroll_frameTexture = loader.loadTexture( 'rightBorder.png' ), verticalScroll_incButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), verticalScroll_decButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), verticalScroll_thumb_frameTexture = loader.loadTexture( 'scrollBar.png' ), horizontalScroll_frameSize = [0,1,0,self.dragbarSize], horizontalScroll_frameTexture = loader.loadTexture( 'bottomBorder.png' ), horizontalScroll_incButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), horizontalScroll_decButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), horizontalScroll_thumb_frameTexture = loader.loadTexture( 'scrollBar.png' ), ) # child we attach should be inside the window DirectFrame.__init__( self, parent = self.contentWindow.getCanvas(), pos = (0,0,self.virtualSize[1]), scale = (1,1,1), frameSize = ( 0, self.virtualSize[0]+2*self.borderSize, 0, self.virtualSize[1] ), #frameColor = (0,0,0,1), relief = DGG.RIDGE, borderWidth = (0,0), ) self.initialiseoptions(DirectWindow) # offset then clicking on the resize button from the mouse to the resizebutton # position, required to calculate the position / scaling self.offset = None self.resizeButtonTaskName = "resizeTask-%s" % str(hash(self)) # do sizing of the window to virtualSize #self.resize( self.virtualSize[0]+2*self.borderSize # , self.virtualSize[1]+self.dragbarSize+2*self.borderSize ) self.resize(10,10) # a task that keeps a node at the position of the mouse-cursor def mouseNodeTask(self, task): if WindowManager.hasMouse(): x=WindowManager.getMouseX() y=WindowManager.getMouseY() # the mouse position is read relative to render2d, so set it accordingly self.mouseNode.setPos( render2d, x, 0, y ) return task.cont # dragging functions def startWindowDrag( self, param ): self.parentWindow.wrtReparentTo( self.mouseNode ) self.ignoreAll() self.accept( 'mouse1-up', self.stopWindowDrag ) def stopWindowDrag( self, param=None ): # this could be called even after the window has been destroyed #if self: # this is called 2 times (bug), so make sure it's not already parented to aspect2d if self.parentWindow.getParent() != self.parent: self.parentWindow.wrtReparentTo(self.parent) self.ignoreAll() # resize functions def startResizeDrag(self, param): self.offset = self.resizeButton.getPos(aspect2d) - self.mouseNode.getPos(aspect2d) taskMgr.remove( self.resizeButtonTaskName ) taskMgr.add( self.resizeButtonTask, self.resizeButtonTaskName ) self.accept( 'mouse1-up', self.stopResizeDrag,['x'] ) def resize(self,windowX,windowY): # limit max/min size of the window maxX = min(self.maxWindowSize[0], self.virtualSize[0]+2*self.borderSize) minX = max( self.dragbarSize*3, self.minWindowSize[0]) windowWidth = min( maxX, max( minX, windowX ) ) maxY = min( self.maxWindowSize[1], self.virtualSize[1]+self.dragbarSize+2*self.borderSize ) minY = max( self.dragbarSize*4, self.minWindowSize[1]) windowHeight = min( maxY, max( minY, windowY ) ) if self.collapsed: windowHeight = 2*self.dragbarSize+2*self.borderSize windowWidth = windowWidth self.contentWindow.hide() # store changed window width only self.previousSize = windowWidth, self.previousSize[1] else: self.contentWindow.show() self.previousSize = windowWidth, windowHeight # set the window size self.headerParent['frameSize'] = (0, windowWidth, 0, 1) self.headerCenter['frameSize'] = (0, windowWidth, 0, 1) self.parentWindow['frameSize'] = (0, windowWidth, 0, windowHeight) self.contentWindow['frameSize'] = (0, windowWidth-self.borderSize*2, 0, windowHeight-self.dragbarSize-2*self.borderSize) self.contentWindow.setPos(self.borderSize,0,windowHeight-self.borderSize) self.headerRight.setPos(windowWidth-self.dragbarSize, 0, 1) self.textNodePath.setPos(windowWidth/2.,0,.3) self.resizeButton.setPos(windowWidth-self.dragbarSize, 0, windowHeight) def resizeButtonTask(self, task=None): mPos = self.mouseNode.getPos(self.parentWindow) # max height, the smaller of (given maxWindowSize and real size of content and borders windowX = mPos.getX() + self.offset.getX() + self.dragbarSize windowY = mPos.getZ() - self.offset.getZ() self.resize(windowX,windowY) return task.cont def stopResizeDrag(self, param): taskMgr.remove( self.resizeButtonTaskName ) self.ignoreAll() # a bugfix for a wrong implementation def detachNode( self ): self.parentWindow.detachNode() #self. = None #DirectFrame.detachNode( self ) def removeNode( self ): self.parentWindow.removeNode() #DirectFrame.removeNode( self ) def toggleCollapsed(self,state=None): if state is None: state=not self.collapsed if state: self.collapse() else: self.uncollapse() def collapse(self): self.collapsed = True self.resize(*self.previousSize) def uncollapse(self): self.collapsed = False self.resize(*self.previousSize)