示例#1
0
文件: ui.py 项目: tgbugs/desc
class GuiFrame(DirectObject, HasKeybinds):
    #should be able to show/hide, do conditional show hide
    #position where you want
    #parent to other frames
    TEXT_MAGIC_NUMBER = .833333333334  #5/6 ?!?
    DRAW_ORDER={
        'frame':('unsorted',0),
        'frame_bg':('unsorted', 0),
        'items':('unsorted', 0),
        'title':('unsorted', 0),
        'border':('unsorted', 0),
    }

    def __init__(self, title,
                 shortcut = None,  # XXX obsolete, but needs a non deco replacement
                 x = 0,
                 y = .1,
                 width = .2,
                 height = .8,
                 #scale = .05,  # there is some black magic here :/
                 bdr_thickness = 2,
                 bdr_color = (.1, .1, .1, 1),
                 bg_color = (.7, .7, .7, .5),
                 text_color = (0, 0, 0, 1),
                 text_font = TextNode.getDefaultFont(),
                 #text_h = .05,  # do not use directly
                 text_height_mm = 4,
                 items = tuple(),
                ):
        #item_w_pad = 1
        #item_h_pad = 1

        self.title = title
        self.do_xywh(x, y, width, height)
        self.bdr_thickness = bdr_thickness  # FIXME ??
        self.bdr_color = bdr_color
        self.bg_color = bg_color
        self.text_color = text_color
        self.text_font = text_font
        self.text_height_mm = text_height_mm

        #set up variables
        self.__winx__ = base.win.getXSize() 
        self.__winy__ = base.win.getYSize()
        self.__ar__ = base.camLens.getAspectRatio()
        self.__was_dragging__ = False
        self.__first_item__ = None
        self.__add_head__ = None
        self.items = OrderedDict()  # ordered dict to allow sequential addition

        #self.BT = buttonThrower if buttonThrower else base.buttonThrowers[0].node()
        self.BT = base.buttonThrowers[0].node()

        # get our aspect ratio, and pixels per mm
        self.pixels_per_mm = render.getPythonTag('system_data')['max_ppmm']
        self.getWindowData()
        self.accept('window-event', self.getWindowData)

        #set the text height using the above data
        self.setTextHeight()

        # get the root for all frames in the scene
        self.frameRoot = aspect2d.find('frameRoot')
        if not self.frameRoot:
            self.frameRoot = aspect2d.attachNewNode('frameRoot')

        # create the parent node for this frame
        #parent = self.frameRoot.find('frame-*')
        #if not parent:
            #parent = self.frameRoot
        self.frame = self.frameRoot.attachNewNode('frame-%s-%s'%(title, id(self)))
        self.frame.setBin(*self.DRAW_ORDER['frame'])

        # background
        l,r,b,t = 0, self.width, 0, self.height
        self.frame_bg = DirectFrame(parent=self.frame,
                                    frameColor=self.bg_color,
                                    pos=LVecBase3f(self.x, 0, self.y),
                                    frameSize=(l,r,b,t),
                                    state=DGG.NORMAL,  # FIXME framesize is >_<
                                    suppressMouse=1)
        self.frame_bg.setBin(*self.DRAW_ORDER['frame_bg'])

        # border
        self.__make_border__(self.frame_bg, self.bdr_thickness, self.bdr_color, l, r, b, t)

        # setup for items
        self.itemsParent = self.frame_bg.attachNewNode('items parent')

        # title
        self.title_button = self.__create_item__(title, self.title_toggle_vis)
        
        # add any items that we got
        for item in items:
            self.__create_item__(*item)  # FIXME when we call frame adjust we will loose the record of any data items

        # dragging
        self.title_button.bind(DGG.B1PRESS, self.__startDrag)
        self.title_button.bind(DGG.B1RELEASE, self.__stopDrag)

        # raise if we click the frame background
        self.frame_bg.bind(DGG.B1PRESS, self.raise_)
        #self.frame_bg.bind(DGG.B1RELEASE, self.__stopDrag)  # this can cause problems w/ was dragging


        # toggle vis
        if shortcut:
            self.accept(shortcut, self.toggle_vis)

        # adjust the frame
        self.frame_adjust()

    @property
    def text_s(self):
        return self.text_h * self.TEXT_MAGIC_NUMBER

    def setTextHeight(self):
        h_units = 2 * base.a2dTop
        units_per_pixel = h_units / self.__winy__
        text_h = self.text_height_mm * self.pixels_per_mm * units_per_pixel
        self.text_h = text_h

    def do_xywh(self, x, y, w, h):
        """ makes negative wneg xidths and heights work
            as well as negative x and y (bottom right is 0)
        """
        if x < 0:
            x = 1 + x
        if y < 0:
            y = 1 + y
        if w < 0:
            x, w = x + w, -w
        if h < 0:
            y, h = y + h, -h

        self.x = self.fix_x(x)  # for top left
        self.y = self.fix_y(y)  # for top left
        self.width = self.fix_w(w)
        self.height = self.fix_h(h)

    def getWindowData(self, window=None):
        x = base.win.getXSize() 
        y = base.win.getYSize()
        if x != self.__winx__ or y != self.__winy__:
            self.__ar__ = base.camLens.getAspectRatio()  # w/h
            self.__winx__ = x
            self.__winy__ = y
            self.frame_adjust()

    def raise_(self, *args):
        """ function that raises windows
            call FIRST inside any function that should raise
        """
        self.frame.reparentTo(self.frameRoot)  # self.frame doesn't move so no wrt

    def frame_adjust(self):  # FIXME sometimes this fails to call, also calls too often at startup
        self.setTextHeight()
        MI = self.getMaxItems()  # does not count title >_<
        LI = len(self.items)
        DI = MI - LI
        if DI >= 0:
            for i in range(DI+1):
                self.__create_item__(' blank')
        else:
            for i in range(-(DI+1)):
                k,v = self.items.popitem()  # remove the last nodes in order
                v.removeNode()  # FIXME consider keeping these around?

        for k,b in self.items.items():
            if k == 'title':
                if self.frame_bg.isHidden():
                    x, y, z = self.frame_bg.getPos()
                    self.title_button.setPos(LVecBase3f(x, y , z-self.text_h))
                else:
                    self.title_button.setPos(LVecBase3f(0, 0, -self.text_h))
            elif k == self.__first_item__:
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
            else:
                b.setPos(LVecBase3f(0, 0, -self.text_h))
            b['frameSize'] = 0, self.width, 0, self.text_h
            b['text_scale'] = self.text_s, self.text_s
            b['text_pos'] = 0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s
        
    def getWindowSize(self, event=None):  # TODO see if we really need this
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        m = max(self.__winx__, self.__winy__)
        self.__xscale__ = self.__winx__ / m
        self.__yscale__ = self.__winy__ / m

    # put origin in top left and positive down and right
    @staticmethod
    def fix_x(x): return (x - .5) *  2  # TODO * base.a2dLeft?
    @staticmethod
    def fix_y(y): return (y - .5) * -2  # TODO * base.a2dTop?
    @staticmethod
    def fix_w(n): return  n * 2
    @staticmethod
    def fix_h(n): return -n * 2

    def add_item(self, text, command = None, args = tuple()): 
        args = list(args)
        if text[0] != ' ':
            text = ' '+text
        items = list(self.items)
        last_slot = len(self.items)
        if self.__add_head__ == last_slot:
            print('all slots are full, cannot add item to %s'%self)
            return None
        button = self.items[items[self.__add_head__]]
        button['text'] = text
        button['command'] = command
        button['extraArgs'] = args + button['extraArgs']  # blank buttons always have [self,id]
        self.__add_head__ += 1


    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

    def del_all(self):
        if self.__first_item__ != None:
            for id_, button in self.items.items():
                if id_ != 'title':
                    button['text'] = ' blank'
                    button['command'] = None
                    button['extraArgs'] = [self, id_]
            self.__add_head__ = 1

    def del_item(self, text):  # FIXME uniqueness problems
        #d = self.itemsParent.find('*%s*'%text)
        if text[0] != ' ':
            text = ' '+text
        d = [i for i in self.items.values() if i.getName().count(text)]
        try:
            self.__del_item__(d[0].getPythonTag('id'))
        except IndexError:
            print('that item does not seem to exist')
            # if we have a name then there shouldn't be key errors

    def __del_item__(self, index):
        """ I have no idea how this is going to work """
        out = self.items[index]
        p = out.getParent()
        if out.getNumChildren():  # avoid the printing of the AssertionError :/
            c = out.getChild(0)
            c.reparentTo(p)
            if index == self.__first_item__:  # XXX is fails, ints from id !=
                c.setPos(LVecBase3f(out.getPos()))
                id_ = c.getPythonTag('id')
                self.__first_item__ = id_
                out.setPos(LVecBase3f(0, 0, -self.text_h))
        self.items.pop(index)
        parent = list(self.items.values())[-1]
        out['text'] = ' del blank'
        #out['command'] = None
        out['extraArgs'] = [self, index]
        out.reparentTo(parent)
        self.items[index] = out
        if self.__add_head__ > 1:  # title is always at 0
            self.__add_head__ -= 1

    @classmethod
    def __make_border__(cls, parent, thickness, color, l, r , b, t):
        moveto_drawto = (
           ((l,0,t), (l,0,b)),
           ((r,0,t), (r,0,b)),
           ((l,0,b), (r,0,b)),
           ((l,0,t), (r,0,t)),
        )
        for moveto, drawto in moveto_drawto:
            Border = LineSegs()
            Border.setThickness(thickness)
            Border.setColor(*color)
            Border.moveTo(*moveto)
            Border.drawTo(*drawto)
            b = parent.attachNewNode(Border.create())
            b.setBin(*cls.DRAW_ORDER['border'])

    def getMaxItems(self):
        return int(abs(self.height / self.text_h) - 1)

    @event_callback
    def toggle_vis(self):
        if self.frame_bg.isHidden():
            self.frame_bg.show()
            self.raise_()
        else:
            self.frame_bg.hide()

    def title_toggle_vis(self):
        if not self.__was_dragging__:
            self.toggle_vis()
            if self.frame_bg.isHidden():
                self.title_button.wrtReparentTo(self.frame)
                self.title_button['frameColor'] = (1, 1, 1, .5)  # TODO
            else:
                self.title_button.wrtReparentTo(self.frame_bg)
                self.title_button['frameColor'] = (1, 1, 1, 0)  # TODO
        else:
            self.__was_dragging__ = False

    def __startDrag(self, crap):
        self.raise_()
        self._ox, self._oy = base.mouseWatcherNode.getMouse()
        taskMgr.add(self.__drag,'dragging %s'%self.title)
        self.origBTprefix=self.BT.getPrefix()
        self.BT.setPrefix('dragging frame')

    def __drag(self, task):
        if base.mouseWatcherNode.hasMouse():
            x, y = base.mouseWatcherNode.getMouse()
            if x != self._ox or y != self._oy:
                m_old = aspect2d.getRelativePoint(render2d, Point3(self._ox, self._oy, 0))
                m_new = aspect2d.getRelativePoint(render2d, Point3(x, y, 0))
                dx, dy, _ = m_new - m_old
                self.setPos(self.x + dx, self.y + dy)
                self._ox = x
                self._oy = y
                self.__was_dragging__ = True
        return task.cont

    def __stopDrag(self,crap):
        taskMgr.remove('dragging %s'%self.title)
        self.BT.setPrefix(self.origBTprefix)

    def setPos(self, x, y):
        """ actually sets the title button position
            since it is really the parent node
        """
        self.x = x
        self.y = y #- self.text_h  # FIXME is hard :/
        self.frame_bg.setPos(LVecBase3f(x, 0, y))
        if self.frame_bg.isHidden():
            self.title_button.setPos(LVecBase3f(x, 0, y - self.text_h))


    def __enter__(self):
        #load the position
        #load other saved state
        pass

    def __exit__(self):
        #save the position!
        #save other state
        pass
