Example #1
0
class CustomListbox(UpdateListbox):
    remake_func = widget.causes_rebuild("_remake_func")
    rebuild_func = widget.causes_rebuild("_rebuild_func")

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        self.key_list = kwargs.pop("key_list", None)
        self.remake_func = kwargs.pop("remake_func", lambda value: None)
        self.rebuild_func = kwargs.pop("rebuild_func", lambda value: None)
        super(CustomListbox, self).__init__(parent, *args, **kwargs)

    def make_element(self):
        base = super(CustomListbox, self).make_element()
        self.remake_func(base)
        return base

    def update_element(self, element, list_index):
        if 0 <= list_index < len(self.list):
            if (self.key_list is not None):
                self.rebuild_func(element, self.list[list_index],
                                  self.key_list[list_index])
            else:
                self.rebuild_func(element, self.list[list_index])
        else:
            if (self.key_list is not None):
                self.rebuild_func(element, None, None)
            else:
                self.rebuild_func(element, None)
Example #2
0
class YesNoDialog(TextDialog):
    """A Dialog with YES and NO buttons which exits the dialog with True and
    False return values, respectively.
    """
    yes_type = widget.causes_rebuild("_yes_type")
    no_type = widget.causes_rebuild("_no_type")

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent

        self.yes_type = kwargs.pop("yes_type", "yes")
        self.no_type = kwargs.pop("no_type", "no")
        self.invert_enter = kwargs.pop("invert_enter", False)
        self.invert_escape = kwargs.pop("invert_escape", False)

        super(YesNoDialog, self).__init__(parent, *args, **kwargs)

        self.yes_button = button.ExitDialogButton(self, (-.1, -.99),
                                                  (-.3, -.1),
                                                  anchor=constants.BOTTOM_LEFT,
                                                  exit_code=True,
                                                  default=False)

        self.no_button = button.ExitDialogButton(self, (-.9, -.99), (-.3, -.1),
                                                 anchor=constants.BOTTOM_RIGHT,
                                                 exit_code=False,
                                                 default=False)

        self.add_key_handler(pygame.K_RETURN, self.on_return)
        self.add_key_handler(pygame.K_KP_ENTER, self.on_return)
        self.add_key_handler(pygame.K_ESCAPE, self.on_escape)

    def rebuild(self):
        super(YesNoDialog, self).rebuild()

        self.yes_button.text = g.buttons[self.yes_type]['text']
        self.yes_button.underline = g.buttons[self.yes_type]['pos']
        self.yes_button.hotkey = g.buttons[self.yes_type]['key']
        self.no_button.text = g.buttons[self.no_type]['text']
        self.no_button.underline = g.buttons[self.no_type]['pos']
        self.no_button.hotkey = g.buttons[self.no_type]['key']

    def on_return(self, event):
        if event and event.type == pygame.KEYUP:
            return
        if self.invert_enter:
            self.no_button.activate_with_sound(event)
        else:
            self.yes_button.activate_with_sound(event)

    def on_escape(self, event):
        if event and event.type == pygame.KEYUP:
            return
        if self.invert_escape:
            self.yes_button.activate_with_sound(event)
        else:
            self.no_button.activate_with_sound(event)
Example #3
0
class YesNoDialog(TextDialog):
    yes_type = widget.causes_rebuild("_yes_type")
    no_type = widget.causes_rebuild("_no_type")

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent

        self.yes_type = kwargs.pop("yes_type", "yes")
        self.no_type = kwargs.pop("no_type", "no")
        self.invert_enter = kwargs.pop("invert_enter", False)
        self.invert_escape = kwargs.pop("invert_escape", False)

        super(YesNoDialog, self).__init__(parent, *args, **kwargs)

        self.yes_button = button.ExitDialogButton(self, (-.1, -.99),
                                                  (-.3, -.1),
                                                  anchor=constants.BOTTOM_LEFT,
                                                  exit_code=True,
                                                  default=False)

        self.no_button = button.ExitDialogButton(self, (-.9, -.99), (-.3, -.1),
                                                 anchor=constants.BOTTOM_RIGHT,
                                                 exit_code=False,
                                                 default=False)

        self.add_key_handler(pygame.K_RETURN, self.on_return)
        self.add_key_handler(pygame.K_ESCAPE, self.on_escape)

    def rebuild(self):
        super(YesNoDialog, self).rebuild()

        self.yes_button.text = g.buttons[self.yes_type]
        self.yes_button.hotkey = g.buttons[self.yes_type + "_hotkey"]
        self.no_button.text = g.buttons[self.no_type]
        self.no_button.hotkey = g.buttons[self.no_type + "_hotkey"]

    def on_return(self, event):
        if event.type == pygame.KEYUP: return
        if self.invert_enter:
            self.no_button.activate_with_sound(event)
        else:
            self.yes_button.activate_with_sound(event)

    def on_escape(self, event):
        if event.type == pygame.KEYUP: return
        if self.invert_escape:
            self.yes_button.activate_with_sound(event)
        else:
            self.no_button.activate_with_sound(event)
