Example #1
0
class HostMenu(DirectObject):
    def __init__(self):
        self.defaultBtnMap = base.loader.loadModel("gui/button_map")
        self.buttonGeom = (
            self.defaultBtnMap.find("**/button_ready"),
            self.defaultBtnMap.find("**/button_click"),
            self.defaultBtnMap.find("**/button_rollover"),
            self.defaultBtnMap.find("**/button_disabled"))

        defaultFont = loader.loadFont('gui/eufm10.ttf')

        self.logFrame = DirectScrolledFrame(
            canvasSize = (0, base.a2dRight * 2, -5, 0),
            frameSize = (0, base.a2dRight * 2,
                (base.a2dBottom+.2) * 2, 0),
            frameColor = (0.1, 0.1, 0.1, 1))
        self.logFrame.reparentTo(base.a2dTopLeft)

        # create the info and server debug output
        self.textscale = 0.1
        self.txtinfo = OnscreenText(
            scale = self.textscale,
            pos = (0.1, -0.1),
            text = "",
            align = TextNode.ALeft,
            fg = (0.1,1.0,0.15,1),
            bg = (0, 0, 0, 0),
            shadow = (0, 0, 0, 1),
            shadowOffset = (-0.02, -0.02))
        self.txtinfo.setTransparency(1)
        self.txtinfo.reparentTo(self.logFrame.getCanvas())

        # create a close Server button
        self.btnBackPos = Vec3(0.4, 0, 0.2)
        self.btnBackScale = 0.25
        self.btnBack = DirectButton(
            # Scale and position
            scale = self.btnBackScale,
            pos = self.btnBackPos,
            # Text
            text = "Quit Server",
            text_scale = 0.45,
            text_pos = (0, -0.1),
            text_fg = (0.82,0.85,0.87,1),
            text_shadow = (0, 0, 0, 1),
            text_shadowOffset = (-0.02, -0.02),
            text_font = defaultFont,
            # Frame
            geom = self.buttonGeom,
            frameColor = (0, 0, 0, 0),
            relief = 0,
            pressEffect = False,
            # Functionality
            command = self.back,
            rolloverSound = None,
            clickSound = None)
        self.btnBack.setTransparency(1)
        self.btnBack.reparentTo(base.a2dBottomLeft)

        # catch window resizes and recalculate the aspectration
        self.accept("window-event", self.recalcAspectRatio)
        self.accept("addLog", self.addLog)

    def show(self):
        self.logFrame.show()
        self.btnBack.show()

    def hide(self):
        self.logFrame.hide()
        self.btnBack.hide()

    def back(self):
        self.hide()
        base.messenger.send("stop_server")
        self.addLog("Quit Server!")

    def addLog(self, text):
        self.txtinfo.appendText(text + "\n")
        textbounds = self.txtinfo.getTightBounds()
        self.logFrame["canvasSize"] = (0, textbounds[1].getX(),
                                        textbounds[0].getZ(), 0)

    def recalcAspectRatio(self, window):
        """get the new aspect ratio to resize the mainframe"""
        # set the mainframe size to the window borders again
        self.logFrame["frameSize"] = (
            0, base.a2dRight * 2,
            (base.a2dBottom+.2) * 2, 0)
Example #2
0
class CraftInventory(DirectObject):
    def __init__(self):
        self.itemFrame = DirectScrolledFrame(
            text="Inventory",
            text_scale=0.25,
            text_pos=(0, 0.75, 0),
            text_bg=(1, 1, 1, 1),
            # make the frame occupy the whole window
            frameSize=(-0.6, 0.6, -0.8, 0.8),
            pos=(-0.7, 0, 0),
            # make the canvas as big as the frame
            canvasSize=(-0.6, 0.6, -0.8, 0.8),
            # set the frames color to white
            frameColor=(1, 1, 1, 1),
        )
        self.itemList = []

        self.craftFrame = DirectFrame(
            text="Crafting",
            text_scale=0.25,
            text_pos=(0, 0.75, 0),
            text_bg=(1, 1, 1, 1),
            # make the frame occupy the whole window
            frameSize=(-0.6, 0.6, -0.8, 0.8),
            pos=(0.7, 0, 0),
            # set the frames color to white
            frameColor=(1, 1, 1, 1),
        )
        self.craftDrop = DirectFrame(
            text="drop ore",
            text_scale=0.1,
            pos=(0, 0.4, 0),
            frameSize=(-0.25, 0.25, -0.25, 0.25),
            frameColor=(1, 0, 0, 1),
        )
        self.craftDrop.reparentTo(self.craftFrame)

    def show(self):
        self.itemFrame.show()
        self.craftFrame.show()

    def hide(self):
        self.itemFrame.hide()
        self.craftFrame.hide()

    def __makeItem(self, item):
        obj = DirectLabel(
            text=str(item.giveLoot()) + "x " + item.giveType(),
            text_scale=0.5,
            text_pos=(0, -1, 0),
            image=("item.png", "item_hover.png", "item.png"),  # normal, hover, disabled
            scale=0.16,
            numStates=2,
            state=DGG.NORMAL,
            relief=DGG.GROOVE,
        )
        obj.setTransparency(True)
        obj["activeState"] = 0
        obj.reparentTo(self.itemFrame.getCanvas())
        obj.bind(DGG.B1PRESS, self.dragStart, [obj])
        obj.bind(DGG.B1RELEASE, self.dragStop)
        obj.bind(DGG.ENTER, self.inObject, [obj])
        obj.bind(DGG.EXIT, self.outObject, [obj])
        return obj

    def updateList(self, items):
        for item in self.itemList:
            item.destroy()
        i = 1
        j = 0
        pad = 0.2
        numItemsPerRow = 2
        itemWidth = 0.32
        itemHeight = 0.32
        # left = -(itemWidth * (numItemsPerRow/2.0)) - 0.16
        left = self.itemFrame.getX()  # + (itemWidth + 0.16)
        xStep = itemWidth + 0.13
        yStep = -(itemHeight + 0.13)
        top = 0.8 - (0.16 + pad)
        for item in items:
            self.itemList.append(self.__makeItem(item))
            x = left + i * xStep
            y = top + j * yStep
            self.itemList[-1].setPos(x, 0.0, y)
            if i == numItemsPerRow:
                i = 0
                j += 1
            i += 1
        if i == 1:
            j -= 1
        height = ((j) * -yStep) - 0.16
        # resize the canvas. This will make the scrollbars dis-/appear,
        # dependent on if the canvas is bigger than the frame size.
        self.itemFrame["canvasSize"] = (-0.6, 0.6, -height, 0.8)

    def inObject(self, element, event):
        # Can be used to highlight objects
        element["activeState"] = 1
        element.setActiveState()
        # print "over object"

    def outObject(self, element, event):
        # Can be used to unhighlight objects
        # element["state"] = 0
        element["activeState"] = 0
        element.setActiveState()

    def dragStart(self, element, event):
        print "start drag"
        taskMgr.remove("dragDropTask")
        vWidget2render2d = element.getPos(render2d)
        vMouse2render2d = Point3(event.getMouse()[0], 0, event.getMouse()[1])
        editVec = Vec3(vWidget2render2d - vMouse2render2d)
        t = taskMgr.add(self.dragTask, "dragDropTask")
        element.reparentTo(aspect2d)
        t.element = element
        t.editVec = editVec
        t.elementStartPos = element.getPos()

    def dragTask(self, state):
        mwn = base.mouseWatcherNode
        if mwn.hasMouse():
            vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])
            newPos = vMouse2render2d + state.editVec
            state.element.setPos(render2d, newPos)
        return Task.cont

    def dragStop(self, event):
        print "stop drag with:", event
        isInArea = False

        t = taskMgr.getTasksNamed("dragDropTask")[0]
        # for dropArea in self.dropAreas:
        #    if self.isInBounds(event.getMouse(), dropArea["frameSize"], dropArea.getPos(render)):
        #        print "inside Area:", dropArea["text"], dropArea.getPos(render)
        #        isInArea = True
        #        t.element.setPos(dropArea.getPos(render))
        #        t.element.setX(t.element.getX() * self.ratio)
        #        #t.element.setX(t.element.getX() - t.element.getWidth() / 2.0)
        #        break

        if not isInArea:
            t.element.setPos(t.elementStartPos)
            t.element.reparentTo(self.itemFrame.getCanvas())
        taskMgr.remove("dragDropTask")

    def isInBounds(self, location, bounds, posShift=None):
        x = 0 if posShift is None else posShift.getX()
        y = 0 if posShift is None else posShift.getZ()

        left = x + bounds[0]
        right = x + bounds[1]
        bottom = y + bounds[2]
        top = y + bounds[3]

        # are we outside of the bounding box from the left
        if location[0] < left:
            return False
        # are we outside of the bounding box from the right
        if location[0] > right:
            return False
        # are we outside of the bounding box from the bottom
        if location[1] < bottom:
            return False
        # are we outside of the bounding box from the top
        if location[1] > top:
            return False
        # the location is inside the bbox
        return True