示例#2
0
class GuiFrame(DirectObject, HasKeybinds):
    #should be able to show/hide, do conditional show hide
    #position where you want
    #parent to other frames
    TEXT_MAGIC_NUMBER = .833333333334  #5/6 ?!?
    DRAW_ORDER = {
        'frame': ('unsorted', 0),
        'frame_bg': ('unsorted', 0),
        'items': ('unsorted', 0),
        'title': ('unsorted', 0),
        'border': ('unsorted', 0),
    }

    def __init__(
            self,
            title,
            shortcut=None,  # XXX obsolete, but needs a non deco replacement
            x=0,
            y=.1,
            width=.2,
            height=.8,
            #scale = .05,  # there is some black magic here :/
            bdr_thickness=2,
            bdr_color=(.1, .1, .1, 1),
            bg_color=(.7, .7, .7, .5),
            text_color=(0, 0, 0, 1),
            text_font=TextNode.getDefaultFont(),
            #text_h = .05,  # do not use directly
            text_height_mm=4,
            items=tuple(),
    ):
        #item_w_pad = 1
        #item_h_pad = 1

        self.title = title
        self.do_xywh(x, y, width, height)
        self.bdr_thickness = bdr_thickness  # FIXME ??
        self.bdr_color = bdr_color
        self.bg_color = bg_color
        self.text_color = text_color
        self.text_font = text_font
        self.text_height_mm = text_height_mm

        #set up variables
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        self.__ar__ = base.camLens.getAspectRatio()
        self.__was_dragging__ = False
        self.__first_item__ = None
        self.__add_head__ = None
        self.items = OrderedDict()  # ordered dict to allow sequential addition

        #self.BT = buttonThrower if buttonThrower else base.buttonThrowers[0].node()
        self.BT = base.buttonThrowers[0].node()

        # get our aspect ratio, and pixels per mm
        self.pixels_per_mm = render.getPythonTag('system_data')['max_ppmm']
        self.getWindowData()
        self.accept('window-event', self.getWindowData)

        #set the text height using the above data
        self.setTextHeight()

        # get the root for all frames in the scene
        self.frameRoot = aspect2d.find('frameRoot')
        if not self.frameRoot:
            self.frameRoot = aspect2d.attachNewNode('frameRoot')

        # create the parent node for this frame
        #parent = self.frameRoot.find('frame-*')
        #if not parent:
        #parent = self.frameRoot
        self.frame = self.frameRoot.attachNewNode('frame-%s-%s' %
                                                  (title, id(self)))
        self.frame.setBin(*self.DRAW_ORDER['frame'])

        # background
        l, r, b, t = 0, self.width, 0, self.height
        self.frame_bg = DirectFrame(
            parent=self.frame,
            frameColor=self.bg_color,
            pos=LVecBase3f(self.x, 0, self.y),
            frameSize=(l, r, b, t),
            state=DGG.NORMAL,  # FIXME framesize is >_<
            suppressMouse=1)
        self.frame_bg.setBin(*self.DRAW_ORDER['frame_bg'])

        # border
        self.__make_border__(self.frame_bg, self.bdr_thickness, self.bdr_color,
                             l, r, b, t)

        # setup for items
        self.itemsParent = self.frame_bg.attachNewNode('items parent')

        # title
        self.title_button = self.__create_item__(title, self.title_toggle_vis)

        # add any items that we got
        for item in items:
            self.__create_item__(
                *item
            )  # FIXME when we call frame adjust we will loose the record of any data items

        # dragging
        self.title_button.bind(DGG.B1PRESS, self.__startDrag)
        self.title_button.bind(DGG.B1RELEASE, self.__stopDrag)

        # raise if we click the frame background
        self.frame_bg.bind(DGG.B1PRESS, self.raise_)
        #self.frame_bg.bind(DGG.B1RELEASE, self.__stopDrag)  # this can cause problems w/ was dragging

        # toggle vis
        if shortcut:
            self.accept(shortcut, self.toggle_vis)

        # adjust the frame
        self.frame_adjust()

    @property
    def text_s(self):
        return self.text_h * self.TEXT_MAGIC_NUMBER

    def setTextHeight(self):
        h_units = 2 * base.a2dTop
        units_per_pixel = h_units / self.__winy__
        text_h = self.text_height_mm * self.pixels_per_mm * units_per_pixel
        self.text_h = text_h

    def do_xywh(self, x, y, w, h):
        """ makes negative wneg xidths and heights work
            as well as negative x and y (bottom right is 0)
        """
        if x < 0:
            x = 1 + x
        if y < 0:
            y = 1 + y
        if w < 0:
            x, w = x + w, -w
        if h < 0:
            y, h = y + h, -h

        self.x = self.fix_x(x)  # for top left
        self.y = self.fix_y(y)  # for top left
        self.width = self.fix_w(w)
        self.height = self.fix_h(h)

    def getWindowData(self, window=None):
        x = base.win.getXSize()
        y = base.win.getYSize()
        if x != self.__winx__ or y != self.__winy__:
            self.__ar__ = base.camLens.getAspectRatio()  # w/h
            self.__winx__ = x
            self.__winy__ = y
            self.frame_adjust()

    def raise_(self, *args):
        """ function that raises windows
            call FIRST inside any function that should raise
        """
        self.frame.reparentTo(
            self.frameRoot)  # self.frame doesn't move so no wrt

    def frame_adjust(
        self
    ):  # FIXME sometimes this fails to call, also calls too often at startup
        self.setTextHeight()
        MI = self.getMaxItems()  # does not count title >_<
        LI = len(self.items)
        DI = MI - LI
        if DI >= 0:
            for i in range(DI + 1):
                self.__create_item__(' blank')
        else:
            for i in range(-(DI + 1)):
                k, v = self.items.popitem()  # remove the last nodes in order
                v.removeNode()  # FIXME consider keeping these around?

        for k, b in self.items.items():
            if k == 'title':
                if self.frame_bg.isHidden():
                    x, y, z = self.frame_bg.getPos()
                    self.title_button.setPos(LVecBase3f(x, y, z - self.text_h))
                else:
                    self.title_button.setPos(LVecBase3f(0, 0, -self.text_h))
            elif k == self.__first_item__:
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
            else:
                b.setPos(LVecBase3f(0, 0, -self.text_h))
            b['frameSize'] = 0, self.width, 0, self.text_h
            b['text_scale'] = self.text_s, self.text_s
            b['text_pos'] = 0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s

    def getWindowSize(self, event=None):  # TODO see if we really need this
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        m = max(self.__winx__, self.__winy__)
        self.__xscale__ = self.__winx__ / m
        self.__yscale__ = self.__winy__ / m

    # put origin in top left and positive down and right
    @staticmethod
    def fix_x(x):
        return (x - .5) * 2  # TODO * base.a2dLeft?

    @staticmethod
    def fix_y(y):
        return (y - .5) * -2  # TODO * base.a2dTop?

    @staticmethod
    def fix_w(n):
        return n * 2

    @staticmethod
    def fix_h(n):
        return -n * 2

    def add_item(self, text, command=None, args=tuple()):
        args = list(args)
        if text[0] != ' ':
            text = ' ' + text
        items = list(self.items)
        last_slot = len(self.items)
        if self.__add_head__ == last_slot:
            print('all slots are full, cannot add item to %s' % self)
            return None
        button = self.items[items[self.__add_head__]]
        button['text'] = text
        button['command'] = command
        button['extraArgs'] = args + button[
            'extraArgs']  # blank buttons always have [self,id]
        self.__add_head__ += 1

    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

    def del_all(self):
        if self.__first_item__ != None:
            for id_, button in self.items.items():
                if id_ != 'title':
                    button['text'] = ' blank'
                    button['command'] = None
                    button['extraArgs'] = [self, id_]
            self.__add_head__ = 1

    def del_item(self, text):  # FIXME uniqueness problems
        #d = self.itemsParent.find('*%s*'%text)
        if text[0] != ' ':
            text = ' ' + text
        d = [i for i in self.items.values() if i.getName().count(text)]
        try:
            self.__del_item__(d[0].getPythonTag('id'))
        except IndexError:
            print('that item does not seem to exist')
            # if we have a name then there shouldn't be key errors

    def __del_item__(self, index):
        """ I have no idea how this is going to work """
        out = self.items[index]
        p = out.getParent()
        if out.getNumChildren():  # avoid the printing of the AssertionError :/
            c = out.getChild(0)
            c.reparentTo(p)
            if index == self.__first_item__:  # XXX is fails, ints from id !=
                c.setPos(LVecBase3f(out.getPos()))
                id_ = c.getPythonTag('id')
                self.__first_item__ = id_
                out.setPos(LVecBase3f(0, 0, -self.text_h))
        self.items.pop(index)
        parent = list(self.items.values())[-1]
        out['text'] = ' del blank'
        #out['command'] = None
        out['extraArgs'] = [self, index]
        out.reparentTo(parent)
        self.items[index] = out
        if self.__add_head__ > 1:  # title is always at 0
            self.__add_head__ -= 1

    @classmethod
    def __make_border__(cls, parent, thickness, color, l, r, b, t):
        moveto_drawto = (
            ((l, 0, t), (l, 0, b)),
            ((r, 0, t), (r, 0, b)),
            ((l, 0, b), (r, 0, b)),
            ((l, 0, t), (r, 0, t)),
        )
        for moveto, drawto in moveto_drawto:
            Border = LineSegs()
            Border.setThickness(thickness)
            Border.setColor(*color)
            Border.moveTo(*moveto)
            Border.drawTo(*drawto)
            b = parent.attachNewNode(Border.create())
            b.setBin(*cls.DRAW_ORDER['border'])

    def getMaxItems(self):
        return int(abs(self.height / self.text_h) - 1)

    @event_callback
    def toggle_vis(self):
        if self.frame_bg.isHidden():
            self.frame_bg.show()
            self.raise_()
        else:
            self.frame_bg.hide()

    def title_toggle_vis(self):
        if not self.__was_dragging__:
            self.toggle_vis()
            if self.frame_bg.isHidden():
                self.title_button.wrtReparentTo(self.frame)
                self.title_button['frameColor'] = (1, 1, 1, .5)  # TODO
            else:
                self.title_button.wrtReparentTo(self.frame_bg)
                self.title_button['frameColor'] = (1, 1, 1, 0)  # TODO
        else:
            self.__was_dragging__ = False

    def __startDrag(self, crap):
        self.raise_()
        self._ox, self._oy = base.mouseWatcherNode.getMouse()
        taskMgr.add(self.__drag, 'dragging %s' % self.title)
        self.origBTprefix = self.BT.getPrefix()
        self.BT.setPrefix('dragging frame')

    def __drag(self, task):
        if base.mouseWatcherNode.hasMouse():
            x, y = base.mouseWatcherNode.getMouse()
            if x != self._ox or y != self._oy:
                m_old = aspect2d.getRelativePoint(
                    render2d, Point3(self._ox, self._oy, 0))
                m_new = aspect2d.getRelativePoint(render2d, Point3(x, y, 0))
                dx, dy, _ = m_new - m_old
                self.setPos(self.x + dx, self.y + dy)
                self._ox = x
                self._oy = y
                self.__was_dragging__ = True
        return task.cont

    def __stopDrag(self, crap):
        taskMgr.remove('dragging %s' % self.title)
        self.BT.setPrefix(self.origBTprefix)

    def setPos(self, x, y):
        """ actually sets the title button position
            since it is really the parent node
        """
        self.x = x
        self.y = y  #- self.text_h  # FIXME is hard :/
        self.frame_bg.setPos(LVecBase3f(x, 0, y))
        if self.frame_bg.isHidden():
            self.title_button.setPos(LVecBase3f(x, 0, y - self.text_h))

    def __enter__(self):
        #load the position
        #load other saved state
        pass

    def __exit__(self):
        #save the position!
        #save other state
        pass
