예제 #1
0
파일: ui.py 프로젝트: tgbugs/desc
    def __create_item__(self, text, command = None, args = tuple()): 
        args = list(args)

        #if not len(self.items):
            #parent = self.frame
        if len(self.items) <= 1:
            parent = self.itemsParent  #everyone else parents off 2nd text
        else:
            parent = list(self.items.values())[-1]

        if command != None:
            def cmd(*args):
                """ any item should raise
                """
                self.raise_()
                command(*args)
        else:
            cmd = self.raise_


        b = DirectButton(
            parent=parent,
            frameColor=(1,1,1,.0),  # a = 0 => no border overlap
            frameSize=(0, self.width, 0, self.text_h),
            text=' '+text,  # hack to keep spacing from border
            text_font=self.text_font,
            text_fg=self.text_color,
            text_scale=self.text_s,
            text_pos=(0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s),
            command=cmd,
            relief=DGG.FLAT,
            text_align=TextNode.ALeft,
        )

        b.setPos(LVecBase3f(0, 0, -self.text_h))
        b.setName('DirectButton-'+text)
        if not len(self.items):
            self.items['title'] = b
            b.setBin(*self.DRAW_ORDER['title'])
        else:
            b['extraArgs'] = args+[self, id(b)]
            b.node().setPythonTag('id', id(b))
            b.setBin(*self.DRAW_ORDER['items'])
            if len(self.items) is 1:  # the first item that is not the title
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
                self.__first_item__ = id(b)

            self.items[id(b)] = b

        if text == ' blank':
            if self.__add_head__ is None:
                self.__add_head__ = 1

        return b
예제 #2
0
    def __create_item__(self, text, command=None, args=tuple()):
        args = list(args)

        #if not len(self.items):
        #parent = self.frame
        if len(self.items) <= 1:
            parent = self.itemsParent  #everyone else parents off 2nd text
        else:
            parent = list(self.items.values())[-1]

        if command != None:

            def cmd(*args):
                """ any item should raise
                """
                self.raise_()
                command(*args)
        else:
            cmd = self.raise_

        b = DirectButton(
            parent=parent,
            frameColor=(1, 1, 1, .0),  # a = 0 => no border overlap
            frameSize=(0, self.width, 0, self.text_h),
            text=' ' + text,  # hack to keep spacing from border
            text_font=self.text_font,
            text_fg=self.text_color,
            text_scale=self.text_s,
            text_pos=(0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s),
            command=cmd,
            relief=DGG.FLAT,
            text_align=TextNode.ALeft,
        )

        b.setPos(LVecBase3f(0, 0, -self.text_h))
        b.setName('DirectButton-' + text)
        if not len(self.items):
            self.items['title'] = b
            b.setBin(*self.DRAW_ORDER['title'])
        else:
            b['extraArgs'] = args + [self, id(b)]
            b.node().setPythonTag('id', id(b))
            b.setBin(*self.DRAW_ORDER['items'])
            if len(self.items) is 1:  # the first item that is not the title
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
                self.__first_item__ = id(b)

            self.items[id(b)] = b

        if text == ' blank':
            if self.__add_head__ is None:
                self.__add_head__ = 1

        return b
예제 #3
0
파일: Menu.py 프로젝트: Crpaxton4/NanoV
    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)