Example #3
0
class CraftInventory(DirectObject):
    def __init__(self):
        self.itemFrame = DirectScrolledFrame(
            text="Inventory",
            text_scale=0.25,
            text_pos=(0, 0.75, 0),
            text_bg=(1, 1, 1, 1),
            # make the frame occupy the whole window
            frameSize=(-0.6, 0.6, -0.8, 0.8),
            pos=(-0.7, 0, 0),
            # make the canvas as big as the frame
            canvasSize=(-0.6, 0.6, -0.8, 0.8),
            # set the frames color to white
            frameColor=(1, 1, 1, 1))
        self.itemList = []

        self.craftFrame = DirectFrame(
            text="Crafting",
            text_scale=0.25,
            text_pos=(0, 0.75, 0),
            text_bg=(1, 1, 1, 1),
            # make the frame occupy the whole window
            frameSize=(-0.6, 0.6, -0.8, 0.8),
            pos=(0.7, 0, 0),
            # set the frames color to white
            frameColor=(1, 1, 1, 1))
        self.craftDrop = DirectFrame(text="drop ore",
                                     text_scale=0.1,
                                     pos=(0, 0.4, 0),
                                     frameSize=(-0.25, 0.25, -0.25, 0.25),
                                     frameColor=(1, 0, 0, 1))
        self.craftDrop.reparentTo(self.craftFrame)

    def show(self):
        self.itemFrame.show()
        self.craftFrame.show()

    def hide(self):
        self.itemFrame.hide()
        self.craftFrame.hide()

    def __makeItem(self, item):
        obj = DirectLabel(
            text=str(item.giveLoot()) + "x " + item.giveType(),
            text_scale=0.5,
            text_pos=(0, -1, 0),
            image=("item.png", "item_hover.png",
                   "item.png"),  #normal, hover, disabled
            scale=0.16,
            numStates=2,
            state=DGG.NORMAL,
            relief=DGG.GROOVE)
        obj.setTransparency(True)
        obj["activeState"] = 0
        obj.reparentTo(self.itemFrame.getCanvas())
        obj.bind(DGG.B1PRESS, self.dragStart, [obj])
        obj.bind(DGG.B1RELEASE, self.dragStop)
        obj.bind(DGG.ENTER, self.inObject, [obj])
        obj.bind(DGG.EXIT, self.outObject, [obj])
        return obj

    def updateList(self, items):
        for item in self.itemList:
            item.destroy()
        i = 1
        j = 0
        pad = 0.2
        numItemsPerRow = 2
        itemWidth = 0.32
        itemHeight = 0.32
        #left = -(itemWidth * (numItemsPerRow/2.0)) - 0.16
        left = self.itemFrame.getX()  # + (itemWidth + 0.16)
        xStep = itemWidth + 0.13
        yStep = -(itemHeight + 0.13)
        top = 0.8 - (0.16 + pad)
        for item in items:
            self.itemList.append(self.__makeItem(item))
            x = left + i * xStep
            y = top + j * yStep
            self.itemList[-1].setPos(x, 0.0, y)
            if i == numItemsPerRow:
                i = 0
                j += 1
            i += 1
        if i == 1: j -= 1
        height = ((j) * -yStep) - 0.16
        # resize the canvas. This will make the scrollbars dis-/appear,
        # dependent on if the canvas is bigger than the frame size.
        self.itemFrame["canvasSize"] = (-0.6, 0.6, -height, 0.8)

    def inObject(self, element, event):
        # Can be used to highlight objects
        element["activeState"] = 1
        element.setActiveState()
        #print "over object"

    def outObject(self, element, event):
        # Can be used to unhighlight objects
        #element["state"] = 0
        element["activeState"] = 0
        element.setActiveState()

    def dragStart(self, element, event):
        print "start drag"
        taskMgr.remove('dragDropTask')
        vWidget2render2d = element.getPos(render2d)
        vMouse2render2d = Point3(event.getMouse()[0], 0, event.getMouse()[1])
        editVec = Vec3(vWidget2render2d - vMouse2render2d)
        t = taskMgr.add(self.dragTask, 'dragDropTask')
        element.reparentTo(aspect2d)
        t.element = element
        t.editVec = editVec
        t.elementStartPos = element.getPos()

    def dragTask(self, state):
        mwn = base.mouseWatcherNode
        if mwn.hasMouse():
            vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])
            newPos = vMouse2render2d + state.editVec
            state.element.setPos(render2d, newPos)
        return Task.cont

    def dragStop(self, event):
        print "stop drag with:", event
        isInArea = False

        t = taskMgr.getTasksNamed('dragDropTask')[0]
        #for dropArea in self.dropAreas:
        #    if self.isInBounds(event.getMouse(), dropArea["frameSize"], dropArea.getPos(render)):
        #        print "inside Area:", dropArea["text"], dropArea.getPos(render)
        #        isInArea = True
        #        t.element.setPos(dropArea.getPos(render))
        #        t.element.setX(t.element.getX() * self.ratio)
        #        #t.element.setX(t.element.getX() - t.element.getWidth() / 2.0)
        #        break

        if not isInArea:
            t.element.setPos(t.elementStartPos)
            t.element.reparentTo(self.itemFrame.getCanvas())
        taskMgr.remove('dragDropTask')

    def isInBounds(self, location, bounds, posShift=None):
        x = 0 if posShift is None else posShift.getX()
        y = 0 if posShift is None else posShift.getZ()

        left = x + bounds[0]
        right = x + bounds[1]
        bottom = y + bounds[2]
        top = y + bounds[3]

        # are we outside of the bounding box from the left
        if location[0] < left: return False
        # are we outside of the bounding box from the right
        if location[0] > right: return False
        # are we outside of the bounding box from the bottom
        if location[1] < bottom: return False
        # are we outside of the bounding box from the top
        if location[1] > top: return False
        # the location is inside the bbox
        return True