示例#3
0
class Window():
    texture = None
    def __init__(self, title_text, scale, parent=None, child=None, transparent=False, owner=None):
        self.title_text = title_text
        self.scale = scale
        self.title_size = settings.ui_font_size
        self.owner = owner
        self.child = None
        self.last_pos = None
        self.title_color = (1, 1, 1, 1)
        self.title_pad = tuple(self.scale * 2)
        if parent is None:
            parent = aspect2d
        self.parent = parent
        if transparent:
            frameColor = (0, 0, 0, 0)
        else:
            frameColor = (0.5, 0.5, 0.5, 1)
        self.pad = 0
        self.event_handler = DirectObject()
        self.button_thrower = base.buttonThrowers[0].node()
        self.event_handler.accept("wheel_up-up", self.mouse_wheel_event, extraArgs = [-1])
        self.event_handler.accept("wheel_down-up", self.mouse_wheel_event, extraArgs = [1])
        self.scrollers = []

#         if Window.texture is None:
#             Window.texture = loader.loadTexture('textures/futureui1.png')
#         image_scale = (scale[0] * Window.texture.get_x_size(), 1, scale[1] * Window.texture.get_y_size())
        self.frame = DirectFrame(parent=parent, state=DGG.NORMAL, frameColor=frameColor)#, image=self.texture, image_scale=image_scale)
        self.title_frame = DirectFrame(parent=self.frame, state=DGG.NORMAL, frameColor=(.5, .5, .5, 1))
        self.title = OnscreenText(text=self.title_text,
                                  style=Plain,
                                  fg=self.title_color,
                                  scale=tuple(self.scale * self.title_size),
                                  parent=self.title_frame,
                                  pos=(0, 0),
                                  align=TextNode.ALeft,
                                  font=None,
                                  mayChange=True)
        bounds = self.title.getTightBounds()
        self.title_frame['frameSize'] = [0, bounds[1][0] - bounds[0][0] + self.title_pad[0] * 2,
                                         0, bounds[1][2] - bounds[0][2] + self.title_pad[1] * 2]
        self.title.setPos( -bounds[0][0] + self.title_pad[0],  -bounds[0][2] + self.title_pad[1])
        self.close_frame = DirectFrame(parent=self.frame, state=DGG.NORMAL, frameColor=(.5, .5, .5, 1))
        self.close = OnscreenText(text='X',
                                  style=Plain,
                                  fg=self.title_color,
                                  scale=tuple(self.scale * self.title_size),
                                  parent=self.close_frame,
                                  pos=(0, 0),
                                  align=TextNode.ACenter,
                                  font=None,
                                  mayChange=True)
        bounds = self.close.getTightBounds()
        self.close_frame['frameSize'] = [0, bounds[1][0] - bounds[0][0] + self.title_pad[0] * 2,
                                         self.title_frame['frameSize'][2], self.title_frame['frameSize'][3]]
        self.close.setPos( -bounds[0][0] + self.title_pad[0],  -bounds[0][2] + self.title_pad[1])
        self.frame.setPos(0, 0, 0)
        self.title_frame.bind(DGG.B1PRESS, self.start_drag)
        self.title_frame.bind(DGG.B1RELEASE, self.stop_drag)
        self.close_frame.bind(DGG.B1PRESS, self.close_window)
        self.set_child(child)

    def set_child(self, child):
        if child is not None:
            self.child = child
            child.reparent_to(self.frame)
            self.update()

    def update(self):
        if self.child is not None:
            frame_size = list(self.child.frame['frameSize'])
            if frame_size is not None:
                frame_size[0] -= self.pad
                frame_size[1] += self.pad
                frame_size[2] += self.pad
                frame_size[3] -= self.pad
            self.frame['frameSize'] = frame_size
        if self.frame['frameSize'] is not None:
            width = self.frame['frameSize'][1] - self.frame['frameSize'][0]
            title_size = self.title_frame['frameSize']
            title_size[0] = 0
            title_size[1] = width
            self.title_frame['frameSize'] = title_size
            self.close_frame.setPos(width - self.close_frame['frameSize'][1], 0, 0)

    def register_scroller(self, scroller):
        self.scrollers.append(scroller)

    def mouse_wheel_event(self, dir):
        # If the user is scrolling a scroll-bar, don't try to scroll the scrolled-frame too.
        region = base.mouseWatcherNode.getOverRegion()
        if region is not None:
            widget = base.render2d.find("**/*{0}".format(region.name))
            if widget.is_empty() or isinstance(widget.node(), PGSliderBar) or isinstance(widget.getParent().node(), PGSliderBar):
                return

        # Get the mouse-position
        if not base.mouseWatcherNode.hasMouse():
            return
        mouse_pos = base.mouseWatcherNode.getMouse()

        found_scroller = None
        # Determine whether any of the scrolled-frames are under the mouse-pointer
        for scroller in self.scrollers:
            bounds = scroller['frameSize']
            pos = scroller.get_relative_point(base.render2d, Point3(mouse_pos.get_x() ,0, mouse_pos.get_y()))
            if pos.x > bounds[0] and pos.x < bounds[1] and \
                pos.z > bounds[2] and pos.z < bounds[3]:
                found_scroller = scroller
                break

        if found_scroller is not None:
            if not found_scroller.verticalScroll.isHidden():
                self.do_mouse_scroll(found_scroller.verticalScroll, dir, None)
            else:
                self.do_mouse_scroll(found_scroller.horizontalScroll, dir, None)

    def do_mouse_scroll(self, obj, dir, data):
        if isinstance(obj, DirectSlider) or isinstance(obj, DirectScrollBar):
            obj.setValue(obj.getValue() + dir * obj["pageSize"] * 0.1)

    def start_drag(self, event):
        if base.mouseWatcherNode.has_mouse():
            mpos = base.mouseWatcherNode.get_mouse()
            self.drag_start = self.frame.parent.get_relative_point(render2d, Point3(mpos.get_x() ,0, mpos.get_y())) - self.frame.getPos()
            taskMgr.add(self.drag, "drag", -1)

    def drag(self, task):
        if base.mouseWatcherNode.has_mouse():
            mpos = base.mouseWatcherNode.get_mouse()
            current_pos = self.frame.parent.get_relative_point(render2d, Point3(mpos.get_x() ,0, mpos.get_y()))
            self.frame.set_pos(current_pos - self.drag_start)
        return task.again

    def close_window(self, event=None):
        if self.owner is not None:
            self.owner.window_closed(self)
        self.destroy()

    def stop_drag(self, event):
        taskMgr.remove("drag")
        self.last_pos = self.frame.getPos()

    def destroy(self):
        if self.frame is not None:
            self.frame.destroy()
        self.frame = None
        self.scrollers = []
        self.event_handler.ignore_all()

    def getPos(self):
        return self.frame.getPos()

    def setPos(self, pos):
        self.frame.setPos(pos)