예제 #4
0
파일: Menu.py 프로젝트: Crpaxton4/NanoV
    def __init__(self,
                 items,
                 parent=None,
                 buttonThrower=None,
                 onDestroy=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),
                 minZ=None,
                 useMouseZ=True):
        '''
      items : a collection of menu items
         Item format :
            ( 'Item text', 'path/to/image', command )
                        OR
            ( 'Item text', 'path/to/image', command, arg1,arg2,.... )
         If you don't want to use an image, pass 0.

         To create disabled item, pass 0 for the command :
            ( 'Item text', 'path/to/image', 0 )
         so, you can easily switch between enabled or disabled :
            ( 'Item text', 'path/to/image', command if commandEnabled else 0 )
                        OR
            ( 'Item text', 'path/to/image', (0,command)[commandEnabled] )

         To create submenu, pass a sequence of submenu items for the command.
         To create disabled submenu, pass an empty sequence for the command.

         To enable hotkey, insert an underscore before the character,
         e.g. hotkey of 'Item te_xt' is 'x' key.

         To add shortcut key text at the right side of the item, append it at the end of
         the item text, separated by "more than" sign, e.g. 'Item text>Ctrl-T'.

         To insert separator line, pass 0 for the whole item.


      parent : where to attach the menu, defaults to aspect2d

      buttonThrower : button thrower whose thrown events are blocked temporarily
                      when the menu is displayed. If not given, the default
                      button thrower is used

      onDestroy : user function which will be called after the menu is fully destroyed

      font           : text font
      baselineOffset : text's baseline Z offset

      scale       : text scale
      itemHeight  : spacing between items, defaults to 1
      leftPad     : blank space width before text
      separatorHeight : separator line height, relative to itemHeight

      underscoreThickness : underscore line thickness

      BGColor, BGBorderColor, separatorColor, frameColorHover, frameColorPress,
      textColorReady, textColorHover, textColorPress, textColorDisabled
      are some of the menu components' color

      minZ : minimum Z position to restrain menu's bottom from going offscreen (-1..1).
             If it's None, it will be set a little above the screen's bottom.
      '''
        self.parent = parent if parent else aspect2d
        self.onDestroy = onDestroy
        self.BT = buttonThrower if buttonThrower else base.buttonThrowers[
            0].node()
        self.menu = NodePath('menu-%s' % id(self))
        self.parentMenu = None
        self.submenu = None
        self.BTprefix = self.menu.getName() + '>'
        self.submenuCreationTaskName = 'createSubMenu-' + self.BTprefix
        self.submenuRemovalTaskName = 'removeSubMenu-' + self.BTprefix
        self.font = font if font else TextNode.getDefaultFont()
        self.baselineOffset = baselineOffset
        self.scale = scale
        self.itemHeight = itemHeight
        self.leftPad = leftPad
        self.separatorHeight = separatorHeight
        self.underscoreThickness = underscoreThickness
        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.minZ = minZ
        self.mpos = Point2(base.mouseWatcherNode.getMouse())

        self.itemCommand = []
        self.hotkeys = {}
        self.numItems = 0
        self.sel = -1
        self.selByKey = False

        bgPad = self.bgPad = .0125
        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
        imageHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .85
        arrowHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .5
        baselineToTop = (fr[3] * self.itemHeight * self.scale /
                         LH) / (1. + self.baselineOffset)
        baselineToBot = LH / self.scale - baselineToTop
        itemZcenter = (baselineToTop - baselineToBot) * .5
        separatorHalfHeight = .5 * separatorHeight * LH
        LSseparator = LineSegs()
        LSseparator.setColor(.5, .5, .5, .2)

        arrowVtx = [
            (0, itemZcenter),
            (-2 * arrowHalfHeight, itemZcenter + arrowHalfHeight),
            (-arrowHalfHeight, itemZcenter),
            (-2 * arrowHalfHeight, itemZcenter - arrowHalfHeight),
        ]
        tri = Triangulator()
        vdata = GeomVertexData('trig', GeomVertexFormat.getV3(), Geom.UHStatic)
        vwriter = GeomVertexWriter(vdata, 'vertex')
        for x, z in arrowVtx:
            vi = tri.addVertex(x, z)
            vwriter.addData3f(x, 0, z)
            tri.addPolygonVertex(vi)
        tri.triangulate()
        prim = GeomTriangles(Geom.UHStatic)
        for i in range(tri.getNumTriangles()):
            prim.addVertices(tri.getTriangleV0(i), tri.getTriangleV1(i),
                             tri.getTriangleV2(i))
            prim.closePrimitive()
        geom = Geom(vdata)
        geom.addPrimitive(prim)
        geomNode = GeomNode('arrow')
        geomNode.addGeom(geom)
        realArrow = NodePath(geomNode)
        z = -baselineToTop * self.scale - bgPad
        maxWidth = .1 / self.scale
        shortcutTextMaxWidth = 0
        anyImage = False
        anyArrow = False
        anyShortcut = False
        arrows = []
        shortcutTexts = []
        loadPrcFileData('', 'text-flatten 0')
        for item in items:
            if item:
                t, imgPath, f = item[:3]
                haveSubmenu = type(f) in SEQUENCE_TYPES
                anyArrow |= haveSubmenu
                anyImage |= bool(imgPath)
                disabled = not len(f) if haveSubmenu else not callable(f)
                args = item[3:]
                underlinePos = t.find('_')
                t = t.replace('_', '')
                shortcutSepPos = t.find('>')
                if shortcutSepPos > -1:
                    if haveSubmenu:
                        print(
                            "\nA SHORTCUT KEY POINTING TO A SUBMENU IS NON-SENSE, DON'T YOU AGREE ?"
                        )
                    else:
                        shortcutText = NodePath(
                            OnscreenText(
                                parent=self.menu,
                                text=t[shortcutSepPos + 1:],
                                font=self.font,
                                scale=1,
                                fg=(1, 1, 1, 1),
                                align=TextNode.ARight,
                            ))
                        shortcutTextMaxWidth = max(
                            shortcutTextMaxWidth,
                            abs(shortcutText.getTightBounds()[0][0]))
                        anyShortcut = True
                    t = t[:shortcutSepPos]
                else:
                    shortcutText = ''
                EoLcount = t.count('\n')
                arrowZpos = -self.font.getLineHeight() * EoLcount * .5
                if disabled:
                    b = NodePath(
                        OnscreenText(
                            parent=self.menu,
                            text=t,
                            font=self.font,
                            scale=1,
                            fg=textColorDisabled,
                            align=TextNode.ALeft,
                        ))
                    # don't pass the scale and position to OnscreenText constructor,
                    # to maintain correctness between the OnscreenText and DirectButton items
                    # due to the new text generation implementation
                    b.setScale(self.scale)
                    b.setZ(z)
                    maxWidth = max(maxWidth,
                                   b.getTightBounds()[1][0] / self.scale)
                    if shortcutText:
                        shortcutText.reparentTo(b)
                        shortcutText.setColor(Vec4(*textColorDisabled), 1)
                        shortcutText.setZ(arrowZpos)
                        shortcutTexts.append(shortcutText)
                else:
                    b = DirectButton(
                        parent=self.menu,
                        text=t,
                        text_font=self.font,
                        scale=self.scale,
                        pos=(0, 0, z),
                        text_fg=textColorReady,
                        # text color when mouse over
                        text2_fg=textColorHover,
                        # text color when pressed
                        text1_fg=textColorHover
                        if haveSubmenu else textColorPress,
                        # framecolor when pressed
                        frameColor=frameColorHover
                        if haveSubmenu else frameColorPress,
                        command=(lambda: 0)
                        if haveSubmenu else self.__runCommand,
                        extraArgs=[] if haveSubmenu else [f, args],
                        text_align=TextNode.ALeft,
                        relief=DGG.FLAT,
                        rolloverSound=0,
                        clickSound=0,
                        pressEffect=0)
                    b.stateNodePath[2].setColor(
                        *frameColorHover)  # framecolor when mouse over
                    b.stateNodePath[0].setColor(0, 0, 0,
                                                0)  # framecolor when ready
                    bframe = Vec4(b.node().getFrame())
                    if EoLcount:
                        bframe.setZ(EoLcount * 10)
                        b['frameSize'] = bframe
                    maxWidth = max(maxWidth, bframe[1])
                    if shortcutText:
                        for snpi, col in ((0, textColorReady),
                                          (1, textColorPress),
                                          (2, textColorHover)):
                            sct = shortcutText.copyTo(b.stateNodePath[snpi],
                                                      sort=10)
                            sct.setColor(Vec4(*col), 1)
                            sct.setZ(arrowZpos)
                            shortcutTexts.append(sct)
                        shortcutText.removeNode()
                if imgPath:
                    img = loader.loadTexture(imgPath)
                    if disabled:
                        if imgPath in PopupMenu.grayImages:
                            img = PopupMenu.grayImages[imgPath]
                        else:
                            pnm = PNMImage()
                            img.store(pnm)
                            pnm.makeGrayscale(.2, .2, .2)
                            img = Texture()
                            img.load(pnm)
                            PopupMenu.grayImages[imgPath] = img
                    img.setMinfilter(Texture.FTLinearMipmapLinear)
                    img.setWrapU(Texture.WMClamp)
                    img.setWrapV(Texture.WMClamp)
                    CM = CardMaker('')
                    CM.setFrame(-2 * imageHalfHeight - leftPad, -leftPad,
                                itemZcenter - imageHalfHeight,
                                itemZcenter + imageHalfHeight)
                    imgCard = b.attachNewNode(CM.generate())
                    imgCard.setTexture(img)
                if underlinePos > -1:
                    oneLineText = t[:underlinePos + 1]
                    oneLineText = oneLineText[oneLineText.rfind('\n') + 1:]
                    tn = TextNode('')
                    tn.setFont(self.font)
                    tn.setText(oneLineText)
                    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()
                    underlineZpos = -.7 * baselineToBot - self.font.getLineHeight(
                    ) * t[:underlinePos].count('\n')
                    LSunder = LineSegs()
                    LSunder.setThickness(underscoreThickness)
                    LSunder.moveTo(underlineXstart + texMargin, 0,
                                   underlineZpos)
                    LSunder.drawTo(underlineXend - texMargin, 0, underlineZpos)
                    if disabled:
                        underline = b.attachNewNode(LSunder.create())
                        underline.setColor(Vec4(*textColorDisabled), 1)
                    else:
                        underline = b.stateNodePath[0].attachNewNode(
                            LSunder.create())
                        underline.setColor(Vec4(*textColorReady), 1)
                        underline.copyTo(b.stateNodePath[1], 10).setColor(
                            Vec4(*textColorHover
                                 if haveSubmenu else textColorPress), 1)
                        underline.copyTo(b.stateNodePath[2],
                                         10).setColor(Vec4(*textColorHover), 1)
                        hotkey = t[underlinePos].lower()
                        if hotkey in self.hotkeys:
                            self.hotkeys[hotkey].append(self.numItems)
                        else:
                            self.hotkeys[hotkey] = [self.numItems]
                            self.accept(self.BTprefix + hotkey,
                                        self.__processHotkey, [hotkey])
                            self.accept(self.BTprefix + 'alt-' + hotkey,
                                        self.__processHotkey, [hotkey])
                if haveSubmenu:
                    if disabled:
                        arrow = realArrow.instanceUnderNode(b, '')
                        arrow.setColor(Vec4(*textColorDisabled), 1)
                        arrow.setZ(arrowZpos)
                    else:
                        arrow = realArrow.instanceUnderNode(
                            b.stateNodePath[0], 'r')
                        arrow.setColor(Vec4(*textColorReady), 1)
                        arrow.setZ(arrowZpos)
                        arrPress = realArrow.instanceUnderNode(
                            b.stateNodePath[1], 'p')
                        arrPress.setColor(Vec4(*textColorHover), 1)
                        arrPress.setZ(arrowZpos)
                        arrHover = realArrow.instanceUnderNode(
                            b.stateNodePath[2], 'h')
                        arrHover.setColor(Vec4(*textColorHover), 1)
                        arrHover.setZ(arrowZpos)
                        # weird, if sort order is 0, it's obscured by the frame
                        for a in (arrPress, arrHover):
                            a.reparentTo(a.getParent(), sort=10)
                if not disabled:
                    extraArgs = [self.numItems, f if haveSubmenu else 0]
                    self.accept(DGG.ENTER + b.guiId, self.__hoverOnItem,
                                extraArgs)
                    self.accept(DGG.EXIT + b.guiId, self.__offItem)
                    #~ self.itemCommand.append((None,0) if haveSubmenu else (f,args))
                    self.itemCommand.append((f, args))
                    if self.numItems == 0:
                        self.firstButtonIdx = int(b.guiId[2:])
                    self.numItems += 1
                z -= LH + self.font.getLineHeight() * self.scale * EoLcount
            else:  # SEPARATOR LINE
                z += LH - separatorHalfHeight - baselineToBot * self.scale
                LSseparator.moveTo(0, 0, z)
                LSseparator.drawTo(self.scale * .5, 0, z)
                LSseparator.drawTo(self.scale, 0, z)
                z -= separatorHalfHeight + baselineToTop * self.scale
        maxWidth += 7 * arrowHalfHeight * (
            anyArrow or anyShortcut) + .2 + shortcutTextMaxWidth
        arrowXpos = maxWidth - arrowHalfHeight
        realArrow.setX(arrowXpos)
        if anyImage:
            leftPad += 2 * imageHalfHeight + leftPad
        for sct in shortcutTexts:
            sct.setX(maxWidth - 2 * (arrowHalfHeight * anyArrow + .2))
        for c in asList(self.menu.findAllMatches('**/DirectButton*')):
            numLines = c.node().getFrame()[2]
            c.node().setFrame(
                Vec4(
                    -leftPad, maxWidth, -baselineToBot -
                    (numLines * .1 * self.itemHeight if numLines >= 10 else 0),
                    baselineToTop))
        loadPrcFileData('', 'text-flatten 1')

        try:
            minZ = self.menu.getChild(0).getRelativePoint(
                b, Point3(0, 0,
                          b.node().getFrame()[2]))[2]
        except:
            minZ = self.menu.getChild(0).getRelativePoint(
                self.menu, Point3(
                    0, 0,
                    b.getTightBounds()[0][2]))[2] - baselineToBot * .5
        try:
            top = self.menu.getChild(0).node().getFrame()[3]
        except:
            top = self.menu.getChild(0).getZ() + baselineToTop
        l, r, b, t = -leftPad - bgPad / self.scale, maxWidth + bgPad / self.scale, minZ - bgPad / self.scale, top + bgPad / self.scale
        menuBG = DirectFrame(parent=self.menu.getChild(0),
                             frameSize=(l, r, b, t),
                             frameColor=BGColor,
                             state=DGG.NORMAL,
                             suppressMouse=1)
        menuBorder = self.menu.getChild(0).attachNewNode('border')
        borderVtx = (
            (l, 0, b),
            (l, 0, .5 * (b + t)),
            (l, 0, t),
            (.5 * (l + r), 0, t),
            (r, 0, t),
            (r, 0, .5 * (b + t)),
            (r, 0, b),
            (.5 * (l + r), 0, b),
            (l, 0, b),
        )
        LSborderBG = LineSegs()
        LSborderBG.setThickness(4)
        LSborderBG.setColor(0, 0, 0, .7)
        LSborderBG.moveTo(*(borderVtx[0]))
        for v in borderVtx[1:]:
            LSborderBG.drawTo(*v)
        # fills the gap at corners
        for v in range(0, 7, 2):
            LSborderBG.moveTo(*(borderVtx[v]))
        menuBorder.attachNewNode(LSborderBG.create())
        LSborder = LineSegs()
        LSborder.setThickness(2)
        LSborder.setColor(*BGBorderColor)
        LSborder.moveTo(*(borderVtx[0]))
        for v in borderVtx[1:]:
            LSborder.drawTo(*v)
        menuBorder.attachNewNode(LSborder.create())
        for v in range(1, 8, 2):
            LSborderBG.setVertexColor(v, Vec4(0, 0, 0, .1))
            LSborder.setVertexColor(v, Vec4(.3, .3, .3, .5))
        menuBorderB3 = menuBorder.getTightBounds()
        menuBorderDims = menuBorderB3[1] - menuBorderB3[0]
        menuBG.wrtReparentTo(self.menu, sort=-1)
        self.menu.reparentTo(self.parent)
        x = -menuBorderB3[0][0] * self.scale
        for c in asList(self.menu.getChildren()):
            c.setX(x)
        self.maxWidth = maxWidth = menuBorderDims[0]
        self.height = menuBorderDims[2]
        maxWidthR2D = maxWidth * self.menu.getChild(0).getSx(render2d)
        separatorLines = self.menu.attachNewNode(LSseparator.create(), 10)
        separatorLines.setSx(maxWidth)
        for v in range(1, LSseparator.getNumVertices(), 3):
            LSseparator.setVertexColor(v, Vec4(*separatorColor))
        x = clampScalar(-.98, .98 - maxWidthR2D,
                        self.mpos[0] - maxWidthR2D * .5)
        minZ = (-.98 if self.minZ is None else self.minZ)
        z = clampScalar(
            minZ +
            menuBorderDims[2] * self.scale * self.parent.getSz(render2d), .98,
            self.mpos[1] if useMouseZ else -1000)
        self.menu.setPos(render2d, x, 0, z)
        self.menu.setTransparency(1)

        self.origBTprefix = self.BT.getPrefix()
        self.BT.setPrefix(self.BTprefix)
        self.accept(self.BTprefix + 'escape', self.destroy)
        for e in ('mouse1', 'mouse3'):
            self.accept(self.BTprefix + e, self.destroy, [True])
        self.accept(self.BTprefix + 'arrow_down', self.__nextItem)
        self.accept(self.BTprefix + 'arrow_down-repeat', self.__nextItem)
        self.accept(self.BTprefix + 'arrow_up', self.__prevItem)
        self.accept(self.BTprefix + 'arrow_up-repeat', self.__prevItem)
        self.accept(self.BTprefix + 'enter', self.__runSelItemCommand)
        self.accept(self.BTprefix + 'space', self.__runSelItemCommand)