Example #4
0
class DeviceMonitor(DirectObject):
    def __init__(self, device):
        super().__init__()
        self.device = device
        self.create_panel()
        self.activate()
        self.hide()

    def activate(self):
        print("Device connected")
        print("  Name        : {}".format(self.device.name))
        print("  Type        : {}".format(self.device.device_class.name))
        print("  Manufacturer: {}".format(self.device.manufacturer))
        print("  ID          : {:04x}:{:04x}".format(self.device.vendor_id,
                                                     self.device.product_id))
        axis_names = [axis.axis.name for axis in self.device.axes]
        print("  Axes        : {} ({})".format(len(self.device.axes),
                                               ', '.join(axis_names)))
        button_names = [button.handle.name for button in self.device.buttons]
        print("  Buttons     : {} ({})".format(len(self.device.buttons),
                                               ', '.join(button_names)))

        base.attachInputDevice(self.device)

        self.task = base.taskMgr.add(
            self.update,
            "Monitor for {}".format(self.device.name),
            sort=10,
        )

    def deactivate(self):
        print("\"{}\" disconnected".format(self.device.name))
        base.taskMgr.remove(self.task)
        self.panel.detach_node()

    def create_panel(self):
        panel_width = base.a2dLeft * -0.25 + base.a2dRight
        scroll_bar_width = 0.08
        # NOTE: -0.001 because thanks to inaccuracy the vertical bar appears...
        canvas_width = panel_width - scroll_bar_width - 0.001
        canvas_height = base.a2dBottom - base.a2dTop

        self.panel = DirectScrolledFrame(
            frameSize=VBase4(
                0,
                panel_width,
                canvas_height,
                0,
            ),
            frameColor=VBase4(0.8, 0.8, 0.8, 1),
            canvasSize=VBase4(
                0,
                canvas_width,
                canvas_height,
                0,
            ),
            scrollBarWidth=scroll_bar_width,
            manageScrollBars=True,
            autoHideScrollBars=True,
            pos=(base.a2dLeft * 0.25, 0, base.a2dTop),
            parent=base.aspect2d,
        )
        panel_canvas = self.panel.getCanvas()
        offset = -0.0

        # Style sheets

        half_width_entry = dict(
            frameSize=VBase4(
                0,
                canvas_width / 2,
                -0.1,
                0,
            ),
            parent=panel_canvas,
            frameColor=VBase4(0.8, 0.8, 0.8, 1),
        )
        left_aligned_small_text = dict(
            text_align=TextNode.ALeft,
            text_scale=0.05,
            text_fg=VBase4(0,0,0,1),
            text_pos=(0.05, -0.06),
        )
        half_width_text_frame = dict(
            **half_width_entry,
            **left_aligned_small_text,
        )

        header = dict(
            frameSize=VBase4(
                0,
                canvas_width,
                -0.1,
                0,
            ),
            parent=panel_canvas,
            frameColor=VBase4(0.6, 0.6, 0.6, 1),
            text_align=TextNode.ALeft,
            text_scale=0.1,
            text_fg=VBase4(0,0,0,1),
            text_pos=(0.05, -0.075),
        )

        # Basic device data (name, device class, manufacturer, USB ID)

        self.device_header = DirectLabel(
            text="Device data",
            pos=(0, 0, offset),
            **header,
        )
        offset -= 0.1

        def add_data_entry(offset, label, text):
            self.name = DirectLabel(
                text=label,
                pos=(0, 0, offset),
                **half_width_text_frame,
            )
            self.name = DirectLabel(
                text=text,
                pos=(canvas_width / 2, 0, offset),
                **half_width_text_frame,
            )

        metadata = [
            ('Name', self.device.name),
            ('Device class', self.device.device_class.name),
            ('Manufacturer', self.device.manufacturer),
            ('USB ID',
             "{:04x}:{:04x}".format(
                 self.device.vendor_id,
                 self.device.product_id,
             ),
            ),
        ]
        for label, text in metadata:
            add_data_entry(offset, label, text)
            offset -= 0.1

        # Axes

        self.axis_sliders = []
        if len(self.device.axes) > 0:
            offset -= 0.1
            self.axes_header = DirectLabel(
                text="Axes",
                pos=(0, 0, offset),
                **header,
            )
            offset -= 0.1

            def add_axis(offset, axis_name):
                slider_width = canvas_width / 2
                label = DirectLabel(
                    text=axis_name,
                    **left_aligned_small_text,
                    pos=(0.05, 0, offset),
                    parent=panel_canvas,
                )
                slider = DirectSlider(
                    value=0.0,
                    range=(-1.0, 1.0),
                    state=DGG.DISABLED,
                    frameSize=VBase4(
                        0,
                        slider_width,
                        -0.1,
                        0,
                    ),
                    thumb_frameSize=VBase4(
                        0.0,
                        0.04,
                        -0.04,
                        0.04),
                    frameColor=VBase4(0.3, 0.3, 0.3, 1),
                    pos=(canvas_width - slider_width, 0, offset),
                    parent=panel_canvas,
                )
                return slider

            for axis in self.device.axes:
                axis_slider = add_axis(offset, axis.axis.name)
                self.axis_sliders.append(axis_slider)
                offset -= 0.1

        # Buttons

        self.button_buttons = []
        if len(self.device.buttons) > 0:
            offset -= 0.1
            self.buttons_header = DirectLabel(
                text="Buttons",
                pos=(0, 0, offset),
                **header,
            )
            offset -= 0.1

            def add_button(offset, button_name):
                button_width = canvas_width / 2
                label = DirectLabel(
                    text=button_name,
                    **left_aligned_small_text,
                    pos=(0.05, 0, offset),
                    parent=panel_canvas,
                )
                button = DirectFrame(
                    frameSize=VBase4(
                        0,
                        button_width,
                        -0.1,
                        0,
                    ),
                    text="",
                    text_align=TextNode.ACenter,
                    text_scale=0.05,
                    text_fg=VBase4(0,0,0,1),
                    text_pos=(button_width / 2, -0.06),
                    frameColor=VBase4(0.3, 0.3, 0.3, 1),
                    pos=(canvas_width - button_width, 0, offset),
                    parent=panel_canvas,
                )
                return button

            for i in range(len(self.device.buttons)):
                button_name = self.device.buttons[i].handle.name
                button_button = add_button(offset, button_name)
                self.button_buttons.append(button_button)
                offset -= 0.1

        # Vibration

        self.vibration = []
        if self.device.has_feature(InputDevice.Feature.vibration):
            offset -= 0.1
            self.vibration_header = DirectLabel(
                text="Vibration",
                pos=(0, 0, offset),
                **header,
            )
            offset -= 0.1

            def add_vibration(offset, axis_name, index):
                slider_width = canvas_width / 2
                label = DirectLabel(
                    text=axis_name,
                    **left_aligned_small_text,
                    pos=(0.05, 0, offset),
                    parent=panel_canvas,
                )
                slider = DirectSlider(
                    value=0.0,
                    range=(0.0, 1.0),
                    command=self.update_vibration,
                    frameSize=VBase4(
                        0,
                        slider_width,
                        -0.1,
                        0,
                    ),
                    thumb_frameSize=VBase4(
                        0.0,
                        0.04,
                        -0.04,
                        0.04),
                    frameColor=VBase4(0.3, 0.3, 0.3, 1),
                    pos=(canvas_width - slider_width, 0, offset),
                    parent=panel_canvas,
                )
                return slider

            for index, name in enumerate(["low frequency", "high frequency"]):
                self.vibration.append(add_vibration(offset, name, index))
                offset -= 0.1

        # Resize the panel's canvas to the widgets actually in it.
        if -offset > -canvas_height:
            self.panel['canvasSize'] = VBase4(
                0,
                canvas_width,
                offset,
                0,
            )
        self.panel.setCanvasSize()

    def show(self):
        # FIXME: Activate update task here, and deactivate it in hide()?
        self.panel.show()

    def hide(self):
        self.panel.hide()

    def update_vibration(self):
        low = self.vibration[0]['value']
        high = self.vibration[1]['value']
        self.device.set_vibration(low, high)

    def update(self, task):
        # FIXME: There needs to be a demo of events here, too.
        for idx, slider in enumerate(self.axis_sliders):
            slider["value"] = self.device.axes[idx].value
        for idx, button in enumerate(self.button_buttons):
            if self.device.buttons[idx].known:
                if self.device.buttons[idx].pressed:
                    button['frameColor'] = VBase4(0.0, 0.8, 0.0, 1)
                    button['text'] = "down"
                else:
                    button['frameColor'] = VBase4(0.3, 0.3, 0.3, 1)
                    button['text'] = "up"
            else:
                # State is InputDevice.S_unknown. This happens if the device
                # manager hasn't polled yet, and in some cases before a button
                # has been pressed after the program's start.
                button['frameColor'] = VBase4(0.8, 0.8, 0.0, 1)
                button['text'] = "unknown"
        return task.cont