示例#4
0
class Window():
    texture = None

    def __init__(self,
                 title,
                 scale,
                 parent=None,
                 child=None,
                 transparent=False,
                 owner=None):
        self.scale = scale
        self.owner = owner
        self.last_pos = None
        self.title_text = title
        self.title_color = (1, 1, 1, 1)
        self.title_pad = tuple(self.scale * 2)
        if parent is None:
            parent = aspect2d
        self.parent = parent
        if transparent:
            frameColor = (0, 0, 0, 0)
        else:
            frameColor = (0, 0, 0, 1)
        self.pad = 0
        #         if Window.texture is None:
        #             Window.texture = loader.loadTexture('textures/futureui1.png')
        #         image_scale = (scale[0] * Window.texture.get_x_size(), 1, scale[1] * Window.texture.get_y_size())
        self.frame = DirectFrame(
            parent=parent, state=DGG.NORMAL, frameColor=frameColor
        )  #, image=self.texture, image_scale=image_scale)
        self.title_frame = DirectFrame(parent=self.frame,
                                       state=DGG.NORMAL,
                                       frameColor=(.5, .5, .5, 1))
        self.title = OnscreenText(text=self.title_text,
                                  style=Plain,
                                  fg=self.title_color,
                                  scale=tuple(self.scale * 14),
                                  parent=self.title_frame,
                                  pos=(0, 0),
                                  align=TextNode.ALeft,
                                  font=None,
                                  mayChange=True)
        bounds = self.title.getTightBounds()
        self.title_frame['frameSize'] = [
            0, bounds[1][0] - bounds[0][0] + self.title_pad[0] * 2, 0,
            bounds[1][2] - bounds[0][2] + self.title_pad[1] * 2
        ]
        self.title.setPos(-bounds[0][0] + self.title_pad[0],
                          -bounds[0][2] + self.title_pad[1])
        self.close_frame = DirectFrame(parent=self.frame,
                                       state=DGG.NORMAL,
                                       frameColor=(.5, .5, .5, 1))
        self.close = OnscreenText(text='X',
                                  style=Plain,
                                  fg=self.title_color,
                                  scale=tuple(self.scale * 14),
                                  parent=self.close_frame,
                                  pos=(0, 0),
                                  align=TextNode.ACenter,
                                  font=None,
                                  mayChange=True)
        bounds = self.close.getTightBounds()
        self.close_frame['frameSize'] = [
            0, bounds[1][0] - bounds[0][0] + self.title_pad[0] * 2,
            self.title_frame['frameSize'][2], self.title_frame['frameSize'][3]
        ]
        self.close.setPos(-bounds[0][0] + self.title_pad[0],
                          -bounds[0][2] + self.title_pad[1])
        self.frame.setPos(0, 0, 0)
        self.title_frame.bind(DGG.B1PRESS, self.start_drag)
        self.title_frame.bind(DGG.B1RELEASE, self.stop_drag)
        self.close_frame.bind(DGG.B1PRESS, self.close_window)
        self.set_child(child)

    def set_child(self, child):
        if child is not None:
            self.child = child
            child.reparent_to(self.frame)
            self.update()

    def update(self):
        if self.child is not None:
            frame_size = self.child.frame['frameSize']
            if frame_size is not None:
                frame_size[0] -= self.pad
                frame_size[1] += self.pad
                frame_size[2] += self.pad
                frame_size[3] -= self.pad
            self.frame['frameSize'] = frame_size
        if self.frame['frameSize'] is not None:
            width = self.frame['frameSize'][1] - self.frame['frameSize'][0]
            title_size = self.title_frame['frameSize']
            title_size[0] = 0
            title_size[1] = width
            self.title_frame['frameSize'] = title_size
            self.close_frame.setPos(width - self.close_frame['frameSize'][1],
                                    0, 0)

    def start_drag(self, event):
        if base.mouseWatcherNode.has_mouse():
            mpos = base.mouseWatcherNode.get_mouse()
            self.drag_start = self.frame.parent.get_relative_point(
                render2d, Point3(mpos.get_x(), 0,
                                 mpos.get_y())) - self.frame.getPos()
            taskMgr.add(self.drag, "drag", -1)

    def drag(self, task):
        if base.mouseWatcherNode.has_mouse():
            mpos = base.mouseWatcherNode.get_mouse()
            current_pos = self.frame.parent.get_relative_point(
                render2d, Point3(mpos.get_x(), 0, mpos.get_y()))
            self.frame.set_pos(current_pos - self.drag_start)
        return task.again

    def close_window(self, event=None):
        if self.owner is not None:
            self.owner.window_closed(self)
        self.destroy()

    def stop_drag(self, event):
        taskMgr.remove("drag")
        self.last_pos = self.frame.getPos()

    def destroy(self):
        if self.frame is not None:
            self.frame.destroy()
        self.frame = None

    def getPos(self):
        return self.frame.getPos()

    def setPos(self, pos):
        self.frame.setPos(pos)