Example #4
0
class MessageDialog(TextDialog):
    """A Dialog with an OK button that exits the dialog, return value of None"""

    ok_type = widget.causes_rebuild("_ok_type")
    def __init__(self, parent, **kwargs):
        self.parent = parent

        self.ok_type = kwargs.pop("ok_type", "ok")

        super(MessageDialog, self).__init__(parent, **kwargs)

        self.ok_button = button.ExitDialogButton(self, (-.5,-.99), (-.3,-.1),
                                                 anchor=constants.BOTTOM_CENTER)

        self.add_key_handler(pygame.K_RETURN, self.on_return)
        self.add_key_handler(pygame.K_KP_ENTER, self.on_return)

    def on_return(self, event):
        if event.type == pygame.KEYUP: return
        self.ok_button.activate_with_sound(event)

    def rebuild(self):
        super(MessageDialog, self).rebuild()

        self.ok_button.text      = g.buttons[self.ok_type]['text']
        self.ok_button.underline = g.buttons[self.ok_type]['pos']
        self.ok_button.hotkey    = g.buttons[self.ok_type]['key']
Example #5
0
class SimpleMenuDialog(Dialog):
    width = widget.causes_rebuild("_width")

    def __init__(self, *args, **kwargs):
        buttons = kwargs.pop("buttons")
        width = kwargs.pop("width", .20)
        super(SimpleMenuDialog, self).__init__(*args, **kwargs)

        self.size = (-1, -1)
        self.pos = (0, 0)
        self.anchor = constants.TOP_LEFT

        num_buttons = len(buttons)
        height = (.06 * num_buttons) + .01
        self.width = width
        self.button_panel = \
            widget.BorderedWidget(self, (-.5, -.5), (self.width + .02, height),
                                  anchor=constants.MID_CENTER,
                                  background_color=g.colors["dark_blue"],
                                  border_color=g.colors["white"],
                                  borders=constants.ALL)

        y_pos = .01
        for button in buttons:
            if button.parent is not None:
                button.remove_hooks()
            button.parent = self.button_panel
            button.add_hooks()

            button.pos = (.01, y_pos)
            button.size = (self.width, .05)
            button.text_shrink_factor = .70

            y_pos += .06
Example #6
0
class SimpleMenuDialog(Dialog):
    width = widget.causes_rebuild("_width")

    def __init__(self, *args, **kwargs):
        buttons = kwargs.pop("buttons", [])
        width = kwargs.pop("width", .20)
        super(SimpleMenuDialog, self).__init__(*args, **kwargs)

        self.size = (-1, -1)
        self.pos = (0, 0)
        self.anchor = constants.TOP_LEFT
        self.width = width

        self.button_panel = \
            widget.BorderedWidget(self, (-.5, -.5), (0.22, 0.43),
                                  anchor=constants.MID_CENTER,
                                  background_color=g.colors["dark_blue"],
                                  border_color=g.colors["white"],
                                  borders=constants.ALL)

        self.buttons = buttons

    @property
    def buttons(self):
        return self._buttons

    @buttons.setter
    def buttons(self, buttons):
        if (hasattr(self, '_buttons') and not self._buttons is None):
            for button in self._buttons:
                if button.parent is not None:
                    button.remove_hooks()
                    button.parent = None

        self._buttons = buttons
        self.needs_rebuild = True

    def rebuild(self):
        num_buttons = len(self.buttons)
        height = (.06 * num_buttons) + .01

        self.button_panel.size = (self.width + .02, height)

        y_pos = .01
        for button in self.buttons:
            if button.parent is not None:
                button.remove_hooks()
            button.parent = self.button_panel
            button.add_hooks()

            button.pos = (.01, y_pos)
            button.size = (self.width, .05)
            button.text_size = 24

            y_pos += .06

        super(SimpleMenuDialog, self).rebuild()
