class OptionsPage(BookPage): notify = directNotify.newCategory("OptionsPage") # Keys are categories and values are x-positions for the text. Categories = OrderedDict() Categories[AboutCategory] = 0.09 Categories[GeneralCategory] = 0.07 Categories[ControlsCategory] = 0.05 Categories[SoundCategory] = 0.0825 Categories[DisplayCategory] = 0.07 Categories[AdvancedDisplayCategory] = 0.05 def __init__(self, book): BookPage.__init__(self, book, "Options") self.currentCategory = None self.tabs = [] def load(self): BookPage.load(self) icons = loader.loadModel('phase_3.5/models/gui/sos_textures.bam') self.icon = icons.find('**/switch') icons.detachNode() def enter(self): BookPage.enter(self) self.tabScaleFrame = DirectFrame(parent=self.book) self.tabScaleFrame.setZ(0.7683) self.tabsFrame = DirectFrame(parent=self.tabScaleFrame) tabWidth = 0.379136800766 spacing = 0.075 totalWidth = 0.0 bookWidth = 2.0 for i in xrange(len(self.Categories.keys())): if i > 0: totalWidth += spacing totalWidth += tabWidth cl = self.Categories.keys()[i] tab = CategoryTab(self, cl.Name, [cl], ((tabWidth + spacing) * i, 0, 0), self.Categories.values()[i]) self.tabs.append(tab) self.tabsFrame.setX(totalWidth / -2.0) self.tabScaleFrame.setScale(min(1.0, 1.0 / (totalWidth / bookWidth))) self.pickCategory(AboutCategory) def closeWindow(self): if self.currentCategory: self.currentCategory.cleanup() self.currentCategory = None def pickCategory(self, cat): if self.currentCategory: self.currentCategory.cleanup() self.currentCategory = None self.currentCategory = cat(self) for tab in self.tabs: if tab['extraArgs'][0] is cat: tab['state'] = DGG.DISABLED else: tab['state'] = DGG.NORMAL #self.currentCategory.show() def exit(self): if self.currentCategory: self.currentCategory.cleanup() self.currentCategory = None for tab in self.tabs: tab.destroy() self.tabsFrame.destroy() del self.tabsFrame self.tabScaleFrame.destroy() del self.tabScaleFrame self.tabs = [] BookPage.exit(self)
class GagSelectionGui(DirectFrame, FSM): InactivityTime = 5.0 AmmoZSelect = -0.15 AmmoZIdle = 0.035 def __init__(self): DirectFrame.__init__(self, parent=aspect2d, pos=(0, 0, 0.93), scale=0.7) FSM.__init__(self, 'GagSelectionGui') self.setTransparency(TransparencyAttrib.MDual) self.tracks = [] self.currentTrack = 0 self.currentGag = None self.fwdShakeIval = None self.revShakeIval = None self.newTrackSound = None self.keyScrollSound = None self.selectSound = None self.selectDenySound = None self.lastActivityTime = 0.0 self.activityTask = None self.midpoint = 0.0 self.ammoFrame = DirectFrame(parent=self, pos=(0, 0, -0.2), image='phase_14/maps/status_bar.png', image_scale=(0.461 * 0.7, 0, 0.098), relief=None) self.ammoFrame.hide() self.ammoTitle = OnscreenText(parent=self.ammoFrame, text='SUPPLY', fg=(0, 0, 0, 0.65), align=TextNode.ALeft, pos=(-0.37 * 0.7, -0.015, 0)) self.ammoText = OnscreenText(parent=self.ammoFrame, text='', fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), align=TextNode.ARight, pos=(0.37 * 0.7, -0.015, 0)) def update(self): plyr = base.localAvatar if not base.localAvatar.hasAttacks(): return gagId = -1 if self.getCurrentOrNextState() == 'Idle': gagId = plyr.getEquippedAttack() elif self.getCurrentOrNextState() == 'Select' and self.currentGag: gagId = self.currentGag.gagId if gagId != -1: self.ammoFrame.showThrough() if plyr.hasAttackId(gagId): self.ammoText.setText( '%i/%i' % (plyr.getAttackAmmo(gagId), plyr.getAttackMaxAmmo(gagId))) else: self.ammoText.setText('') col = GagGlobals.TrackColorByName[GagGlobals.getTrackOfGag(gagId)] self.ammoFrame['image_color'] = (col[0], col[1], col[2], 1.0) else: self.ammoFrame.hide() def enterOff(self): pass def exitOff(self): pass def hide(self): showAmmo = False if not self.ammoFrame.isHidden(): showAmmo = True DirectFrame.hide(self) if showAmmo: self.ammoFrame.showThrough() def enterSelect(self): base.localAvatar.disableGagKeys() self.ammoFrame.setZ(self.AmmoZSelect) self.show() self.update() self.acceptSelectionClick() self.resetTimeout() self.activityTask = taskMgr.add(self.__activityTask, "activityTask") def acceptSelectionClick(self): self.accept('mouse1-up', self.selectCurrentGag) def ignoreSelectionClick(self): self.ignore('mouse1-up') def resetTimeout(self): self.lastActivityTime = globalClock.getFrameTime() def __activityTask(self, task): time = globalClock.getFrameTime() if time - self.lastActivityTime >= self.InactivityTime: self.request('Idle') return task.done return task.cont def exitSelect(self): self.activityTask.remove() self.activityTask = None self.hide() self.ignoreSelectionClick() def enterIdle(self): self.ammoFrame.setZ(self.AmmoZIdle) self.hide() self.update() if base.localAvatar.avatarMovementEnabled: base.localAvatar.enableGagKeys() def exitIdle(self): pass def disable(self): self.disableControls() self.hide() def enable(self): self.enableControls() self.show() def cleanup(self): self.request('Off') self.disableControls() self.newTrackSound = None self.keyScrollSound = None self.selectSound = None self.selectDenySound = None if self.fwdShakeIval: self.fwdShakeIval.finish() self.fwdShakeIval = None if self.revShakeIval: self.revShakeIval.finish() self.revShakeIval = None self.currentTrack = None self.currentGag = None if self.tracks: for track in self.tracks: track.cleanup() self.tracks = None self.destroy() def __accumulateTracks(self): tracks = [] for gagId in base.localAvatar.attacks.keys(): trackId = GagGlobals.getTrackOfGag(gagId, getId=True) if trackId not in tracks: tracks.append(trackId) tracks.sort() return tracks def load(self): tracks = self.__accumulateTracks() for i in xrange(len(tracks)): track = GagTrack(self, tracks[i]) track.load() track.reparentTo(self) track.setX(FRAME_OFFSET * i) self.tracks.append(track) self.midpoint = (len(self.tracks) / 2.0) * -FRAME_OFFSET # Center the gui horizontally self.setX(self.midpoint) self.ammoFrame.setX(-self.midpoint) if base.config.GetBool('gsg-want-hlsounds', False): self.newTrackSound = base.loadSfx( "phase_14/audio/sfx/wpn_hudon.ogg") self.keyScrollSound = base.loadSfx( 'phase_14/audio/sfx/wpn_moveselect.ogg') self.selectSound = base.loadSfx( 'phase_14/audio/sfx/wpn_select.ogg') self.selectDenySound = base.loadSfx( 'phase_14/audio/sfx/wpn_denyselect.ogg') else: self.newTrackSound = base.loadSfx( "phase_3/audio/sfx/GUI_create_toon_back.ogg") self.keyScrollSound = base.loadSfx( 'phase_3/audio/sfx/GUI_rollover.ogg') self.selectSound = base.loadSfx( 'phase_3/audio/sfx/GUI_create_toon_fwd.ogg') self.selectDenySound = base.loadSfx( 'phase_4/audio/sfx/ring_miss.ogg') self.fwdShakeIval = Effects.createXBounce(self, 1, Vec3(self.midpoint, 0, 0.93), 0.05, 0.05) self.revShakeIval = Effects.createXBounce(self, 1, Vec3(self.midpoint, 0, 0.93), 0.05, -0.05) if base.localAvatar.hasAttacks(): self.updateCurrentTrack(0) def enableControls(self): self.accept('wheel_up', self.__handleScrollUp) self.accept('wheel_down', self.__handleScrollDown) for i in xrange(len(self.tracks)): self.accept(str(i + 1), self.__handleTrackChoose, [i]) self.request('Idle') def selectCurrentGag(self): selected = False self.newTrackSound.stop() self.keyScrollSound.stop() if self.currentGag is not None: if base.localAvatar.getEquippedAttack() == self.currentGag.gagId: selected = True elif (not self.currentGag.locked and self.currentGag.hasAmmo()): gagId = self.currentGag.gagId base.localAvatar.needsToSwitchToGag = gagId if base.localAvatar.gagsTimedOut == False: base.localAvatar.selectGag(gagId) selected = True if not selected: # Denied! self.selectDenySound.play() self.resetTimeout() else: self.selectSound.play() self.request('Idle') def disableControls(self): self.ignore('wheel_up') self.ignore('wheel_down') for i in xrange(len(self.tracks)): self.ignore(str(i + 1)) self.request('Idle') def __maybeDoSelect(self): if self.getCurrentOrNextState() == 'Idle': self.request('Select') def __handleTrackChoose(self, idx): if not base.localAvatar.hasAttacks(): return self.__maybeDoSelect() self.resetTimeout() if self.currentTrack == idx: # Scroll through the current track. self.tracks[self.currentTrack].selectNextGag() self.newTrackSound.stop() self.keyScrollSound.play() else: # Always start from the beginning when using the keys to choose a track. self.updateCurrentTrack(idx, 0) self.newTrackSound.play() def __handleScrollUp(self): if not base.localAvatar.hasAttacks(): return self.__maybeDoSelect() self.resetTimeout() track = self.tracks[self.currentTrack] if track.isOnFirstGag(): self.prevTrack() else: track.selectPrevGag() self.newTrackSound.stop() self.keyScrollSound.play() def __handleScrollDown(self): if not base.localAvatar.hasAttacks(): return self.__maybeDoSelect() self.resetTimeout() track = self.tracks[self.currentTrack] if track.isOnLastGag(): self.nextTrack() else: track.selectNextGag() self.newTrackSound.stop() self.keyScrollSound.play() def nextTrack(self): newIdx = self.currentTrack + 1 if newIdx > len(self.tracks) - 1: newIdx = 0 self.updateCurrentTrack(newIdx) def prevTrack(self): newIdx = self.currentTrack - 1 if newIdx < 0: newIdx = len(self.tracks) - 1 self.updateCurrentTrack(newIdx) def updateCurrentTrack(self, idx, startLoc=None): oldTrack = self.tracks[self.currentTrack] oldTrack.deselectAll() oldTrack.stashContents() if idx - self.currentTrack < 0: direction = 1 else: direction = 0 if startLoc is None: startLoc = direction if direction == 0: self.fwdShakeIval.start() else: self.revShakeIval.start() self.currentTrack = idx # Resort the tracks numTracks = len(self.tracks) maxTrack = numTracks - 1 for i in xrange(len(self.tracks)): track = self.tracks[i] if i == idx: sort = FRAME_FRONT_SORT elif i > idx: sort = FRAME_SORT_BEGIN + (maxTrack - i) * FRAME_SORT_DISTANCE elif i < idx: sort = FRAME_SORT_BEGIN + (i * FRAME_SORT_DISTANCE) track.setBin('gsg-popup', sort) if i == idx: track.unstashContents() if startLoc == 0: track.selectFirstGag() else: track.selectLastGag() else: track.stashContents()
class DropDownMenu(DirectObject): ALeft = 0 ACenter = 1 ARight = 2 ENone = 0 EFade = 1 ESlide = 2 EStretch = 3 PLeft = 0 PRight = 1 PBottom = 2 PTop = 3 parents = (('a2dBottomLeft', 'a2dLeftCenter', 'a2dTopLeft'), ('a2dTopRight', 'a2dRightCenter', 'a2dBottomRight'), ('a2dBottomLeft', 'a2dBottomCenter', 'a2dBottomRight'), ('a2dTopLeft', 'a2dTopCenter', 'a2dTopRight')) def __init__(self, items, parent=None, sidePad=.0, edgePos=PTop, align=ALeft, effect=ENone, buttonThrower=None, font=None, baselineOffset=.0, scale=.05, itemHeight=1., leftPad=.0, separatorHeight=.5, underscoreThickness=1, BGColor=(0, 0, 0, .7), BGBorderColor=(1, .85, .4, 1), separatorColor=(1, 1, 1, 1), frameColorHover=(1, .85, .4, 1), frameColorPress=(0, 1, 0, 1), textColorReady=(1, 1, 1, 1), textColorHover=(0, 0, 0, 1), textColorPress=(0, 0, 0, 1), textColorDisabled=(.5, .5, .5, 1), draggable=False, onMove=None): ''' sidePad : additional space on the left and right of the text item edgePos : menu bar position on the screen, use DropDownMenu.PLeft, PRight, PBottom, or PTop align : menu items alignment on menu bar, use DropDownMenu.ALeft, ACenter, or ARight effect : the drop down appearance effect, use DropDownMenu.ENone, EFade, ESlide, or EStretch draggable : menu bar's draggability status onMove : a function which will be called after changing edge position Read the remaining options documentation in PopupMenu class. ''' self.parent = parent if parent else getattr( base, DropDownMenu.parents[edgePos][align]) self.BT = buttonThrower if buttonThrower else base.buttonThrowers[ 0].node() self.menu = self.parent.attachNewNode('dropdownmenu-%s' % id(self)) self.font = font if font else TextNode.getDefaultFont() self.baselineOffset = baselineOffset self.scale = scale self.itemHeight = itemHeight self.sidePad = sidePad self.edgePos = edgePos self.alignment = align self.effect = effect self.leftPad = leftPad self.underscoreThickness = underscoreThickness self.separatorHeight = separatorHeight self.BGColor = BGColor self.BGBorderColor = BGBorderColor self.separatorColor = separatorColor self.frameColorHover = frameColorHover self.frameColorPress = frameColorPress self.textColorReady = textColorReady self.textColorHover = textColorHover self.textColorPress = textColorPress self.textColorDisabled = textColorDisabled self.draggable = draggable self.onMove = onMove self.dropDownMenu = self.whoseDropDownMenu = None self.gapFromEdge = gapFromEdge = .008 texMargin = self.font.getTextureMargin() * self.scale * .25 b = DirectButton(parent=NodePath(''), text='^|g_', text_font=self.font, scale=self.scale) fr = b.node().getFrame() b.getParent().removeNode() baselineToCenter = (fr[2] + fr[3]) * self.scale LH = (fr[3] - fr[2]) * self.itemHeight * self.scale baselineToTop = (fr[3] * self.itemHeight * self.scale / LH) / (1. + self.baselineOffset) baselineToBot = LH / self.scale - baselineToTop self.height = LH + .01 l, r, b, t = 0, 5, -self.height, 0 self.menuBG = DirectFrame(parent=self.menu, frameColor=BGColor, frameSize=(l, r, b, t), state=DGG.NORMAL, suppressMouse=1) if self.draggable: self.setDraggable(1) LSborder = LineSegs() LSborder.setThickness(2) LSborder.setColor(0, 0, 0, 1) LSborder.moveTo(l, 0, b) LSborder.drawTo(r, 0, b) self.menuBG.attachNewNode(LSborder.create()) self.itemsParent = self.menu.attachNewNode('menu items parent') x = sidePad * self.scale + gapFromEdge for t, menuItemsGenerator in items: underlinePos = t.find('_') t = t.replace('_', '') b = DirectButton( parent=self.itemsParent, text=t, text_font=self.font, pad=(sidePad, 0), scale=self.scale, pos=(x, 0, -baselineToTop * self.scale - gapFromEdge), text_fg=textColorReady, # text color when mouse over text2_fg=textColorHover, # text color when pressed text1_fg=textColorPress, # framecolor when pressed frameColor=frameColorPress, command=self.__createMenu, extraArgs=[True, menuItemsGenerator], text_align=TextNode.ALeft, relief=DGG.FLAT, rolloverSound=0, clickSound=0) b['extraArgs'] += [b.getName()] b.stateNodePath[2].setColor( *frameColorHover) # framecolor when mouse over b.stateNodePath[0].setColor(0, 0, 0, 0) # framecolor when ready fr = b.node().getFrame() b['frameSize'] = (fr[0], fr[1], -baselineToBot, baselineToTop) self.accept(DGG.ENTER + b.guiId, self.__createMenu, [False, menuItemsGenerator, b.getName()]) if underlinePos > -1: tn = TextNode('') tn.setFont(self.font) tn.setText(t[:underlinePos + 1]) tnp = NodePath(tn.getInternalGeom()) underlineXend = tnp.getTightBounds()[1][0] tnp.removeNode() tn.setText(t[underlinePos]) tnp = NodePath(tn.getInternalGeom()) b3 = tnp.getTightBounds() underlineXstart = underlineXend - (b3[1] - b3[0])[0] tnp.removeNode() LSunder = LineSegs() LSunder.setThickness(underscoreThickness) LSunder.moveTo(underlineXstart + texMargin, 0, -.7 * baselineToBot) LSunder.drawTo(underlineXend - texMargin, 0, -.7 * baselineToBot) underline = b.stateNodePath[0].attachNewNode(LSunder.create()) underline.setColor(Vec4(*textColorReady), 1) underline.copyTo(b.stateNodePath[1], 10).setColor(Vec4(*textColorPress), 1) underline.copyTo(b.stateNodePath[2], 10).setColor(Vec4(*textColorHover), 1) self.accept('alt-' + t[underlinePos].lower(), self.__createMenu, [True, menuItemsGenerator, b.getName()]) x += (fr[1] - fr[0]) * self.scale self.width = x - 2 * gapFromEdge self.align(align) self.setEdgePos(edgePos) self.minZ = base.a2dBottom + self.height if edgePos == DropDownMenu.PBottom else None viewPlaneNode = PlaneNode('cut menu') viewPlaneNode.setPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, -LH))) self.clipPlane = self.menuBG.attachNewNode(viewPlaneNode) def __createMenu(self, clicked, menuItemsGenerator, DBname, crap=None): itself = self.dropDownMenu and self.whoseDropDownMenu == DBname if not (clicked or self.dropDownMenu) or (not clicked and itself): return self.__removeMenu() if clicked and itself: return # removes any context menu if clicked: self.__removePopupMenu() self.dropDownMenu = PopupMenu( items=menuItemsGenerator(), parent=self.parent, buttonThrower=self.BT, font=self.font, baselineOffset=self.baselineOffset, scale=self.scale, itemHeight=self.itemHeight, leftPad=self.leftPad, separatorHeight=self.separatorHeight, underscoreThickness=self.underscoreThickness, BGColor=self.BGColor, BGBorderColor=self.BGBorderColor, separatorColor=self.separatorColor, frameColorHover=self.frameColorHover, frameColorPress=self.frameColorPress, textColorReady=self.textColorReady, textColorHover=self.textColorHover, textColorPress=self.textColorPress, textColorDisabled=self.textColorDisabled, minZ=self.minZ) self.acceptOnce(self.dropDownMenu.BTprefix + 'destroyed', setattr, [self, 'dropDownMenu', None]) self.whoseDropDownMenu = DBname item = self.menu.find('**/%s' % DBname) fr = item.node().getFrame() #~ if self.edgePos==DropDownMenu.PLeft: #~ x=max(fr[1],self.dropDownMenu.menu.getX(item)) #~ z=fr[2] #~ elif self.edgePos==DropDownMenu.PRight: #~ x=min(fr[0],self.dropDownMenu.menu.getX(item)) #~ z=fr[2]-self.dropDownMenu.maxWidth #~ elif self.edgePos in (DropDownMenu.PBottom,DropDownMenu.PTop): #~ x=fr[1]-self.dropDownMenu.maxWidth if self.alignment==DropDownMenu.ARight else fr[0] #~ z=fr[2] if self.edgePos==DropDownMenu.PTop else fr[3]+self.dropDownMenu.height if self.edgePos == DropDownMenu.PLeft: x = max(fr[1], self.dropDownMenu.menu.getX(item)) z = fr[3] - (self.height - self.gapFromEdge) / self.scale elif self.edgePos == DropDownMenu.PRight: x = min(fr[0], self.dropDownMenu.menu.getX(item)) z = fr[3] - (self.height - self.gapFromEdge ) / self.scale - self.dropDownMenu.maxWidth elif self.edgePos in (DropDownMenu.PBottom, DropDownMenu.PTop): x = fr[ 1] - self.dropDownMenu.maxWidth if self.alignment == DropDownMenu.ARight else fr[ 0] z = fr[3] - ( self.height - self.gapFromEdge ) / self.scale if self.edgePos == DropDownMenu.PTop else fr[2] + ( self.height) / self.scale + self.dropDownMenu.height self.dropDownMenu.menu.setPos(item, x, 0, z) if self.effect == DropDownMenu.EFade: self.dropDownMenu.menu.colorScaleInterval( .3, Vec4(1), Vec4(1, 1, 1, 0), blendType='easeIn').start() elif self.effect == DropDownMenu.ESlide: pos = self.dropDownMenu.menu.getPos() if self.edgePos == DropDownMenu.PTop: startPos = Point3(0, 0, self.dropDownMenu.height * self.scale) elif self.edgePos == DropDownMenu.PBottom: startPos = Point3(0, 0, -self.dropDownMenu.height * self.scale) elif self.edgePos == DropDownMenu.PLeft: startPos = Point3(-self.dropDownMenu.maxWidth * self.scale, 0, 0) elif self.edgePos == DropDownMenu.PRight: startPos = Point3(self.dropDownMenu.maxWidth * self.scale, 0, 0) self.dropDownMenu.menu.posInterval(.3, pos, pos + startPos, blendType='easeIn').start() self.dropDownMenu.menu.setClipPlane(self.clipPlane) elif self.effect == DropDownMenu.EStretch: if self.edgePos == DropDownMenu.PTop: startHpr = Vec3(0, -90, 0) elif self.edgePos == DropDownMenu.PBottom: dz = self.dropDownMenu.height * self.scale for c in asList(self.dropDownMenu.menu.getChildren()): c.setZ(c.getZ() + dz) self.dropDownMenu.menu.setZ(self.dropDownMenu.menu, -dz) startHpr = Vec3(0, 90, 0) elif self.edgePos == DropDownMenu.PLeft: startHpr = Vec3(90, 0, 0) elif self.edgePos == DropDownMenu.PRight: dx = self.dropDownMenu.maxWidth * self.scale for c in asList(self.dropDownMenu.menu.getChildren()): c.setX(c.getX() - dx) self.dropDownMenu.menu.setX(self.dropDownMenu.menu, dx) startHpr = Vec3(-90, 0, 0) self.dropDownMenu.menu.hprInterval(.3, Vec3(0), startHpr, blendType='easeIn').start() def __removeMenu(self): if self.dropDownMenu: self.dropDownMenu.destroy() self.dropDownMenu = None def __removePopupMenu(self): menuEvents = messenger.find('menu-') if menuEvents: menuEvent = menuEvents.keys() activeMenu = menuEvents[menuEvent[0]].values()[0][0].im_self activeMenu.destroy(delParents=True) def __reverseItems(self): tmp = NodePath('') self.itemsParent.getChildren().reparentTo(tmp) children = asList(tmp.getChildren()) for c in reversed(children): c.reparentTo(self.itemsParent) tmp.removeNode() x = self.sidePad * self.scale + self.gapFromEdge for c in asList(self.itemsParent.getChildren()): c.setX(x) fr = c.node().getFrame() x += (fr[1] - fr[0]) * self.scale def __startDrag(self, crap): taskMgr.add(self.__drag, 'dragging menu bar', extraArgs=[Point2(base.mouseWatcherNode.getMouse())]) self.__removePopupMenu() self.origBTprefix = self.BT.getPrefix() self.BT.setPrefix('dragging menu bar') def __drag(self, origMpos): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() if mpos != origMpos: x, y = mpos.getX(), mpos.getY(), deltas = [x + 1, 1 - x, y + 1, 1 - y] closestEdge = deltas.index(min(deltas)) if closestEdge != self.edgePos: self.setEdgePos(closestEdge) return Task.cont def __stopDrag(self, crap): taskMgr.remove('dragging menu bar') self.BT.setPrefix(self.origBTprefix) def destroy(self): self.__removeMenu() self.ignoreAll() self.menu.removeNode() def isDraggable(self): ''' Returns menu bar's draggable status ''' return self.draggable def setDraggable(self, d): ''' Sets menu bar's draggable status ''' self.draggable = d if d: self.menuBG.bind(DGG.B1PRESS, self.__startDrag) self.menuBG.bind(DGG.B1RELEASE, self.__stopDrag) else: self.menuBG.unbind(DGG.B1PRESS) self.menuBG.unbind(DGG.B1RELEASE) def align(self, align=None): ''' Aligns menu text on menu bar. Use one of DropDownMenu.ALeft, DropDownMenu.ACenter, or DropDownMenu.ARight. ''' self.parent = getattr( base, DropDownMenu.parents[self.edgePos] [self.alignment if align is None else align]) self.menu.reparentTo(self.parent) if align is not None: self.itemsParent.setX(-.5 * self.width * align) self.menuBG.setX(-.5 * (5 - self.width) * align) self.alignment = align def setEdgePos(self, edge): ''' Sticks menu bar to 4 possible screen edges : DropDownMenu.PLeft, DropDownMenu.PRight, DropDownMenu.PBottom, or DropDownMenu.PTop. ''' lastEdge = self.edgePos reverseItems = lastEdge == DropDownMenu.PLeft self.edgePos = edge self.itemsParent.setZ(0) self.menuBG.setSz(1) alignment = None if self.edgePos == DropDownMenu.PLeft: self.menu.setR(-90) if self.alignment != DropDownMenu.ACenter: alignment = 2 - self.alignment self.__reverseItems() self.minZ = None else: if self.edgePos == DropDownMenu.PRight: self.menu.setR(90) self.minZ = None elif self.edgePos == DropDownMenu.PBottom: self.menu.setR(0) self.menuBG.setSz(-1) self.itemsParent.setZ(-self.menuBG.node().getFrame()[2]) self.minZ = base.a2dBottom + self.height elif self.edgePos == DropDownMenu.PTop: self.menu.setR(0) self.minZ = None if reverseItems: if self.alignment != DropDownMenu.ACenter: alignment = 2 - self.alignment self.__reverseItems() self.align(alignment) if callable(self.onMove): self.onMove()