class ScrolledItemSelector(DirectObject):
    '''Touch optimised list that holds a set of items. By clicking on one you select it and return
    its Value with get_selected(). To add an item simply use add_item(). You can also pass a
    function with the 'command' field that gets called on a selection switch'''
    def __init__(self,
                 frame_size=(1, 1.5),
                 frame_color=(0.2, 0.2, 0.2, 0.8),
                 pos=(0, 0, 0),
                 item_v_padding=0.02,
                 item_h_padding=0.04,
                 item_scale=1,
                 item_side_ratio=0.2,
                 item_background=(0.3, 0.3, 0.3, 1),
                 item_background_active=(0.6, 0.6, 0.6, 1),
                 command=lambda: None):

        self.f_x, self.f_y = frame_size
        self.c_x = self.f_x
        self.c_y = self.f_y

        self.item_v_padding = item_v_padding
        self.item_h_padding = item_h_padding
        self.item_scale = item_scale
        self.item_background = item_background
        self.item_background_active = item_background_active

        self.frame = DirectScrolledFrame(
            frameSize=(-self.f_x / 2, self.f_x / 2, -self.f_y / 2,
                       self.f_y / 2),
            canvasSize=(-self.c_x / 2, self.c_x / 2, -self.c_y / 2,
                        self.c_y / 2),
            frameColor=frame_color,
            pos=pos,
            scrollBarWidth=(0),
            manageScrollBars=False,
            state=DGG.NORMAL)

        self.frame.verticalScroll.destroy()
        self.frame.horizontalScroll.destroy()
        self.frame.bind(DGG.WITHIN, self._start_listening)
        self.frame.bind(DGG.WITHOUT, self._stop_listening)

        self.canvas = self.frame.getCanvas()

        self.i_x = self.f_x * (1 - item_h_padding)
        self.i_y = self.f_y * item_side_ratio
        self.i_size = (-self.i_x / 2, self.i_x / 2, -self.i_y / 2,
                       self.i_y / 2)

        self.i_start = self.c_y / 2 + self.i_y / 2

        self.active_item = None
        self.item_list = []
        self.value_list = []

        self.start_mY = None
        self.old_mY = None
        self.m_diff = 0
        self.is_scrolling = False
        self.command = command

        # messenger.toggleVerbose()

    def _start_listening(self, watcher):
        print('start listening')
        self.accept("mouse1", self._start_scroll)
        self.accept("mouse1-up", self._stop_scroll)

    def _stop_listening(self, watcher):
        print('stop listening')
        self.ignore("mouse1")
        self.ignore("mouse1-up")
        self._stop_scroll()

    def _switch_active_item(self, item, watcher):
        if not self.is_scrolling:
            print('switched')
            if self.active_item is not None:
                self.active_item['frameColor'] = self.item_background
            item['frameColor'] = self.item_background_active
            self.active_item = item
            self.command()

    def _start_scroll(self):
        n = len(self.item_list)
        content_length = (
            (n * (self.i_y + self.item_v_padding))
            +  # Size of all elements with padding
            self.item_v_padding)  # Add one padding for the bottom

        if content_length > self.c_y:
            taskMgr.doMethodLater(0.01, self._scroll_task, 'scroll_task')
            self.start_mY = base.mouseWatcherNode.getMouse().getY()
            self.old_mY = self.start_mY

    def _stop_scroll(self):
        taskMgr.remove('scroll_task')
        taskMgr.doMethodLater(0.01, self._scroll_fade_task, 'scrollfadetask')
        self.is_scrolling = False

    def _scroll_task(self, task):
        mY = base.mouseWatcherNode.getMouse().getY()
        old_c = self.canvas.getZ()
        self.m_diff = self.old_mY - mY
        n = len(self.item_list)

        if self.m_diff != 0:
            self.is_scrolling = True

        self.c_scroll_start = 0
        self.c_scroll_stop = (
            (n * (self.i_y + self.item_v_padding))
            +  # Size of all elements with padding
            self.item_v_padding -  # Add one padding for the bottom
            self.f_y)  # Substract the length of the canvas
        self.c_new_pos = (old_c - self.m_diff)

        hits_not_upper_bound = self.c_new_pos >= self.c_scroll_start
        hits_not_lower_bound = self.c_new_pos <= self.c_scroll_stop

        # print('canvas : ' + str(self.canvas.getZ()))

        if hits_not_upper_bound and hits_not_lower_bound:
            self.canvas.setZ(self.c_new_pos)
        elif not hits_not_upper_bound:
            self.canvas.setZ(self.c_scroll_start)
        elif not hits_not_lower_bound:
            self.canvas.setZ(self.c_scroll_stop)

        self.old_mY = mY
        return task.again

    def _scroll_fade_task(self, task):
        if self.m_diff is None or abs(self.m_diff) < 0.005:
            self.m_diff = 0
            return task.done

        old_c = self.canvas.getZ()
        n = len(self.item_list)
        self.c_scroll_start = 0
        self.c_scroll_stop = (
            (n * (self.i_y + self.item_v_padding))
            +  # Size of all elements with padding
            self.item_v_padding -  # Add one padding for the bottom
            self.c_y)  # Substract the length of the canvas

        hits_not_upper_bound = (old_c - self.m_diff) >= self.c_scroll_start
        hits_not_lower_bound = (old_c - self.m_diff) <= self.c_scroll_stop

        if hits_not_upper_bound and hits_not_lower_bound:
            self.canvas.setZ(old_c - self.m_diff)
            self.m_diff *= 0.85
            return task.again
        elif not hits_not_upper_bound:
            self.canvas.setZ(self.c_scroll_start)
            self.m_diff = 0
            return task.done
        elif not hits_not_lower_bound:
            self.canvas.setZ(self.c_scroll_stop)
            self.m_diff = 0
            return task.done

    def add_item(self,
                 image=None,
                 image_scale=0.15,
                 image_pos=(0, 0, 0),
                 title=None,
                 title_pos=(0, 0, 0),
                 text=None,
                 text_pos=(0, 0, 0),
                 value=None):
        '''Appends an item to the end of the list. It can hold an image,
        title and text. Image: Panda3d path eg. 'models/picture.jpg'.
        Value: The item has function 'get/set_value()' to work with individual
        values of an activated element. Value gets set to the item on adding it.'''

        item_nr = len(self.item_list) + 1
        item_pos = self.i_start - (self.i_y + self.item_v_padding) * item_nr

        item = DirectFrame(
            parent=self.canvas,
            text=str(item_nr),  # Abused as an ID tag
            text_fg=(0, 0, 0, 0),
            frameSize=self.i_size,
            frameColor=self.item_background,
            borderWidth=(0.01, 0.01),
            pos=(0, 0, item_pos),
            relief=DGG.FLAT,
            state=DGG.NORMAL,
            enableEdit=0,
            suppressMouse=0)
        item.bind(DGG.B1RELEASE, self._switch_active_item, [item])

        if image is not None:
            OnscreenImage(  # Add an Image
                image=image,
                pos=image_pos,
                scale=(1 * image_scale, 1, 1 * image_scale),
                parent=(item))

        DirectLabel(
            parent=item,  # Add a Title
            text=title,
            text_scale=self.i_y / 4,
            text_fg=(1, 1, 1, 1),
            text_align=TextNode.ALeft,
            frameColor=(0, 0, 0, 0),
            pos=title_pos)

        DirectLabel(
            parent=item,  # Add a Text
            text=text,
            text_scale=self.i_y / 5,
            text_fg=(1, 1, 1, 1),
            text_align=TextNode.ALeft,
            frameColor=(0, 0, 0, 0),
            pos=text_pos)

        self.item_list.append(item)
        self.value_list.append(value)

    def get_active_item(self):
        return self.active_item

    def set_active_item(self, pos):
        self._switch_active_item(self.item_list[pos], None)

    def get_active_id(self):
        return int(self.active_item['text'])

    def get_active_value(self):
        return self.value_list[int(self.active_item['text']) - 1]

    def hide(self):
        '''Triggers the DirectFrame.hide() of the main frame'''

        self.frame.hide()

    def show(self):
        '''Triggers the DirectFrame.show() of the main frame'''

        self.frame.show()

    def clear(self):
        '''Destroys every item that was added to the list'''

        for item in self.item_list:
            item.destroy()
        self.item_list = []
        self.active_item = None
        self.value_list = []
        self.canvas.setZ(0)

    def destroy(self):
        '''Destroys the whole list and every item in it'''

        self.canvas.destroy()
        self.frame.destroy()