Example #7
0
class TextEntryDialog(TextDialog):
    ok_type = widget.causes_rebuild("_ok_type")

    def __init__(self,
                 parent,
                 pos=(-.50, -.50),
                 size=(.40, .10),
                 anchor=constants.MID_CENTER,
                 **kwargs):
        kwargs.setdefault('wrap', False)
        kwargs.setdefault("shrink_factor", 1)
        kwargs.setdefault("text_size", 20)
        self.default_text = kwargs.pop("default_text", "")
        self.ok_type = kwargs.pop("ok_type", "ok")
        super(TextEntryDialog, self).__init__(parent, pos, size, anchor,
                                              **kwargs)

        self.text_field = text.EditableText(self, (0, -.50), (-.80, -.50),
                                            borders=constants.ALL,
                                            base_font="normal")

        self.ok_button = button.FunctionButton(self, (-.82, -.50),
                                               (-.18, -.50),
                                               function=self.return_text)

        self.add_key_handler(pygame.K_RETURN, self.return_text)
        self.add_key_handler(pygame.K_KP_ENTER, self.return_text)
        self.add_key_handler(pygame.K_ESCAPE, self.return_nothing)

    def rebuild(self):
        super(TextEntryDialog, self).rebuild()

        self.ok_button.text = g.buttons[self.ok_type]['text']
        self.ok_button.underline = g.buttons[self.ok_type]['pos']
        self.ok_button.hotkey = g.buttons[self.ok_type]['key']

    def show(self):
        self.text_field.text = self.default_text
        self.text_field.cursor_pos = len(self.default_text)
        return super(TextEntryDialog, self).show()

    def return_nothing(self, event):
        if event and event.type == pygame.KEYUP:
            return
        raise constants.ExitDialog, ""

    def return_text(self, event=None):
        if event and event.type == pygame.KEYUP:
            return
        raise constants.ExitDialog, self.text_field.text
Example #8
0
class ChoiceDescriptionDialog(ChoiceDialog):
    key_list = widget.causes_rebuild("_key_list")

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        self.key_list = kwargs.pop("key_list", None)
        self.desc_func = kwargs.pop("desc_func", lambda pane, key: NullDialog)

        super(ChoiceDescriptionDialog, self).__init__(parent, *args, **kwargs)

        self.description_pane = \
            widget.BorderedWidget(self, (-1, 0), (-.45, -.85),
                                  anchor = constants.TOP_RIGHT)


    def make_listbox(self):
        return listbox.UpdateListbox(self, (0, 0), (-.53, -.85),
                                     anchor=constants.TOP_LEFT,
                                     update_func=self.handle_update)

    def rebuild(self):
        self.listbox.needs_rebuild = True
        list_pos = self.listbox.list_pos

        if 0 <= list_pos < len(self.list):
            if self.key_list:
                assert len(self.list) <= len(self.key_list), \
                       "Key list must be at least as long as display list."

                key = self.key_list[self.listbox.list_pos]
            else:
                key = self.list[self.listbox.list_pos]
        else:
            key = None

        # Safely clear all the description pane's children.
        self.description_pane.remove_hooks()
        self.description_pane.children = []
        self.description_pane.add_hooks()

        self.desc_func(self.description_pane, key)

        super(ChoiceDescriptionDialog, self).rebuild()

    def handle_update(self, item):
        self.needs_rebuild = True
Example #9
0
class Image(widget.Widget):
    image = widget.causes_rebuild("_image")

    def __init__(self,
                 parent,
                 pos,
                 size=(1, 1),
                 anchor=constants.TOP_LEFT,
                 image=None):
        super(Image, self).__init__(parent, pos, size, anchor)

        self.old_size = None

        if image:
            if type(image) is str:
                image = pygame.image.load(image)

            self.image = image.convert_alpha()

    def _calc_size(self):
        size = list(super(Image, self)._calc_size())
        if size[0] == size[1] == 0:
            raise ValueError, "One image dimension must be specified!"

        image_size = self.image.get_size()
        ratio = image_size[0] / float(image_size[1])
        if size[0] == 0:
            size[0] = int(size[1] * ratio)
        elif size[1] == 0:
            size[1] = int(size[0] / ratio)

        return tuple(size)

    def rescale(self):
        self.scaled_image = scale(self.image, self.real_size)

    def resize(self):
        super(Image, self).resize()
        if self.real_size != self.old_size:
            self.rescale()
            self.old_size = self.real_size

    def redraw(self):
        super(Image, self).redraw()
        self.surface.blit(self.scaled_image, (0, 0))