示例#5
0
class DirectWindow( DirectFrame ):
  def __init__( self,
                pos              = ( -.5, .5),
                title            = 'Title',
                curSize          = (1, 1),
                maxSize          = ( 1, 1 ),
                minSize          = ( .5, .5 ),
                backgroundColor = ( 0, 0, 1, .6),
                borderColor      = ( 1, 1, 1, 1 ),
                titleColor       = ( 1, 1, 1, 1 ),
                borderSize       = 0.04,
                titleSize        = 0.06,
                closeButton      = False,
                resizeButton    = True,
                windowParent     = aspect2d,
                preserve         = True,
                preserveWhole      = True, 
              ):
    self.preserve = preserve
    self.preserveWhole = preserveWhole
    self.windowParent = windowParent
    self.windowPos = pos
    DirectFrame.__init__( self,
        parent       = aspect2d,
        pos          = ( self.windowPos[0], 0, self.windowPos[1] ),
        frameColor  = ( 0, 0, 0, 0 ),
        frameTexture = loader.loadTexture( DIRECTORY+'transparent.png' )
      )
    self.setTransparency(True)
    
    # the title part of the window, drag around to move the window
    self.headerHeight = titleSize
    h = -self.headerHeight
    self.windowHeaderLeft = DirectButton(
        parent       = self,
        frameTexture = DEFAULT_TITLE_GEOM_LEFT,
        frameSize    = ( -.5, .5, -.5, .5 ),
        borderWidth  = ( 0, 0 ),
        relief       = DGG.FLAT,
        frameColor   = titleColor,
      )
    self.windowHeaderCenter = DirectButton(
        parent       = self,
        frameTexture = DEFAULT_TITLE_GEOM_CENTER,
        frameSize    = ( -.5, .5, -.5, .5 ),
        borderWidth  = ( 0, 0 ),
        relief       = DGG.FLAT,
        frameColor   = titleColor,
      )
    if closeButton:
      rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT_CLOSE
      command = self.destroy
    else:
      rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT
      command = None
    self.windowHeaderRight = DirectButton(
        parent       = self,
        frameTexture = rightTitleGeom,
        frameSize    = ( -.5, .5, -.5, .5 ),
        borderWidth  = ( 0, 0 ),
        relief       = DGG.FLAT,
        frameColor   = titleColor,
        command      = command
      )
    
    self.windowHeaderLeft.setTransparency(True)
    self.windowHeaderCenter.setTransparency(True)
    self.windowHeaderRight.setTransparency(True)
    
    self.windowHeaderLeft.bind( DGG.B1PRESS, self.startWindowDrag )
    self.windowHeaderCenter.bind( DGG.B1PRESS, self.startWindowDrag )
    self.windowHeaderRight.bind( DGG.B1PRESS, self.startWindowDrag )
    
    # this is not handled correctly, if a window is dragged which has been
    # created before another it will not be released
    # check the bugfixed startWindowDrag function
    #self.windowHeader.bind(DGG.B1RELEASE,self.stopWindowDrag)
    
    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.attachNewNode(text)
    self.textNodePath.setScale(self.headerHeight*0.8)
    
    # the content part of the window, put stuff beneath
    # contentWindow.getCanvas() to put it into it
    self.maxVirtualSize = maxSize
    self.minVirtualSize = minSize
    self.resizeSize     = borderSize
    self.contentWindow = DirectScrolledFrame(
        parent                                  = self,
        pos                                     = ( 0, 0, -self.headerHeight ),
        canvasSize                              = ( 0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1] ),
        frameColor                              = ( 0, 0, 0, 0), # defines the background color of the resize-button
        relief                                  = DGG.FLAT,
        borderWidth                             = (0, 0),
        verticalScroll_frameSize                = [0, self.resizeSize, 0, 1],
        horizontalScroll_frameSize              = [0, 1, 0, self.resizeSize],
        
        # resize the scrollbar according to window size
        verticalScroll_resizeThumb              = False,
        horizontalScroll_resizeThumb            = False,
        # define the textures for the scrollbars
        verticalScroll_frameTexture             = VERTICALSCROLL_FRAMETEXTURE,
        verticalScroll_incButton_frameTexture   = VERTICALSCROLL_INCBUTTON_FRAMETEXTURE,
        verticalScroll_decButton_frameTexture   = VERTICALSCROLL_DECBUTTON_FRAMETEXTURE,
        verticalScroll_thumb_frameTexture       = VERTICALSCROLL_TUMB_FRAMETEXTURE,
        horizontalScroll_frameTexture           = HORIZONTALSCROLL_FRAMETEXTURE,
        horizontalScroll_incButton_frameTexture = HORIZONTALSCROLL_INCBUTTON_FRAMETEXTURE,
        horizontalScroll_decButton_frameTexture = HORIZONTALSCROLL_DECBUTTON_FRAMETEXTURE,
        horizontalScroll_thumb_frameTexture     = HORIZONTALSCROLL_TUMB_FRAMETEXTURE,
        # make all flat, so the texture is as we want it
        verticalScroll_relief                   = DGG.FLAT,
        verticalScroll_thumb_relief             = DGG.FLAT,
        verticalScroll_decButton_relief         = DGG.FLAT,
        verticalScroll_incButton_relief         = DGG.FLAT,
        horizontalScroll_relief                 = DGG.FLAT,
        horizontalScroll_thumb_relief           = DGG.FLAT,
        horizontalScroll_decButton_relief       = DGG.FLAT,
        horizontalScroll_incButton_relief       = DGG.FLAT,
        # colors
        verticalScroll_frameColor               = borderColor,
        verticalScroll_incButton_frameColor     = borderColor,
        verticalScroll_decButton_frameColor     = borderColor,
        verticalScroll_thumb_frameColor         = borderColor,
        horizontalScroll_frameColor             = borderColor,
        horizontalScroll_incButton_frameColor   = borderColor,
        horizontalScroll_decButton_frameColor   = borderColor,
        horizontalScroll_thumb_frameColor       = borderColor,
      )
    self.contentWindow.setTransparency(True)
    
    # background color
    self.backgroundColor = DirectFrame(
        parent       = self.contentWindow.getCanvas(),
        frameSize    = ( 0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1] ),
        frameColor   = backgroundColor,
        relief       = DGG.FLAT,
        borderWidth  = ( .01, .01),
      )
    self.backgroundColor.setTransparency(True)

    # Add a box
    self.box = boxes.VBox(parent = self.getCanvas())
   
    # is needed for some nicer visuals of the resize button (background)
    self.windowResizeBackground = DirectButton(
        parent       = self,
        frameSize    = ( -.5, .5, -.5, .5 ),
        borderWidth  = ( 0, 0 ),
        scale        = ( self.resizeSize, 1, self.resizeSize ),
        relief       = DGG.FLAT,
        frameColor   = backgroundColor,
      )
    # the resize button of the window
    if resizeButton:
        self.windowResize = DirectButton(
            parent       = self,
            frameSize    = ( -.5, .5, -.5, .5 ),
            borderWidth  = ( 0, 0 ),
            scale        = ( self.resizeSize, 1, self.resizeSize ),
            relief       = DGG.FLAT,
            frameTexture = DEFAULT_RESIZE_GEOM,
            frameColor   = borderColor,
          )
        self.windowResize.setTransparency(True)
        self.windowResize.bind(DGG.B1PRESS,self.startResizeDrag)
        self.windowResize.bind(DGG.B1RELEASE,self.stopResizeDrag)
    else:
        self.windowResize = DirectFrame()
    
    # offset then clicking on the resize button from the mouse to the resizebutton
    # position, required to calculate the position / scaling
    self.offset = None
    self.taskName = "resizeTask-%s" % str(hash(self))
    
    # do sizing of the window (minimum)
    #self.resize( Vec3(0,0,0), Vec3(0,0,0) )
    # maximum
    #self.resize( Vec3(100,0,-100), Vec3(0,0,0) )
    self.resize( Vec3(curSize[0], 0, -curSize[1]), Vec3(0,0,0))
    self.widgets = []
  
  def getCanvas(self):
    return self.contentWindow.getCanvas()
  
  # dragging functions
  def startWindowDrag( self, param ):
    self.wrtReparentTo( aspect2dMouseNode )
    self.ignoreAll()
    self.accept( 'mouse1-up', self.stopWindowDrag )
  def stopWindowDrag( self, param=None ):
    # this is called 2 times (bug), so make sure it's not already parented to aspect2d
    if self.getParent() != self.windowParent:
      self.wrtReparentTo( self.windowParent )
    if self.preserve:
        if self.preserveWhole:
            if self.getZ() > 1:
                self.setZ(1)
            elif self.getZ() < -1 - self.getHeight():
                self.setZ(-1 - self.getHeight())
            if self.getX() > base.a2dRight - self.getWidth():
                self.setX(base.a2dRight - self.getWidth())
            elif self.getX() < base.a2dLeft:
                self.setX(base.a2dLeft)
        else:
            if self.getZ() > 1:
                self.setZ(1)
            elif self.getZ() < -1 + self.headerHeight:
                self.setZ(-1 + self.headerHeight)
            if self.getX() > base.a2dRight - self.headerHeight:
                self.setX(base.a2dRight - self.headerHeight)
            elif self.getX() < base.a2dLeft + self.headerHeight - self.getWidth():
                self.setX(base.a2dLeft + self.headerHeight - self.getWidth())
    #else: #Window moved beyond reach. Destroy window?
  # resize functions
  def resize( self, mPos, offset ):
    mXPos = max( min( mPos.getX(), self.maxVirtualSize[0] ), self.minVirtualSize[0])
    mZPos = max( min( mPos.getZ(), -self.minVirtualSize[1] ), -self.maxVirtualSize[1]-self.headerHeight)
    self.windowResize.setPos( mXPos-self.resizeSize/2., 0, mZPos+self.resizeSize/2. )
    self.windowResizeBackground.setPos( mXPos-self.resizeSize/2., 0, mZPos+self.resizeSize/2. )
    self['frameSize'] = (0, mXPos, 0, mZPos)
    self.windowHeaderLeft.setPos( self.headerHeight/2., 0, -self.headerHeight/2. )
    self.windowHeaderLeft.setScale( self.headerHeight, 1, self.headerHeight )
    self.windowHeaderCenter.setPos( mXPos/2., 0, -self.headerHeight/2. )
    self.windowHeaderCenter.setScale( mXPos - self.headerHeight*2., 1, self.headerHeight )
    self.windowHeaderRight.setPos( mXPos-self.headerHeight/2., 0, -self.headerHeight/2. )
    self.windowHeaderRight.setScale( self.headerHeight, 1, self.headerHeight )
    self.contentWindow['frameSize'] = ( 0, mXPos, mZPos+self.headerHeight, 0)
    self.textNodePath.setPos( mXPos/2., 0, -self.headerHeight/3.*2. )
    # show and hide that small background for the window sizer
    if mXPos == self.maxVirtualSize[0] and \
       mZPos == -self.maxVirtualSize[1]-self.headerHeight:
      self.windowResizeBackground.hide()
    else:
      self.windowResizeBackground.show()
  
  def resizeTask( self, task=None ):
    mPos = aspect2dMouseNode.getPos( self )+self.offset
    self.resize( mPos, self.offset )
    return task.cont
  def startResizeDrag( self, param ):
    self.offset  = self.windowResize.getPos( aspect2dMouseNode )
    taskMgr.remove( self.taskName )
    taskMgr.add( self.resizeTask, self.taskName )
  def stopResizeDrag( self, param ):
    taskMgr.remove( self.taskName )
    # get the window to the front
    self.wrtReparentTo( self.windowParent )
  def addHorizontal(self, widgets):
      """
      Accepts a list of directgui objects which are added to a horizontal box, which is then added to the vertical stack.
      """
      hbox = boxes.HBox()
      for widget in widgets:
          widget.setScale(0.05)
          hbox.pack(widget)
          self.widgets.append(widget)
      self.box.pack(hbox)
      self.updateMaxSize()
  
  def addVertical(self, widgets):
      """
      Accepts a list of directgui objects which are added to a vertical box, which is then added to the vertical stack.
      May cause funky layout results.
      """
      for widget in widgets:
          widget.setScale(0.05)
          self.box.pack(widget)
          self.widgets.append(widget)
      self.updateMaxSize()
  
  def add(self, widgets):
      """Shortcut function for addVertical"""
      self.addVertical(widgets)
  
  def updateMaxSize(self):
      """Updates the max canvas size to include all items packed.
      Window is resized to show all contents."""
      bottomLeft, topRight = self.box.getTightBounds()
      self.maxVirtualSize = (topRight[0], -bottomLeft[2])
      if self.minVirtualSize[0] > self.maxVirtualSize[0]:
          self.minVirtualSize = (self.maxVirtualSize[0], self.minVirtualSize[1])
      if self.minVirtualSize[1] > self.maxVirtualSize[1]:
          self.minVirtualSize = (self.minVirtualSize[0], self.maxVirtualSize[1]+self.headerHeight)
      self.contentWindow['canvasSize'] = ( 0, self.maxVirtualSize[0], -self.maxVirtualSize[1],  0)
      self.backgroundColor['frameSize'] = ( 0, self.maxVirtualSize[0], -self.maxVirtualSize[1], 0 )
      # For CityMania Onlue
      self.reset()
      self.center()
  
  def reset(self):
    """Poorly named function that resizes window to fit all contents"""
    self.resize( Vec3(self.maxVirtualSize[0], 0, -self.maxVirtualSize[1]-self.headerHeight), Vec3(0,0,0))

  def center(self):
      """Centers window on screen"""
      self.setPos(-self.maxVirtualSize[0]/2.0, 0, (self.maxVirtualSize[1]+self.headerHeight)/2.0)
  
  def getEntries(self):
        """Returns the fields for any entires"""
        entries = []
        for widget in self.widgets:
            if isinstance(widget, DirectEntry):
                entries.append(widget.get())
        return entries

  def addScrolledList(self, items, numItemsVisible = 4, itemHeight = 1):
        '''Adds a list of items into a scrolled list'''
        scrolled_list = DirectScrolledList(
            decButton_pos= (0.35, 0, 5),
            decButton_text = "Up",
            decButton_borderWidth = (0.005, 0.005),
            
            incButton_pos= (0.35, 0, -5),
            incButton_text = "Down",
            incButton_borderWidth = (0.005, 0.005),
            
            frameSize = (-5, 5, -5, 5),
            frameColor = (1,0,1,0.5),
            pos = (-1, 0, 0),
            items = items,
            numItemsVisible = numItemsVisible,
            forceHeight = itemHeight,
            itemFrame_frameSize = (-5, 5, -5, 5),
            itemFrame_pos = (0.35, 0, 0.4),
            scale = (0.05),
            #parent = (aspect2d),
            )
        scrolled_list.updateFrameStyle()
        self.add([scrolled_list])