Example #6
0
class ScrolledButtonsList(DirectObject):
    """
       A class to display a list of selectable buttons.
       It is displayed using scrollable window (DirectScrolledFrame).
    """
    def __init__(self, parent=None, frameSize=(.8,1.2), buttonTextColor=(1,1,1,1),
                 font=None, itemScale=.045, itemTextScale=0.85, itemTextZ=0,
                 command=None, contextMenu=None, autoFocus=0,
                 colorChange=1, colorChangeDuration=1, newItemColor=globals.colors['guiblue1'],
                 rolloverColor=globals.colors['guiyellow'],
                 suppressMouseWheel=1, modifier='control'):
        self.mode = None
        self.focusButton=None
        self.command=command
        self.contextMenu=contextMenu
        self.autoFocus=autoFocus
        self.colorChange=colorChange
        self.colorChangeDuration=colorChangeDuration*.5
        self.newItemColor=newItemColor
        self.rolloverColor=rolloverColor
        self.rightClickTextColors=(Vec4(0,1,0,1),Vec4(0,35,100,1))
        self.font=font
        if font:
           self.fontHeight=font.getLineHeight()
        else:
           self.fontHeight=TextNode.getDefaultFont().getLineHeight()
        self.fontHeight*=1.2 # let's enlarge font height a little
        self.xtraSideSpace=.2*self.fontHeight
        self.itemTextScale=itemTextScale
        self.itemTextZ=itemTextZ
        self.buttonTextColor=buttonTextColor
        self.suppressMouseWheel=suppressMouseWheel
        self.modifier=modifier
        self.buttonsList=[]
        self.numItems=0
        self.__eventReceivers={}
        # DirectScrolledFrame to hold items
        self.itemScale=itemScale
        self.itemVertSpacing=self.fontHeight*self.itemScale
        self.frameWidth,self.frameHeight=frameSize
        # I set canvas' Z size smaller than the frame to avoid the auto-generated vertical slider bar
        self.childrenFrame = DirectScrolledFrame(
                     parent=parent,pos=(-self.frameWidth*.5,0,.5*self.frameHeight), relief=DGG.GROOVE,
                     state=DGG.NORMAL, # to create a mouse watcher region
                     frameSize=(0, self.frameWidth, -self.frameHeight, 0), frameColor=(0,0,0,.7),
                     canvasSize=(0, 0, -self.frameHeight*.5, 0), borderWidth=(0.01,0.01),
                     manageScrollBars=0, enableEdit=0, suppressMouse=0, sortOrder=1000 )
        # the real canvas is "self.childrenFrame.getCanvas()",
        # but if the frame is hidden since the beginning,
        # no matter how I set the canvas Z pos, the transform would be resistant,
        # so just create a new node under the canvas to be my canvas
        self.canvas=self.childrenFrame.getCanvas().attachNewNode('myCanvas')
        # slider background
        SliderBG=DirectFrame( parent=self.childrenFrame,frameSize=(-.025,.025,-self.frameHeight,0),
                     frameColor=(0,0,0,.7), pos=(-.03,0,0),enableEdit=0, suppressMouse=0)
        # slider thumb track
        sliderTrack = DirectFrame( parent=SliderBG, relief=DGG.FLAT, #state=DGG.NORMAL,
                     frameColor=(1,1,1,.2), frameSize=(-.015,.015,-self.frameHeight+.01,-.01),
                     enableEdit=0, suppressMouse=0)
        # page up
        self.pageUpRegion=DirectFrame( parent=SliderBG, relief=DGG.FLAT, state=DGG.NORMAL,
                     frameColor=(1,.8,.2,.1), frameSize=(-.015,.015,0,0),
                     enableEdit=0, suppressMouse=0)
        self.pageUpRegion.setAlphaScale(0)
        self.pageUpRegion.bind(DGG.B1PRESS,self.__startScrollPage,[-1])
        self.pageUpRegion.bind(DGG.WITHIN,self.__continueScrollUp)
        self.pageUpRegion.bind(DGG.WITHOUT,self.__suspendScrollUp)
        # page down
        self.pageDnRegion=DirectFrame( parent=SliderBG, relief=DGG.FLAT, state=DGG.NORMAL,
                     frameColor=(1,.8,.2,.1), frameSize=(-.015,.015,0,0),
                     enableEdit=0, suppressMouse=0)
        self.pageDnRegion.setAlphaScale(0)
        self.pageDnRegion.bind(DGG.B1PRESS,self.__startScrollPage,[1])
        self.pageDnRegion.bind(DGG.WITHIN,self.__continueScrollDn)
        self.pageDnRegion.bind(DGG.WITHOUT,self.__suspendScrollDn)
        self.pageUpDnSuspended=[0,0]
        # slider thumb
        self.vertSliderThumb=DirectButton(parent=SliderBG, relief=DGG.FLAT,
                     frameColor=(1,1,1,.6), frameSize=(-.015,.015,0,0),
                     enableEdit=0, suppressMouse=0, rolloverSound=None, clickSound=None)
        self.vertSliderThumb.bind(DGG.B1PRESS,self.__startdragSliderThumb)
        self.vertSliderThumb.bind(DGG.WITHIN,self.__enteringThumb)
        self.vertSliderThumb.bind(DGG.WITHOUT,self.__exitingThumb)
        self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
        self.sliderThumbDragPrefix='draggingSliderThumb-'
        # GOD & I DAMN IT !!!
        # These things below don't work well if the canvas has a lot of buttons.
        # So I end up checking the mouse region every frame by myself using a continuous task.
  #       self.accept(DGG.WITHIN+self.childrenFrame.guiId,self.__enteringFrame)
  #       self.accept(DGG.WITHOUT+self.childrenFrame.guiId,self.__exitingFrame)
        self.isMouseInRegion=False
        self.mouseOutInRegionCommand=(self.__exitingFrame,self.__enteringFrame)
        taskMgr.doMethodLater(.2,self.__getFrameRegion,'getFrameRegion')
  
    def __getFrameRegion(self,t):
        for g in range(base.mouseWatcherNode.getNumGroups()):
            region=base.mouseWatcherNode.getGroup(g).findRegion(self.childrenFrame.guiId)
            if region!=None:
               self.frameRegion=region
               taskMgr.add(self.__mouseInRegionCheck,'mouseInRegionCheck')
               break
  
    def __mouseInRegionCheck(self,t):
        """
           check if the mouse is within or without the scrollable frame, and
           upon within or without, run the provided command
        """
        if not base.mouseWatcherNode.hasMouse(): return Task.cont
        m=base.mouseWatcherNode.getMouse()
        bounds=self.frameRegion.getFrame()
        inRegion=bounds[0]<m[0]<bounds[1] and bounds[2]<m[1]<bounds[3]
        if self.isMouseInRegion==inRegion: return Task.cont
        self.isMouseInRegion=inRegion
        self.mouseOutInRegionCommand[inRegion]()
        return Task.cont
  
    def __startdragSliderThumb(self,m=None):
        if self.mode != None:
            if hasattr(self.mode, 'enableMouseCamControl') == 1:
                if self.mode.enableMouseCamControl == 1:
                    self.mode.game.app.disableMouseCamControl()
        mpos=base.mouseWatcherNode.getMouse()
        parentZ=self.vertSliderThumb.getParent().getZ(render2d)
        sliderDragTask=taskMgr.add(self.__dragSliderThumb,'dragSliderThumb')
        sliderDragTask.ZposNoffset=mpos[1]-self.vertSliderThumb.getZ(render2d)+parentZ
  #       sliderDragTask.mouseX=base.winList[0].getPointer(0).getX()
        self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
        base.buttonThrowers[0].node().setPrefix(self.sliderThumbDragPrefix)
        self.acceptOnce(self.sliderThumbDragPrefix+'mouse1-up',self.__stopdragSliderThumb)
  
    def __dragSliderThumb(self,t):
        if not base.mouseWatcherNode.hasMouse():
           return
        mpos=base.mouseWatcherNode.getMouse()
  #       newY=base.winList[0].getPointer(0).getY()
        self.__updateCanvasZpos((t.ZposNoffset-mpos[1])/self.canvasRatio)
  #       base.winList[0].movePointer(0, t.mouseX, newY)
        return Task.cont
  
    def __stopdragSliderThumb(self,m=None):
        if self.mode != None:
            if hasattr(self.mode, 'enableMouseCamControl') == 1:
                if self.mode.enableMouseCamControl == 1:
                    self.mode.game.app.enableMouseCamControl()
        taskMgr.remove('dragSliderThumb')
        self.__stopScrollPage()
        base.buttonThrowers[0].node().setPrefix(self.oldPrefix)
        if self.isMouseInRegion:
           self.mouseOutInRegionCommand[self.isMouseInRegion]()
  
    def __startScrollPage(self,dir,m):
        self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
        base.buttonThrowers[0].node().setPrefix(self.sliderThumbDragPrefix)
        self.acceptOnce(self.sliderThumbDragPrefix+'mouse1-up',self.__stopdragSliderThumb)
        t=taskMgr.add(self.__scrollPage,'scrollPage',extraArgs=[int((dir+1)*.5),dir*.01/self.canvasRatio])
        self.pageUpDnSuspended=[0,0]
  
    def __scrollPage(self,dir,scroll):
        if not self.pageUpDnSuspended[dir]:
           self.__scrollCanvas(scroll)
        return Task.cont
  
    def __stopScrollPage(self,m=None):
        taskMgr.remove('scrollPage')
  
    def __suspendScrollUp(self,m=None):
        self.pageUpRegion.setAlphaScale(0)
        self.pageUpDnSuspended[0]=1
    def __continueScrollUp(self,m=None):
        if taskMgr.hasTaskNamed('dragSliderThumb'):
           return
        self.pageUpRegion.setAlphaScale(1)
        self.pageUpDnSuspended[0]=0
   
    def __suspendScrollDn(self,m=None):
        self.pageDnRegion.setAlphaScale(0)
        self.pageUpDnSuspended[1]=1
    def __continueScrollDn(self,m=None):
        if taskMgr.hasTaskNamed('dragSliderThumb'):
           return
        self.pageDnRegion.setAlphaScale(1)
        self.pageUpDnSuspended[1]=0
  
    def __suspendScrollPage(self,m=None):
        self.__suspendScrollUp()
        self.__suspendScrollDn()
   
    def __enteringThumb(self,m=None):
        self.vertSliderThumb['frameColor']=(1,1,1,1)
        self.__suspendScrollPage()
  
    def __exitingThumb(self,m=None):
        self.vertSliderThumb['frameColor']=(1,1,1,.6)
  
    def __scrollCanvas(self,scroll):
        if self.vertSliderThumb.isHidden() or self.buttonsList == []:
           return
        self.__updateCanvasZpos(self.canvas.getZ()+scroll)
  
    def __updateCanvasZpos(self,Zpos):
        newZ=clampScalar(Zpos, .0, self.canvasLen-self.frameHeight+.015)
        self.canvas.setZ(newZ)
        thumbZ=-newZ*self.canvasRatio
        self.vertSliderThumb.setZ(thumbZ)
        self.pageUpRegion['frameSize']=(-.015,.015,thumbZ-.01,-.01)
        self.pageDnRegion['frameSize']=(-.015,.015,-self.frameHeight+.01,thumbZ+self.vertSliderThumb['frameSize'][2])
  
    def __adjustCanvasLength(self,numItem):
        self.canvasLen=float(numItem)*self.itemVertSpacing
        self.canvasRatio=(self.frameHeight-.015)/(self.canvasLen+.01)
        if self.canvasLen<=self.frameHeight-.015:
           canvasZ=.0
           self.vertSliderThumb.hide()
           self.pageUpRegion.hide()
           self.pageDnRegion.hide()
           self.canvasLen=self.frameHeight-.015
        else:
           canvasZ=self.canvas.getZ()
           self.vertSliderThumb.show()
           self.pageUpRegion.show()
           self.pageDnRegion.show()
        self.__updateCanvasZpos(canvasZ)
        self.vertSliderThumb['frameSize']=(-.015,.015,-self.frameHeight*self.canvasRatio,-.01)
        thumbZ=self.vertSliderThumb.getZ()
        self.pageUpRegion['frameSize']=(-.015,.015,thumbZ-.01,-.01)
        self.pageDnRegion['frameSize']=(-.015,.015,-self.frameHeight+.01,thumbZ+self.vertSliderThumb['frameSize'][2])
  
    def __acceptAndIgnoreWorldEvent(self,event,command,extraArgs=[]):
        receivers=messenger.whoAccepts(event)
        if receivers is None:
           self.__eventReceivers[event]={}
        else:
           self.__eventReceivers[event]=receivers.copy()
        for r in self.__eventReceivers[event].keys():
            if type(r) != types.TupleType:
                r.ignore(event)
        self.accept(event,command,extraArgs)
  
    def __ignoreAndReAcceptWorldEvent(self,events):
        for event in events:
            self.ignore(event)
            if self.__eventReceivers.has_key(event):
               for r, method_xtraArgs_persist in self.__eventReceivers[event].items():
                   if type(r) != types.TupleType:
                       messenger.accept(event,r,*method_xtraArgs_persist)
            self.__eventReceivers[event]={}
  
    def __enteringFrame(self,m=None):
        # sometimes the WITHOUT event for page down region doesn't fired,
        # so directly suspend the page scrolling here
        self.__suspendScrollPage()
        BTprefix=base.buttonThrowers[0].node().getPrefix()
        if BTprefix==self.sliderThumbDragPrefix:
           return
        self.inOutBTprefix=BTprefix
        if self.suppressMouseWheel:
           self.__acceptAndIgnoreWorldEvent(self.inOutBTprefix+'wheel_up',
                command=self.__scrollCanvas, extraArgs=[-.07])
           self.__acceptAndIgnoreWorldEvent(self.inOutBTprefix+'wheel_down',
                command=self.__scrollCanvas, extraArgs=[.07])
        else:
           self.accept(self.inOutBTprefix+self.modifier+'-wheel_up',self.__scrollCanvas, [-.07])
           self.accept(self.inOutBTprefix+self.modifier+'-wheel_down',self.__scrollCanvas, [.07])
  
    def __exitingFrame(self,m=None):
        if not hasattr(self,'inOutBTprefix'):
           return
        if self.suppressMouseWheel:
           self.__ignoreAndReAcceptWorldEvent( (
                                               self.inOutBTprefix+'wheel_up',
                                               self.inOutBTprefix+'wheel_down',
                                               ) )
        else:
           self.ignore(self.inOutBTprefix+self.modifier+'-wheel_up')
           self.ignore(self.inOutBTprefix+self.modifier+'-wheel_down')
  
    def __setFocusButton(self,button,item):
        if self.focusButton:
           self.restoreNodeButton2Normal()
        self.focusButton=button
        self.highlightNodeButton()
        if callable(self.command) and button in self.buttonsList:
           # run user command and pass the selected item, it's index, and the button
           self.command(item,self.buttonsList.index(button),button)
  
    def __rightPressed(self,button,m):
        self.__isRightIn=True
  #       text0 : normal
  #       text1 : pressed
  #       text2 : rollover
  #       text3 : disabled
        button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rightClickTextColors[self.focusButton==button])
        button.bind(DGG.B3RELEASE,self.__rightReleased,[button])
        button.bind(DGG.WITHIN,self.__rightIn,[button])
        button.bind(DGG.WITHOUT,self.__rightOut,[button])
  
    def __rightIn(self,button,m):
        self.__isRightIn=True
        button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rightClickTextColors[self.focusButton==button])
    def __rightOut(self,button,m):
        self.__isRightIn=False
        button._DirectGuiBase__componentInfo['text2'][0].setColorScale(Vec4(1,1,1,1))
  
    def __rightReleased(self,button,m):
        button.unbind(DGG.B3RELEASE)
        button.unbind(DGG.WITHIN)
        button.unbind(DGG.WITHOUT)
        button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rolloverColor)
        if not self.__isRightIn:
           return
        if callable(self.contextMenu):
           # run user command and pass the selected item, it's index, and the button
           self.contextMenu(button['extraArgs'][1],self.buttonsList.index(button),button)

    def scrollToBottom(self):
        ##for i in range(0,self.numItems):
        self.__scrollCanvas(1)

    def selectButton(self, button, item):
        self.__setFocusButton(button, item)
  
    def restoreNodeButton2Normal(self):
        """
           stop highlighting item
        """
        if self.focusButton != None:
            #self.focusButton['text_fg']=(1,1,1,1)
            self.focusButton['frameColor']=(0,0,0,0)
  
    def highlightNodeButton(self,idx=None):
        """
           highlight the item
        """
        if idx is not None:
            self.focusButton=self.buttonsList[idx]
        #self.focusButton['text_fg']=(.01,.01,.01,1)
        # nice dark blue.  don't mess with the text fg color though! we want it custom
        self.focusButton['frameColor']=(0,.3,.8,1)
  
    def clear(self):
        """
           clear the list
        """
        for c in self.buttonsList:
            c.remove()
        self.buttonsList=[]
        self.focusButton=None
        self.numItems=0
  
    def addItem(self,text,extraArgs=None,atIndex=None,textColorName=None):
        """
           add item to the list
           text : text for the button
           extraArgs : the object which will be passed to user command(s)
                       (both command and contextMenu) when the button get clicked
           atIndex : where to add the item
                     <None> : put item at the end of list
                     <integer> : put item at index <integer>
                     <button> : put item at <button>'s index
            textColorName : the color name eg. 'yellow'
        """
        textColor = self.buttonTextColor
        if textColorName != None:
            textColor = globals.colors[textColorName]
            
        button = DirectButton(parent=self.canvas,
            scale=self.itemScale,
            relief=DGG.FLAT,
            frameColor=(0,0,0,0),text_scale=self.itemTextScale,
            text=text, text_pos=(0,self.itemTextZ),text_fg=textColor,
            text_font=self.font, text_align=TextNode.ALeft,
            command=self.__setFocusButton,
            enableEdit=0, suppressMouse=0, rolloverSound=None,clickSound=None)
        #button.setMyMode(self.mode)
        l,r,b,t=button.getBounds()
        # top & bottom are blindly set without knowing where exactly the baseline is,
        # but this ratio fits most fonts
        baseline=-self.fontHeight*.25
        #button['saved_color'] = textColor
        button['frameSize']=(l-self.xtraSideSpace,r+self.xtraSideSpace,baseline,baseline+self.fontHeight)
  
  #          Zc=NodePath(button).getBounds().getCenter()[1]-self.fontHeight*.5+.25
  # #          Zc=button.getCenter()[1]-self.fontHeight*.5+.25
  #          button['frameSize']=(l-self.xtraSideSpace,r+self.xtraSideSpace,Zc,Zc+self.fontHeight)
       
        button['extraArgs']=[button,extraArgs]
        button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rolloverColor)
        button.bind(DGG.B3PRESS,self.__rightPressed,[button])
        if isinstance(atIndex,DirectButton):
           if atIndex.isEmpty():
              atIndex=None
           else:
              index=self.buttonsList.index(atIndex)
              self.buttonsList.insert(index,button)
        if atIndex==None:
           self.buttonsList.append(button)
           index=self.numItems
        elif type(atIndex)==IntType:
           index=atIndex
           self.buttonsList.insert(index,button)
        Zpos=(-.7-index)*self.itemVertSpacing
        button.setPos(.02,0,Zpos)
        if index!=self.numItems:
           for i in range(index+1,self.numItems+1):
               self.buttonsList[i].setZ(self.buttonsList[i],-self.fontHeight)
        self.numItems+=1
        self.__adjustCanvasLength(self.numItems)
        if self.autoFocus:
           self.focusViewOnItem(index)
        if self.colorChange:
           Sequence(
              button.colorScaleInterval(self.colorChangeDuration,self.newItemColor,globals.colors['guiblue3']),
              button.colorScaleInterval(self.colorChangeDuration,Vec4(1,1,1,1),self.newItemColor)
              ).start()
  
    def focusViewOnItem(self,idx):
        """
           Scroll the window so the newly added item will be displayed
           in the middle of the window, if possible.
        """
        Zpos=(idx+.7)*self.itemVertSpacing-self.frameHeight*.5
        self.__updateCanvasZpos(Zpos)
       
    def setAutoFocus(self,b):
        """
           set auto-view-focus state of newly added item
        """
        self.autoFocus=b
  
    def index(self,button):
        """
           get the index of button
        """
        if not button in self.buttonsList:
           return None
        return self.buttonsList.index(button)
       
    def getNumItems(self):
        """
           get the current number of items on the list
        """
        return self.numItems
  
    def disableItem(self,i):
        if not 0<=i<self.numItems:
           print 'DISABLING : invalid index (%s)' %i
           return
        self.buttonsList[i]['state']=DGG.DISABLED
        self.buttonsList[i].setColorScale(.3,.3,.3,1)
   
    def enableItem(self,i):
        if not 0<=i<self.numItems:
           print 'ENABLING : invalid index (%s)' %i
           return
        self.buttonsList[i]['state']=DGG.NORMAL
        self.buttonsList[i].setColorScale(1,1,1,1)
  
    def removeItem(self,index):
        if not 0<=index<self.numItems:
           print 'REMOVAL : invalid index (%s)' %index
           return
        if self.numItems==0: return
        if self.focusButton==self.buttonsList[index]:
           self.focusButton=None
        self.buttonsList[index].removeNode()
        del self.buttonsList[index]
        self.numItems-=1
        for i in range(index,self.numItems):
            self.buttonsList[i].setZ(self.buttonsList[i],self.fontHeight)
        self.__adjustCanvasLength(self.numItems)
  
    def destroy(self):
        self.clear()
        self.__exitingFrame()
        self.ignoreAll()
        self.childrenFrame.removeNode()
        taskMgr.remove('mouseInRegionCheck')
  
    def hide(self):
        self.childrenFrame.hide()
        self.isMouseInRegion=False
        self.__exitingFrame()
        taskMgr.remove('mouseInRegionCheck')
  
    def show(self):
        self.childrenFrame.show()
        if not hasattr(self,'frameRegion'):
           taskMgr.doMethodLater(.2,self.__getFrameRegion,'getFrameRegion')
        elif not taskMgr.hasTaskNamed('mouseInRegionCheck'):
           taskMgr.add(self.__mouseInRegionCheck,'mouseInRegionCheck')
  
    def toggleVisibility(self):
        if self.childrenFrame.isHidden():
           self.show()
        else:
           self.hide()
  
    def setMyMode(self, myMode):
        self.mode = myMode