Example #10
0
class ChoiceDialog(YesNoDialog):
    list = widget.causes_rebuild("_list")

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        self.list = kwargs.pop("list", [])
        self.default = kwargs.pop("default", None)
        kwargs.setdefault("yes_type", "ok")
        kwargs.setdefault("no_type", "back")
        kwargs.setdefault("background_color", g.colors["clear"])

        super(ChoiceDialog, self).__init__(parent, *args, **kwargs)

        self.listbox = self.make_listbox()
        self.add_handler(constants.DOUBLECLICK, self.handle_double_click, 200)

        self.yes_button.exit_code_func = self.return_list_pos
        self.no_button.exit_code = None

    def handle_double_click(self, event):
        if self.listbox.is_over(event.pos):
            self.yes_button.activated(None)

    def make_listbox(self):
        return listbox.Listbox(self, (0, 0), (-1, -.85),
                               anchor=constants.TOP_LEFT)

    def return_list_pos(self):
        return self.listbox.list_pos

    def show(self):
        if type(self.default) == int:
            self.listbox.list_pos = self.default
        elif type(self.default) == str and self.default in self.list:
            self.listbox.list_pos = self.list.index(self.default)
        else:
            self.listbox.list_pos = 0
        self.listbox.auto_scroll = True

        return super(ChoiceDialog, self).show()

    def rebuild(self):
        self.listbox.list = self.list
        super(ChoiceDialog, self).rebuild()
Example #11
0
class MessageDialog(TextDialog):
    ok_type = widget.causes_rebuild("_ok_type")

    def __init__(self, parent, **kwargs):
        self.parent = parent

        self.ok_type = kwargs.pop("ok_type", "ok")

        super(MessageDialog, self).__init__(parent, **kwargs)

        self.ok_button = button.ExitDialogButton(
            self, (-.5, -.99), (-.3, -.1), anchor=constants.BOTTOM_CENTER)

        self.add_key_handler(pygame.K_RETURN, self.on_return)

    def on_return(self, event):
        if event.type == pygame.KEYUP: return
        self.ok_button.activate_with_sound(event)

    def rebuild(self):
        super(MessageDialog, self).rebuild()

        self.ok_button.text = g.buttons[self.ok_type]
        self.ok_button.hotkey = g.buttons[self.ok_type + "_hotkey"]
