diff --git a/direct/src/gui/DirectCheckButton.py b/direct/src/gui/DirectCheckButton.py new file mode 100644 index 0000000000..4a9d2036bb --- /dev/null +++ b/direct/src/gui/DirectCheckButton.py @@ -0,0 +1,147 @@ +from DirectButton import * +from DirectLabel import * + +class DirectCheckButton(DirectButton): + """ + DirectCheckButton(parent) - Create a DirectGuiWidget which responds + to mouse clicks by setting a state of on or off and execute a callback + function (passing that state through) if defined + """ + def __init__(self, parent = aspect2d, **kw): + # Inherits from DirectButton + # A Direct Frame can have: + # - A background texture (pass in path to image, or Texture Card) + # - A midground geometry item (pass in geometry) + # - A foreground text Node (pass in text string or Onscreen Text) + # For a direct button: + # Each button has 4 states (ready, press, rollover, disabled) + # The same image/geom/text can be used for all four states or each + # state can have a different text/geom/image + # State transitions happen automatically based upon mouse interaction + # Responds to click event and calls command if None + + optiondefs = ( + ('indicatorValue', 0, self.setIndicatorValue), + # boxBorder defines the space created around the check box + ('boxBorder', 0, None), + # boxPlacement maps left, above, right, below + ('boxPlacement', 'left', None), + ) + # Merge keyword options with default options + self.defineoptions(kw, optiondefs) + # Initialize superclasses + DirectButton.__init__(self, parent) + + self.indicator = self.createcomponent("indicator", (), None, + DirectLabel, (self,), + numStates = 2, + state = 'disabled', + text = ('X' , 'X'), + relief = 'sunken', + ) + + # Call option initialization functions + self.initialiseoptions(DirectCheckButton) + # After initialization with X giving it the correct size, put back space + self.indicator['text'] = (' ', '*') + self.indicator['text_pos'] = (0,-.17) + + + # Override the resetFrameSize of DirectGuiWidget inorder to provide space for label + def resetFrameSize(self): + self.setFrameSize(fClearFrame = 1) + + def setFrameSize(self, fClearFrame = 0): + + if self['frameSize']: + # Use user specified bounds + self.bounds = self['frameSize'] + else: + # Use ready state to compute bounds + frameType = self.frameStyle[0].getType() + if fClearFrame and (frameType != PGFrameStyle.TNone): + self.frameStyle[0].setType(PGFrameStyle.TNone) + self.guiItem.setFrameStyle(0, self.frameStyle[0]) + # To force an update of the button + self.guiItem.getStateDef(0) + # Clear out frame before computing bounds + self.getBounds() + # Restore frame style if necessary + if (frameType != PGFrameStyle.TNone): + self.frameStyle[0].setType(frameType) + self.guiItem.setFrameStyle(0, self.frameStyle[0]) + + # Ok, they didn't set specific bounds, + # let's add room for the label indicator + # get the difference in height + + diff = self.indicator.getHeight() + (2*self['boxBorder']) - (self.bounds[3] - self.bounds[2]) + # If background is smaller then indicator, enlarge background + if diff > 0: + if self['boxPlacement'] == 'left': #left + self.bounds[0] += -(self.indicator.getWidth() + (2*self['boxBorder'])) + self.bounds[3] += diff/2 + self.bounds[2] -= diff/2 + elif self['boxPlacement'] == 'below': #below + self.bounds[2] += -(self.indicator.getHeight() + (2*self['boxBorder'])) + elif self['boxPlacement'] == 'right': #right + self.bounds[1] += self.indicator.getWidth() + (2*self['boxBorder']) + self.bounds[3] += diff/2 + self.bounds[2] -= diff/2 + else: #above + self.bounds[3] += self.indicator.getHeight() + (2*self['boxBorder']) + + # Else make space on correct side for indicator + else: + if self['boxPlacement'] == 'left': #left + self.bounds[0] += -(self.indicator.getWidth() + (2*self['boxBorder'])) + elif self['boxPlacement'] == 'below': #below + self.bounds[2] += -(self.indicator.getHeight() + (2*self['boxBorder'])) + elif self['boxPlacement'] == 'right': #right + self.bounds[1] += self.indicator.getWidth() + (2*self['boxBorder']) + else: #above + self.bounds[3] += self.indicator.getHeight() + (2*self['boxBorder']) + + # Set frame to new dimensions + self.guiItem.setFrame(self.bounds[0], self.bounds[1], + self.bounds[2], self.bounds[3]) #3 is top border!! + + # If they didn't specify a position, put it in the center of new area + if not self.indicator['pos']: + bbounds = self.bounds + lbounds = self.indicator.bounds + newpos = [0,0,0] + + if self['boxPlacement'] == 'left': #left + newpos[0] += bbounds[0]-lbounds[0] + self['boxBorder'] + dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder'] + newpos[2] += bbounds[3]-lbounds[3] + self['boxBorder'] - dropValue + elif self['boxPlacement'] == 'right': #right + newpos[0] += bbounds[1]-lbounds[1] - self['boxBorder'] + dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder'] + newpos[2] += bbounds[3]-lbounds[3] + self['boxBorder'] - dropValue + elif self['boxPlacement'] == 'above': #above + newpos[2] += bbounds[3]-lbounds[3] - self['boxBorder'] + else: #below + newpos[2] += bbounds[2]-lbounds[2] + self['boxBorder'] + + self.indicator.setPos(newpos[0],newpos[1],newpos[2]) + + + def commandFunc(self, event): + self['indicatorValue'] = 1 - self['indicatorValue'] + + if self['command']: + # Pass any extra args to command + apply(self['command'], [self['indicatorValue']] + self['extraArgs']) + + def setIndicatorValue(self): + self.component('indicator').guiItem.setState(self['indicatorValue']) + + + + + + + + diff --git a/direct/src/gui/DirectFrame.py b/direct/src/gui/DirectFrame.py index b0366ad8eb..ce4023b857 100644 --- a/direct/src/gui/DirectFrame.py +++ b/direct/src/gui/DirectFrame.py @@ -23,6 +23,7 @@ class DirectFrame(DirectGuiWidget): ('geom', None, self.setGeom), # A foreground text node ('text', None, self.setText), + ('textMayChange', 1, None), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs, @@ -71,7 +72,8 @@ class DirectFrame(DirectGuiWidget): (), parent = self.stateNodePath[i], text = text, scale = 1, sort = TEXT_SORT_INDEX, - mayChange = 1) + mayChange = self['textMayChange'], + ) def setGeom(self): # Determine argument type diff --git a/direct/src/gui/DirectGui.py b/direct/src/gui/DirectGui.py index 848fedc8b5..cf7b61af13 100644 --- a/direct/src/gui/DirectGui.py +++ b/direct/src/gui/DirectGui.py @@ -14,3 +14,4 @@ from DirectLabel import * from DirectScrolledList import * from DirectDialog import * from DirectWaitBar import * +from DirectCheckButton import * diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 30508896b2..63d418887b 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -866,13 +866,14 @@ class DirectGuiWidget(DirectGuiBase, NodePath): self.guiItem.setFrame(self.bounds[0], self.bounds[1], self.bounds[2], self.bounds[3]) + def getBounds(self, state = 0): self.stateNodePath[state].calcTightBounds(self.ll, self.ur) # Scale bounds to give a pad around graphics - self.bounds = (self.ll[0] - self['pad'][0], + self.bounds = [self.ll[0] - self['pad'][0], self.ur[0] + self['pad'][0], self.ll[2] - self['pad'][1], - self.ur[2] + self['pad'][1]) + self.ur[2] + self['pad'][1]] return self.bounds def getWidth(self): diff --git a/direct/src/gui/DirectScrolledList.py b/direct/src/gui/DirectScrolledList.py index 022da2d7ed..03930b1d36 100644 --- a/direct/src/gui/DirectScrolledList.py +++ b/direct/src/gui/DirectScrolledList.py @@ -103,6 +103,11 @@ class DirectScrolledList(DirectFrame): item = self["items"][i] item.show() item.setPos(0,0, - (i - self.index) * self.maxHeight) + + + if self['command']: + # Pass any extra args to command + apply(self['command'], self['extraArgs']) return ret def __scrollByTask(self, task): @@ -123,6 +128,7 @@ class DirectScrolledList(DirectFrame): task.delta = 1 self.scrollBy(task.delta) taskMgr.add(task, self.taskName("scroll")) + def __decButtonDown(self, event): task = Task.Task(self.__scrollByTask) @@ -135,13 +141,14 @@ class DirectScrolledList(DirectFrame): def __buttonUp(self, event): taskMgr.remove(self.taskName("scroll")) - def addItem(self, item): + def addItem(self, item, refresh=1): """ Add this string and extraArg to the list """ self['items'].append(item) item.reparentTo(self.itemFrame) - self.refresh() + if refresh: + self.refresh() def removeItem(self, item):