class HostMenu(DirectObject):
    def __init__(self):
        self.defaultBtnMap = base.loader.loadModel("gui/button_map")
        self.buttonGeom = (self.defaultBtnMap.find("**/button_ready"),
                           self.defaultBtnMap.find("**/button_click"),
                           self.defaultBtnMap.find("**/button_rollover"),
                           self.defaultBtnMap.find("**/button_disabled"))

        defaultFont = loader.loadFont('gui/eufm10.ttf')

        self.logFrame = DirectScrolledFrame(
            canvasSize=(0, base.a2dRight * 2, -5, 0),
            frameSize=(0, base.a2dRight * 2, (base.a2dBottom + .2) * 2, 0),
            frameColor=(0.1, 0.1, 0.1, 1))
        self.logFrame.reparentTo(base.a2dTopLeft)

        # create the info and server debug output
        self.textscale = 0.1
        self.txtinfo = OnscreenText(scale=self.textscale,
                                    pos=(0.1, -0.1),
                                    text="",
                                    align=TextNode.ALeft,
                                    fg=(0.1, 1.0, 0.15, 1),
                                    bg=(0, 0, 0, 0),
                                    shadow=(0, 0, 0, 1),
                                    shadowOffset=(-0.02, -0.02))
        self.txtinfo.setTransparency(1)
        self.txtinfo.reparentTo(self.logFrame.getCanvas())

        # create a close Server button
        self.btnBackPos = Vec3(0.4, 0, 0.2)
        self.btnBackScale = 0.25
        self.btnBack = DirectButton(
            # Scale and position
            scale=self.btnBackScale,
            pos=self.btnBackPos,
            # Text
            text="Quit Server",
            text_scale=0.45,
            text_pos=(0, -0.1),
            text_fg=(0.82, 0.85, 0.87, 1),
            text_shadow=(0, 0, 0, 1),
            text_shadowOffset=(-0.02, -0.02),
            text_font=defaultFont,
            # Frame
            geom=self.buttonGeom,
            frameColor=(0, 0, 0, 0),
            relief=0,
            pressEffect=False,
            # Functionality
            command=self.back,
            rolloverSound=None,
            clickSound=None)
        self.btnBack.setTransparency(1)
        self.btnBack.reparentTo(base.a2dBottomLeft)

        # catch window resizes and recalculate the aspectration
        self.accept("window-event", self.recalcAspectRatio)
        self.accept("addLog", self.addLog)

    def show(self):
        self.logFrame.show()
        self.btnBack.show()

    def hide(self):
        self.logFrame.hide()
        self.btnBack.hide()

    def back(self):
        self.hide()
        base.messenger.send("stop_server")
        self.addLog("Quit Server!")

    def addLog(self, text):
        self.txtinfo.appendText(text + "\n")
        textbounds = self.txtinfo.getTightBounds()
        self.logFrame["canvasSize"] = (0, textbounds[1].getX(),
                                       textbounds[0].getZ(), 0)

    def recalcAspectRatio(self, window):
        """get the new aspect ratio to resize the mainframe"""
        # set the mainframe size to the window borders again
        self.logFrame["frameSize"] = (0, base.a2dRight * 2,
                                      (base.a2dBottom + .2) * 2, 0)
Example #8
0
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)