Example #12
0
class Slider(button.Button):
    slider_color = widget.causes_redraw("_slider_color")
    slider_pos = widget.causes_rebuild("_slider_pos")
    slider_max = widget.causes_rebuild("_slider_max")
    slider_size = widget.causes_rebuild("_slider_size")
    horizontal = widget.causes_rebuild("_horizontal")

    def __init__(self, parent, pos = (-1,0), size = (-.1, -1),
                 anchor = constants.TOP_RIGHT, borders = constants.ALL,
                 border_color=None, background_color=None, slider_color=None,
                 slider_pos=0, slider_max=10, slider_size=5, horizontal=False,
                 **kwargs):
        kwargs.setdefault("priority", 80)
        super(Slider, self).__init__(parent, pos, size, anchor=anchor, **kwargs)

        self.borders = borders
        self.border_color = border_color or g.colors["white"]
        self.background_color = background_color or g.colors["dark_blue"]
        self.selected_color = self.background_color
        self.unselected_color = self.background_color
        self.slider_color = slider_color or g.colors["light_blue"]

        self.slider_pos = slider_pos
        self.slider_max = slider_max
        self.slider_size = slider_size
        self.horizontal = horizontal

        self.drag_state = None
        self.button = button.Button(self, pos = None, size = None,
                                    anchor = constants.TOP_LEFT,
                                    border_color = self.border_color,
                                    selected_color = self.slider_color,
                                    unselected_color = self.slider_color,
                                    priority = self.priority - 5)

    def redraw(self):
        super(Slider, self).redraw()
        self.button.selected_color = self.slider_color
        self.button.unselected_color = self.slider_color

    def add_hooks(self):
        super(Slider, self).add_hooks()
        self.parent.add_handler(constants.DRAG, self.handle_drag)
        self.parent.add_handler(constants.CLICK, self.handle_click, 50)

    def remove_hooks(self):
        super(Slider, self).remove_hooks()
        self.parent.remove_handler(constants.DRAG, self.handle_drag)
        self.parent.remove_handler(constants.CLICK, self.handle_click)

    def _calc_length(self, items):
        return items / float(self.slider_size + self.slider_max)

    def rebuild(self):
        super(Slider, self).rebuild()
        self.needs_resize = True

    def resize(self):
        super(Slider, self).resize()
        bar_start = self._calc_length(self.slider_pos)
        bar_length = self._calc_length(self.slider_size)

        if self.horizontal:
            self.button.pos = (-bar_start, 0)
            self.button.size = (-bar_length, -1)
            borders = [constants.TOP, constants.BOTTOM]

            self.button.resize()
            real_pos = self.button.real_pos[0]
            real_size = self.button.real_size[0]
            if real_pos == 0:
                borders.append(constants.LEFT)
            if real_pos + real_size == self.real_size[0]:
                borders.append(constants.RIGHT)
            self.button.borders = tuple(borders)
        else:
            self.button.pos = (0, -bar_start)
            self.button.size = (-1, -bar_length)
            borders = [constants.LEFT, constants.RIGHT]

            self.button.resize()
            real_pos = self.button.real_pos[1]
            real_size = self.button.real_size[1]
            if real_pos == 0:
                borders.append(constants.TOP)
            if real_pos + real_size == self.real_size[1]:
                borders.append(constants.BOTTOM)
            self.button.borders = tuple(borders)

    def handle_drag(self, event):
        if not self.visible:
            return

        if self.drag_state == None:
            self.start_pos = tuple(event.pos[i]-event.rel[i] for i in range(2))
            self.start_slider_pos = self.slider_pos
            if self.button.is_over(self.start_pos):
                self.drag_state = True
            else:
                self.drag_state = False

        if self.drag_state == True:
            if self.horizontal:
                dir = 0
            else:
                dir = 1

            mouse_pos = pygame.mouse.get_pos()
            rel = mouse_pos[dir] - self.start_pos[dir]
            unit = self._calc_length(1) * self.real_size[dir]
            movement = int( ( rel + (unit / 2.) ) // unit )

            new_pos = self.safe_pos(self.start_slider_pos + movement)
            self.slider_pos = new_pos

            raise constants.Handled

    def safe_pos(self, value):
        return max(0, min(self.slider_max, value))

    def handle_click(self, event):
        if self.drag_state == True:
            self.drag_state = None
            if not self.is_over(pygame.mouse.get_pos()):
                raise constants.Handled
        else:
            self.drag_state = None

    def jump(self, go_lower, big_jump=False, tiny_jump=False):
        if big_jump:
            jump_dist = max(1, self.slider_max // 2)
        elif tiny_jump:
            jump_dist = max(1, self.slider_max // 100)
        else:
            jump_dist = max(1, self.slider_size - 1)
        if go_lower:
            self.slider_pos = self.safe_pos(self.slider_pos - jump_dist)
        else:
            self.slider_pos = self.safe_pos(self.slider_pos + jump_dist)

    def activated(self, event):
        assert event.type == pygame.MOUSEBUTTONUP
        if self.horizontal:
            self.jump(go_lower=(event.pos[0] < self.button.collision_rect[0]))
        else:
            self.jump(go_lower = event.pos[1] < self.button.collision_rect[1])
        raise constants.Handled
Example #13
0
class Button(text.SelectableText):
    hotkey = widget.causes_rebuild("_hotkey")
    force_underline = widget.causes_rebuild("_force_underline")

    # A second layer of property wraps .hotkey, to update key handlers.
    __hotkey = ""

    def _on_set_hotkey(self, value):
        if self.parent and value != self.__hotkey:
            if self.__hotkey:
                self.parent.remove_key_handler(self.__hotkey,
                                               self.handle_event)
            if value:
                self.parent.add_key_handler(value, self.handle_event)
        self.__hotkey = value

    _hotkey = property(lambda self: self.__hotkey, _on_set_hotkey)

    def __init__(self,
                 parent,
                 pos,
                 size=(0, .045),
                 base_font=None,
                 borders=constants.ALL,
                 hotkey="",
                 force_underline=None,
                 text_shrink_factor=.825,
                 priority=100,
                 **kwargs):
        self.parent = parent

        from code.g import get_hotkey, strip_hotkey
        autohotkey = kwargs.pop('autohotkey', False)
        if autohotkey:
            text = kwargs.get('text', "")
            self.hotkey = get_hotkey(text)
            # Strip hotkey info from text
            if 'text' in kwargs: kwargs['text'] = strip_hotkey(text)
        else:
            self.hotkey = hotkey

        self.priority = priority

        super(Button, self).__init__(parent, pos, size, **kwargs)

        self.base_font = base_font or g.font[1]
        self.borders = borders
        self.shrink_factor = text_shrink_factor

        self.force_underline = force_underline

        self.selected = False

    def add_hooks(self):
        super(Button, self).add_hooks()
        if self.parent:
            self.parent.add_handler(constants.MOUSEMOTION, self.watch_mouse,
                                    self.priority)
            self.parent.add_handler(constants.CLICK, self.handle_event,
                                    self.priority)
            if self.hotkey:
                self.parent.add_key_handler(self.hotkey, self.handle_event,
                                            self.priority)

    def remove_hooks(self):
        super(Button, self).remove_hooks()
        if self.parent:
            self.parent.remove_handler(constants.MOUSEMOTION, self.watch_mouse)
            self.parent.remove_handler(constants.CLICK, self.handle_event)
            if self.hotkey:
                self.parent.remove_key_handler(self.hotkey, self.handle_event)

    def rebuild(self):
        old_underline = self.underline
        self.calc_underline()
        if self.underline != old_underline:
            self.needs_redraw = True

        super(Button, self).rebuild()

    def calc_underline(self):
        if self.force_underline != None:
            self.underline = self.force_underline
        elif self.text and self.hotkey and type(self.hotkey) in (str, unicode):
            if self.hotkey in self.text:
                self.underline = self.text.index(self.hotkey)
            elif self.hotkey.lower() in self.text.lower():
                self.underline = self.text.lower().index(self.hotkey.lower())
        else:
            self.underline = -1

    def watch_mouse(self, event):
        """Selects the button if the mouse is over it."""
        if self.visible and getattr(self, "collision_rect", None):
            # This gets called a lot, so it's been optimized.
            select_now = self.is_over(pygame.mouse.get_pos())
            if (self._selected ^ select_now):  # If there's a change.
                self.selected = select_now

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONUP:
            if self.visible and getattr(self, "collision_rect",
                                        None) and self.is_over(event.pos):
                self.activate_with_sound(event)
        elif event.type == pygame.KEYDOWN:
            if self.visible and self.hotkey in (event.unicode, event.key):
                self.activate_with_sound(event)

    def activate_with_sound(self, event):
        """Called when the button is pressed or otherwise triggered.

           This method is called directly by the GUI handler, and should be
           overwritten only to remove the click it plays."""

        from code.g import play_sound
        play_sound("click")
        self.activated(event)

    def activated(self, event):
        """Called when the button is pressed or otherwise triggered."""
        raise constants.Handled
Example #14
0
class Listbox(widget.FocusWidget, text.SelectableText):
    list = widget.causes_rebuild("_list")
    align = widget.causes_redraw("_align")
    list_size = widget.causes_rebuild("_list_size")
    list_pos = widget.causes_rebuild("_list_pos")

    def __init__(self,
                 parent,
                 pos,
                 size,
                 anchor=constants.TOP_LEFT,
                 list=None,
                 list_pos=0,
                 list_size=-20,
                 borders=constants.ALL,
                 align=constants.CENTER,
                 **kwargs):
        super(Listbox, self).__init__(parent,
                                      pos,
                                      size,
                                      anchor=anchor,
                                      **kwargs)

        self.list = list or []
        self.display_elements = []
        self.borders = borders

        self.align = align
        self.list_size = list_size
        self.list_pos = list_pos

        self.auto_scroll = True
        self.scrollbar = scrollbar.UpdateScrollbar(self,
                                                   update_func=self.on_scroll)

    def add_hooks(self):
        super(Listbox, self).add_hooks()
        self.parent.add_handler(constants.CLICK, self.on_click, 90)
        self.parent.add_key_handler(pygame.K_UP, self.got_key)
        self.parent.add_key_handler(pygame.K_DOWN, self.got_key)
        self.parent.add_key_handler(pygame.K_PAGEUP, self.got_key)
        self.parent.add_key_handler(pygame.K_PAGEDOWN, self.got_key)

    def remove_hooks(self):
        super(Listbox, self).remove_hooks()
        self.parent.remove_handler(constants.CLICK, self.on_click)
        self.parent.remove_key_handler(pygame.K_UP, self.got_key)
        self.parent.remove_key_handler(pygame.K_DOWN, self.got_key)
        self.parent.remove_key_handler(pygame.K_PAGEUP, self.got_key)
        self.parent.remove_key_handler(pygame.K_PAGEDOWN, self.got_key)

    def on_scroll(self, scroll_pos):
        self.needs_rebuild = True

    def on_click(self, event):
        if self.collision_rect.collidepoint(event.pos):
            self.has_focus = True
            self.took_focus(self)

            # Figure out which element was clicked...
            local_vert_abs = event.pos[1] - self.collision_rect[1]
            local_vert_pos = local_vert_abs / float(self.collision_rect.height)
            index = int(local_vert_pos * len(self.display_elements))

            # ... and select it.
            self.list_pos = index + self.scrollbar.scroll_pos

    def safe_pos(self, raw_pos):
        return max(0, min(len(self.list) - 1, raw_pos))

    def got_key(self, event):
        if not self.has_focus:
            return

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                new_pos = self.list_pos - 1
            elif event.key == pygame.K_DOWN:
                new_pos = self.list_pos + 1
            elif event.key == pygame.K_PAGEUP:
                new_pos = self.list_pos - (self.scrollbar.window - 1)
            elif event.key == pygame.K_PAGEDOWN:
                new_pos = self.list_pos + (self.scrollbar.window - 1)
            else:
                return

            self.list_pos = self.safe_pos(new_pos)
            self.scrollbar.scroll_to(self.list_pos)
            raise constants.Handled

    def num_elements(self):
        # If self.list_size is negative, we interpret it as a minimum height
        # for each element and calculate the number of elements to show.
        list_size = self.list_size
        if list_size < 0:
            min_height = -list_size
            list_size = max(1,
                            self._make_collision_rect().height // min_height)
        return list_size

    def remake_elements(self):
        list_size = self.num_elements()
        current_size = len(self.display_elements)

        if current_size > list_size:
            # Remove the excess ones.
            for child in self.display_elements[list_size:]:
                child.remove_hooks()
            del self.display_elements[list_size:]
        elif current_size < list_size:
            if current_size > 0:
                self.display_elements[-1].borders = \
                    (constants.LEFT, constants.TOP)

            # Create the new ones.
            for i in range(list_size - current_size):
                self.display_elements.append(self.make_element())

        self.display_elements[-1].borders = (constants.TOP, constants.LEFT,
                                             constants.BOTTOM)

        # Move the scrollbar to the end so that it gets drawn on top.
        self.children.remove(self.scrollbar)
        self.children.append(self.scrollbar)

    def make_element(self):
        return text.SelectableText(self,
                                   None,
                                   None,
                                   anchor=constants.TOP_LEFT,
                                   borders=(constants.TOP, constants.LEFT),
                                   border_color=self.border_color,
                                   selected_color=self.selected_color,
                                   unselected_color=self.unselected_color,
                                   align=self.align)

    def resize(self):
        super(Listbox, self).resize()

        if self.num_elements() != len(self.display_elements):
            self.remake_elements()

        self.scrollbar.resize()

        self.rebuild()

    def rebuild(self):
        self.list_pos = self.safe_pos(self.list_pos)

        if self.needs_resize:
            self.resize()
            return

        window_size = len(self.display_elements)
        list_size = len(self.list)

        self.scrollbar.window = len(self.display_elements)
        self.scrollbar.elements = list_size

        if self.auto_scroll:
            self.auto_scroll = False
            self.scrollbar.center(self.list_pos)

        self.scrollbar.rebuild()

        scrollbar_width = self.scrollbar.real_size[0]
        my_width = self.real_size[0]
        scrollbar_rel_width = scrollbar_width / float(my_width)

        offset = self.scrollbar.scroll_pos
        for index, element in enumerate(self.display_elements):
            list_index = index + offset

            # Position and size the element.
            element.pos = (0, -index / float(window_size))
            element.size = (-1 + scrollbar_rel_width, -1 / float(window_size))

            # Set up the element contents.
            element.selected = (list_index == self.list_pos)
            self.update_element(element, list_index)

        self.needs_redraw = True
        super(Listbox, self).rebuild()

    def update_element(self, element, list_index):
        if 0 <= list_index < len(self.list):
            element.text = self.list[list_index]
        else:
            element.text = ""
Example #15
0
class Scrollbar(widget.Widget):
    scroll_pos = widget.causes_rebuild("_scroll_pos")
    elements = widget.causes_rebuild("_elements")
    window = widget.causes_rebuild("_window")
    horizontal = widget.causes_rebuild("_horizontal")

    def __init__(self,
                 parent,
                 pos=(-1, 0),
                 size=(.025, -1),
                 anchor=constants.TOP_RIGHT,
                 scroll_pos=0,
                 elements=15,
                 window=5,
                 horizontal=False):
        super(Scrollbar, self).__init__(parent, pos, size, anchor)

        self.scroll_pos = scroll_pos
        self.elements = elements
        self.window = window
        self.horizontal = horizontal

        self.slider = slider.UpdateSlider(
            self, (-.5, -.5),
            None,
            border_color="scrollbar_border",
            background_color="scrollbar_background",
            slider_color="scrollbar_background_slider",
            anchor=constants.MID_CENTER,
            horizontal=horizontal,
            update_func=self.on_change)

        self.button1 = _ArrowButton(self, (0, 0),
                                    None,
                                    anchor=constants.TOP_LEFT,
                                    first=True,
                                    horizontal=horizontal,
                                    priority=90)

        self.button2 = _ArrowButton(self, (-1, -1),
                                    None,
                                    anchor=constants.BOTTOM_RIGHT,
                                    first=False,
                                    horizontal=horizontal,
                                    priority=90)

    def resize(self):
        super(Scrollbar, self).resize()
        if self.horizontal:
            long = self.real_size[0]
            short = self.real_size[1]
            size = short / float(long)
            self.button1.size = (-size, -1)
            self.button2.size = (-size, -1)
            self.slider.size = ((size * 2) - 1, -1)
        else:
            long = self.real_size[1]
            short = self.real_size[0]
            size = short / float(long)
            self.button1.size = (-1, -size)
            self.button2.size = (-1, -size)
            self.slider.size = (-1, (size * 2) - 1)

    def rebuild(self):
        self.slider.slider_max = slider.calc_max(self.elements, self.window)
        self.scroll_pos = min(self.scroll_pos, self.slider.slider_max)
        self.slider.slider_pos = self.scroll_pos
        self.slider.slider_size = self.window

        self.needs_redraw = True
        super(Scrollbar, self).rebuild()

    def adjust(self, lower):
        if lower:
            self.slider.slider_pos = self.slider.safe_pos(self.scroll_pos - 1)
        else:
            self.slider.slider_pos = self.slider.safe_pos(self.scroll_pos + 1)

    def center(self, element):
        self.slider.slider_pos = self.slider.safe_pos(element -
                                                      self.window // 2)

    def scroll_to(self, element):
        if element < self.scroll_pos:
            self.slider.slider_pos = self.slider.safe_pos(element)
        elif element >= self.scroll_pos + self.window:
            self.slider.slider_pos = self.slider.safe_pos(element -
                                                          self.window + 1)

    def on_change(self, value):
        self.scroll_pos = value
Example #16
0
class HotkeyText(text.Text):

    force_underline = widget.causes_rebuild("_force_underline")

    def __init__(self, *args, **kwargs):
        self.hotkey_target = kwargs.pop('hotkey_target', None)
        self.hotkey = kwargs.pop('hotkey', False)
        self.autohotkey = kwargs.pop('autohotkey', False)
        self.force_underline = kwargs.pop('force_underline', None)

        super(HotkeyText, self).__init__(*args, **kwargs)

    @property
    def hotkey_target(self):
        return self._hotkey_target

    @hotkey_target.setter
    def hotkey_target(self, target):
        self._hotkey_target = target
        if self.hotkey_target is not None:
            self._hotkey_target.hotkey = self._hotkey

    @property
    def hotkey(self):
        return self._hotkey

    @hotkey.setter
    def hotkey(self, hotkey):
        self._hotkey = hotkey
        if self.hotkey_target is not None:
            self.hotkey_target.hotkey = self._hotkey
        self.needs_rebuild = True

    @property
    def text(self):
        return text.Text.text.fget(self)

    @text.setter
    def text(self, value):
        if self.autohotkey and (value != None):
            from code.g import get_hotkey, strip_hotkey
            self.hotkey = get_hotkey(value)
            text.Text.text.fset(self, strip_hotkey(value))
        else:
            text.Text.text.fset(self, value)

    def calc_underline(self):
        if self.force_underline != None:
            self.underline = self.force_underline
        elif self.text and self.hotkey and type(self.hotkey) in (str, unicode):
            if self.hotkey in self.text:
                self.underline = self.text.index(self.hotkey)
            elif self.hotkey.lower() in self.text.lower():
                self.underline = self.text.lower().index(self.hotkey.lower())
        else:
            self.underline = -1

    def rebuild(self):
        old_underline = self.underline
        self.calc_underline()
        if self.underline != old_underline:
            self.needs_redraw = True

        super(HotkeyText, self).rebuild()