示例#6
0
class DirectWindow(DirectFrame):
    def __init__(
        self,
        pos=(-.5, .5),
        title='Title',
        curSize=(1, 1),
        maxSize=(1, 1),
        minSize=(.5, .5),
        backgroundColor=(0, 0, 1, .6),
        borderColor=(1, 1, 1, 1),
        titleColor=(1, 1, 1, 1),
        borderSize=0.04,
        titleSize=0.06,
        closeButton=False,
        resizeButton=True,
        windowParent=aspect2d,
        preserve=True,
        preserveWhole=True,
    ):
        self.preserve = preserve
        self.preserveWhole = preserveWhole
        self.windowParent = windowParent
        self.windowPos = pos
        DirectFrame.__init__(
            self,
            parent=aspect2d,
            pos=(self.windowPos[0], 0, self.windowPos[1]),
            frameColor=(0, 0, 0, 0),
            frameTexture=loader.loadTexture(DIRECTORY + 'transparent.png'))
        self.setTransparency(True)

        # the title part of the window, drag around to move the window
        self.headerHeight = titleSize
        h = -self.headerHeight
        self.windowHeaderLeft = DirectButton(
            parent=self,
            frameTexture=DEFAULT_TITLE_GEOM_LEFT,
            frameSize=(-.5, .5, -.5, .5),
            borderWidth=(0, 0),
            relief=DGG.FLAT,
            frameColor=titleColor,
        )
        self.windowHeaderCenter = DirectButton(
            parent=self,
            frameTexture=DEFAULT_TITLE_GEOM_CENTER,
            frameSize=(-.5, .5, -.5, .5),
            borderWidth=(0, 0),
            relief=DGG.FLAT,
            frameColor=titleColor,
        )
        if closeButton:
            rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT_CLOSE
            command = self.destroy
        else:
            rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT
            command = None
        self.windowHeaderRight = DirectButton(parent=self,
                                              frameTexture=rightTitleGeom,
                                              frameSize=(-.5, .5, -.5, .5),
                                              borderWidth=(0, 0),
                                              relief=DGG.FLAT,
                                              frameColor=titleColor,
                                              command=command)

        self.windowHeaderLeft.setTransparency(True)
        self.windowHeaderCenter.setTransparency(True)
        self.windowHeaderRight.setTransparency(True)

        self.windowHeaderLeft.bind(DGG.B1PRESS, self.startWindowDrag)
        self.windowHeaderCenter.bind(DGG.B1PRESS, self.startWindowDrag)
        self.windowHeaderRight.bind(DGG.B1PRESS, self.startWindowDrag)

        # this is not handled correctly, if a window is dragged which has been
        # created before another it will not be released
        # check the bugfixed startWindowDrag function
        #self.windowHeader.bind(DGG.B1RELEASE,self.stopWindowDrag)

        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.attachNewNode(text)
        self.textNodePath.setScale(self.headerHeight * 0.8)

        # the content part of the window, put stuff beneath
        # contentWindow.getCanvas() to put it into it
        self.maxVirtualSize = maxSize
        self.minVirtualSize = minSize
        self.resizeSize = borderSize
        self.contentWindow = DirectScrolledFrame(
            parent=self,
            pos=(0, 0, -self.headerHeight),
            canvasSize=(0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1]),
            frameColor=(
                0, 0, 0,
                0),  # defines the background color of the resize-button
            relief=DGG.FLAT,
            borderWidth=(0, 0),
            verticalScroll_frameSize=[0, self.resizeSize, 0, 1],
            horizontalScroll_frameSize=[0, 1, 0, self.resizeSize],

            # resize the scrollbar according to window size
            verticalScroll_resizeThumb=False,
            horizontalScroll_resizeThumb=False,
            # define the textures for the scrollbars
            verticalScroll_frameTexture=VERTICALSCROLL_FRAMETEXTURE,
            verticalScroll_incButton_frameTexture=
            VERTICALSCROLL_INCBUTTON_FRAMETEXTURE,
            verticalScroll_decButton_frameTexture=
            VERTICALSCROLL_DECBUTTON_FRAMETEXTURE,
            verticalScroll_thumb_frameTexture=VERTICALSCROLL_TUMB_FRAMETEXTURE,
            horizontalScroll_frameTexture=HORIZONTALSCROLL_FRAMETEXTURE,
            horizontalScroll_incButton_frameTexture=
            HORIZONTALSCROLL_INCBUTTON_FRAMETEXTURE,
            horizontalScroll_decButton_frameTexture=
            HORIZONTALSCROLL_DECBUTTON_FRAMETEXTURE,
            horizontalScroll_thumb_frameTexture=
            HORIZONTALSCROLL_TUMB_FRAMETEXTURE,
            # make all flat, so the texture is as we want it
            verticalScroll_relief=DGG.FLAT,
            verticalScroll_thumb_relief=DGG.FLAT,
            verticalScroll_decButton_relief=DGG.FLAT,
            verticalScroll_incButton_relief=DGG.FLAT,
            horizontalScroll_relief=DGG.FLAT,
            horizontalScroll_thumb_relief=DGG.FLAT,
            horizontalScroll_decButton_relief=DGG.FLAT,
            horizontalScroll_incButton_relief=DGG.FLAT,
            # colors
            verticalScroll_frameColor=borderColor,
            verticalScroll_incButton_frameColor=borderColor,
            verticalScroll_decButton_frameColor=borderColor,
            verticalScroll_thumb_frameColor=borderColor,
            horizontalScroll_frameColor=borderColor,
            horizontalScroll_incButton_frameColor=borderColor,
            horizontalScroll_decButton_frameColor=borderColor,
            horizontalScroll_thumb_frameColor=borderColor,
        )
        self.contentWindow.setTransparency(True)

        # background color
        self.backgroundColor = DirectFrame(
            parent=self.contentWindow.getCanvas(),
            frameSize=(0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1]),
            frameColor=backgroundColor,
            relief=DGG.FLAT,
            borderWidth=(.01, .01),
        )
        self.backgroundColor.setTransparency(True)

        # Add a box
        self.box = boxes.VBox(parent=self.getCanvas())

        # is needed for some nicer visuals of the resize button (background)
        self.windowResizeBackground = DirectButton(
            parent=self,
            frameSize=(-.5, .5, -.5, .5),
            borderWidth=(0, 0),
            scale=(self.resizeSize, 1, self.resizeSize),
            relief=DGG.FLAT,
            frameColor=backgroundColor,
        )
        # the resize button of the window
        if resizeButton:
            self.windowResize = DirectButton(
                parent=self,
                frameSize=(-.5, .5, -.5, .5),
                borderWidth=(0, 0),
                scale=(self.resizeSize, 1, self.resizeSize),
                relief=DGG.FLAT,
                frameTexture=DEFAULT_RESIZE_GEOM,
                frameColor=borderColor,
            )
            self.windowResize.setTransparency(True)
            self.windowResize.bind(DGG.B1PRESS, self.startResizeDrag)
            self.windowResize.bind(DGG.B1RELEASE, self.stopResizeDrag)
        else:
            self.windowResize = DirectFrame()

        # offset then clicking on the resize button from the mouse to the resizebutton
        # position, required to calculate the position / scaling
        self.offset = None
        self.taskName = "resizeTask-%s" % str(hash(self))

        # do sizing of the window (minimum)
        #self.resize( Vec3(0,0,0), Vec3(0,0,0) )
        # maximum
        #self.resize( Vec3(100,0,-100), Vec3(0,0,0) )
        self.resize(Vec3(curSize[0], 0, -curSize[1]), Vec3(0, 0, 0))
        self.widgets = []

    def getCanvas(self):
        return self.contentWindow.getCanvas()

    # dragging functions
    def startWindowDrag(self, param):
        self.wrtReparentTo(aspect2dMouseNode)
        self.ignoreAll()
        self.accept('mouse1-up', self.stopWindowDrag)

    def stopWindowDrag(self, param=None):
        # this is called 2 times (bug), so make sure it's not already parented to aspect2d
        if self.getParent() != self.windowParent:
            self.wrtReparentTo(self.windowParent)
        if self.preserve:
            if self.preserveWhole:
                if self.getZ() > 1:
                    self.setZ(1)
                elif self.getZ() < -1 - self.getHeight():
                    self.setZ(-1 - self.getHeight())
                if self.getX() > base.a2dRight - self.getWidth():
                    self.setX(base.a2dRight - self.getWidth())
                elif self.getX() < base.a2dLeft:
                    self.setX(base.a2dLeft)
            else:
                if self.getZ() > 1:
                    self.setZ(1)
                elif self.getZ() < -1 + self.headerHeight:
                    self.setZ(-1 + self.headerHeight)
                if self.getX() > base.a2dRight - self.headerHeight:
                    self.setX(base.a2dRight - self.headerHeight)
                elif self.getX(
                ) < base.a2dLeft + self.headerHeight - self.getWidth():
                    self.setX(base.a2dLeft + self.headerHeight -
                              self.getWidth())
        #else: #Window moved beyond reach. Destroy window?

    # resize functions
    def resize(self, mPos, offset):
        mXPos = max(min(mPos.getX(), self.maxVirtualSize[0]),
                    self.minVirtualSize[0])
        mZPos = max(min(mPos.getZ(), -self.minVirtualSize[1]),
                    -self.maxVirtualSize[1] - self.headerHeight)
        self.windowResize.setPos(mXPos - self.resizeSize / 2., 0,
                                 mZPos + self.resizeSize / 2.)
        self.windowResizeBackground.setPos(mXPos - self.resizeSize / 2., 0,
                                           mZPos + self.resizeSize / 2.)
        self['frameSize'] = (0, mXPos, 0, mZPos)
        self.windowHeaderLeft.setPos(self.headerHeight / 2., 0,
                                     -self.headerHeight / 2.)
        self.windowHeaderLeft.setScale(self.headerHeight, 1, self.headerHeight)
        self.windowHeaderCenter.setPos(mXPos / 2., 0, -self.headerHeight / 2.)
        self.windowHeaderCenter.setScale(mXPos - self.headerHeight * 2., 1,
                                         self.headerHeight)
        self.windowHeaderRight.setPos(mXPos - self.headerHeight / 2., 0,
                                      -self.headerHeight / 2.)
        self.windowHeaderRight.setScale(self.headerHeight, 1,
                                        self.headerHeight)
        self.contentWindow['frameSize'] = (0, mXPos, mZPos + self.headerHeight,
                                           0)
        self.textNodePath.setPos(mXPos / 2., 0, -self.headerHeight / 3. * 2.)
        # show and hide that small background for the window sizer
        if mXPos == self.maxVirtualSize[0] and \
           mZPos == -self.maxVirtualSize[1]-self.headerHeight:
            self.windowResizeBackground.hide()
        else:
            self.windowResizeBackground.show()

    def resizeTask(self, task=None):
        mPos = aspect2dMouseNode.getPos(self) + self.offset
        self.resize(mPos, self.offset)
        return task.cont

    def startResizeDrag(self, param):
        self.offset = self.windowResize.getPos(aspect2dMouseNode)
        taskMgr.remove(self.taskName)
        taskMgr.add(self.resizeTask, self.taskName)

    def stopResizeDrag(self, param):
        taskMgr.remove(self.taskName)
        # get the window to the front
        self.wrtReparentTo(self.windowParent)

    def addHorizontal(self, widgets):
        """
      Accepts a list of directgui objects which are added to a horizontal box, which is then added to the vertical stack.
      """
        hbox = boxes.HBox()
        for widget in widgets:
            widget.setScale(0.05)
            hbox.pack(widget)
            self.widgets.append(widget)
        self.box.pack(hbox)
        self.updateMaxSize()

    def addVertical(self, widgets):
        """
      Accepts a list of directgui objects which are added to a vertical box, which is then added to the vertical stack.
      May cause funky layout results.
      """
        for widget in widgets:
            widget.setScale(0.05)
            self.box.pack(widget)
            self.widgets.append(widget)
        self.updateMaxSize()

    def add(self, widgets):
        """Shortcut function for addVertical"""
        self.addVertical(widgets)

    def updateMaxSize(self):
        """Updates the max canvas size to include all items packed.
      Window is resized to show all contents."""
        bottomLeft, topRight = self.box.getTightBounds()
        self.maxVirtualSize = (topRight[0], -bottomLeft[2])
        if self.minVirtualSize[0] > self.maxVirtualSize[0]:
            self.minVirtualSize = (self.maxVirtualSize[0],
                                   self.minVirtualSize[1])
        if self.minVirtualSize[1] > self.maxVirtualSize[1]:
            self.minVirtualSize = (self.minVirtualSize[0],
                                   self.maxVirtualSize[1] + self.headerHeight)
        self.contentWindow['canvasSize'] = (0, self.maxVirtualSize[0],
                                            -self.maxVirtualSize[1], 0)
        self.backgroundColor['frameSize'] = (0, self.maxVirtualSize[0],
                                             -self.maxVirtualSize[1], 0)
        # For CityMania Onlue
        self.reset()
        self.center()

    def reset(self):
        """Poorly named function that resizes window to fit all contents"""
        self.resize(
            Vec3(self.maxVirtualSize[0], 0,
                 -self.maxVirtualSize[1] - self.headerHeight), Vec3(0, 0, 0))

    def center(self):
        """Centers window on screen"""
        self.setPos(-self.maxVirtualSize[0] / 2.0, 0,
                    (self.maxVirtualSize[1] + self.headerHeight) / 2.0)

    def getEntries(self):
        """Returns the fields for any entires"""
        entries = []
        for widget in self.widgets:
            if isinstance(widget, DirectEntry):
                entries.append(widget.get())
        return entries

    def addScrolledList(self, items, numItemsVisible=4, itemHeight=1):
        '''Adds a list of items into a scrolled list'''
        scrolled_list = DirectScrolledList(
            decButton_pos=(0.35, 0, 5),
            decButton_text="Up",
            decButton_borderWidth=(0.005, 0.005),
            incButton_pos=(0.35, 0, -5),
            incButton_text="Down",
            incButton_borderWidth=(0.005, 0.005),
            frameSize=(-5, 5, -5, 5),
            frameColor=(1, 0, 1, 0.5),
            pos=(-1, 0, 0),
            items=items,
            numItemsVisible=numItemsVisible,
            forceHeight=itemHeight,
            itemFrame_frameSize=(-5, 5, -5, 5),
            itemFrame_pos=(0.35, 0, 0.4),
            scale=(0.05),
            #parent = (aspect2d),
        )
        scrolled_list.updateFrameStyle()
        self.add([scrolled_list])