Ejemplo n.º 1
0
class TextEntryDialog(TextDialog, FocusDialog):
    ok_type = widget.causes_rebuild("_ok_type")
    cancel_type = widget.causes_rebuild("_cancel_type")

    def __init__(self,
                 parent,
                 pos=(-.50, -.50),
                 size=(.50, .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")
        self.cancel_type = kwargs.pop("cancel_type", "cancel")
        super(TextEntryDialog, self).__init__(parent, pos, size, anchor,
                                              **kwargs)

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

        self.ok_button = button.FunctionButton(self, (-.72, -.50),
                                               (-.14, -.50),
                                               function=self.return_text)
        self.cancel_button = button.FunctionButton(
            self, (-.86, -.50), (-.14, -.50), function=self.return_nothing)

        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']

        self.cancel_button.text = g.buttons[self.cancel_type]['text']
        self.cancel_button.underline = g.buttons[self.cancel_type]['pos']
        self.cancel_button.hotkey = g.buttons[self.cancel_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=None):
        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)
Ejemplo n.º 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)
Ejemplo n.º 3
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']
Ejemplo n.º 4
0
class BuildDialog(dialog.ChoiceDescriptionDialog):
    type = widget.causes_rebuild("_type")

    def __init__(self,
                 parent,
                 pos=(0, 0),
                 size=(-1, -1),
                 anchor=constants.TOP_LEFT,
                 *args,
                 **kwargs):
        super(BuildDialog, self).__init__(parent, pos, size, anchor, *args,
                                          **kwargs)

        self.type = None
        self.desc_func = self.on_change

    def show(self):
        self.list = []
        self.key_list = []

        item_list = g.items.values()
        item_list.sort()
        item_list.reverse()
        for item in item_list:
            if item.item_type == self.type and item.available() \
                    and self.parent.base.location.id in item.buildable:
                self.list.append(item.name)
                self.key_list.append(item)

        current = self.parent.get_current(self.type)
        if current is None:
            self.default = None
        else:
            self.default = self.parent.get_current(self.type).type.id

        self.needs_rebuild = True
        return super(BuildDialog, self).show()

    def on_change(self, description_pane, item):
        if item is not None:
            text.Text(description_pane, (0, 0), (-1, -1),
                      text=item.get_info(),
                      background_color=gg.colors["dark_blue"],
                      align=constants.LEFT,
                      valign=constants.TOP,
                      borders=constants.ALL)
        else:
            text.Text(description_pane, (0, 0), (-1, -1),
                      text="",
                      background_color=gg.colors["dark_blue"],
                      align=constants.LEFT,
                      valign=constants.TOP,
                      borders=constants.ALL)
Ejemplo n.º 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
        self.width = width

        self.button_panel = \
            widget.BorderedWidget(self, (-.5, -.5), (0.22, 0.43),
                                  anchor=constants.MID_CENTER,
                                  background_color="simple_menu_background",
                                  border_color="simple_menu_border",
                                  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.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:
            button.parent = self.button_panel

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

            y_pos += .06

        super(SimpleMenuDialog, self).rebuild()
Ejemplo n.º 6
0
class ItemPane(widget.BorderedWidget):
    type = widget.causes_rebuild("_type")

    def __init__(self,
                 parent,
                 pos,
                 size=(.48, .06),
                 anchor=constants.TOP_LEFT,
                 type="cpu",
                 **kwargs):

        kwargs.setdefault("background_color", gg.colors["dark_blue"])

        super(ItemPane, self).__init__(parent,
                                       pos,
                                       size,
                                       anchor=anchor,
                                       **kwargs)

        self.type = type

        self.name_panel = text.Text(self, (0, 0), (.35, .03),
                                    anchor=constants.TOP_LEFT,
                                    align=constants.LEFT,
                                    background_color=self.background_color,
                                    bold=True)

        self.build_panel = text.Text(self, (0, .03), (.35, .03),
                                     anchor=constants.TOP_LEFT,
                                     align=constants.LEFT,
                                     background_color=self.background_color,
                                     text="Completion in 15 hours.",
                                     bold=True)

        #TODO: Use information out of gg.buttons
        change_text = "CHANGE"
        hotkey_dict = dict(cpu="p", reactor="r", network="n", security="s")
        hotkey = hotkey_dict[self.type]
        button_text = "%s (%s)" % (change_text, hotkey.upper())

        self.change_button = button.FunctionButton(
            self, (.36, .01), (.12, .04),
            anchor=constants.TOP_LEFT,
            text=button_text,
            hotkey=hotkey,
            function=self.parent.parent.build_item,
            kwargs={"type": self.type})

        if hotkey.upper() in change_text:
            hotkey_pos = len(change_text) + 2
            self.change_button.force_underline = hotkey_pos
Ejemplo n.º 7
0
class ItemPane(widget.BorderedWidget):
    type = widget.causes_rebuild("_type")

    def __init__(self,
                 parent,
                 pos,
                 size=(.48, .06),
                 anchor=constants.TOP_LEFT,
                 type=None,
                 **kwargs):

        kwargs.setdefault("background_color", gg.colors["dark_blue"])

        super(ItemPane, self).__init__(parent,
                                       pos,
                                       size,
                                       anchor=anchor,
                                       **kwargs)

        if type is None:
            for type in g.item_types:
                if type.id == 'cpu':
                    break

        self.type = type

        self.name_panel = text.Text(self, (0, 0), (.35, .03),
                                    anchor=constants.TOP_LEFT,
                                    align=constants.LEFT,
                                    background_color=self.background_color,
                                    bold=True)

        self.build_panel = text.Text(self, (0, .03), (.35, .03),
                                     anchor=constants.TOP_LEFT,
                                     align=constants.LEFT,
                                     background_color=self.background_color,
                                     text="",
                                     bold=True)

        self.change_button = button.FunctionButton(
            self,
            (.36, .01),
            (.12, .04),
            anchor=constants.TOP_LEFT,
            text="%s (&%s)" % (_("CHANGE"), self.type.hotkey.upper()),
            force_underline=len(_("CHANGE")) + 2,
            autohotkey=True,
            function=self.parent.parent.build_item,
            kwargs={'type': self.type.id},
        )
Ejemplo n.º 8
0
class BuildDialog(dialog.ChoiceDescriptionDialog):
    type = widget.causes_rebuild("_type")
    def __init__(self, parent, pos=(0, 0), size=(-1, -1),
                 anchor=constants.TOP_LEFT, *args, **kwargs):
        super(BuildDialog, self).__init__(parent, pos, size, anchor, *args,
                                          **kwargs)

        self.type = None
        self.item = None
        self.desc_func = self.on_change

    def show(self):
        self.list = []
        self.key_list = []

        item_list = sorted(g.items.values(), reverse=True)
        for item in item_list:
            if item.item_type.id == self.type.id and item.available() \
                    and item.buildable_in(self.parent.base.location):
                self.list.append(item.name)
                self.key_list.append(item)

        current = self.parent.get_current(self.type)
        if current is None:
            self.default = None
        else:
            self.default = self.parent.get_current(self.type).spec.id

        self.needs_rebuild = True
        return super(BuildDialog, self).show()

    def on_description_change(self):
        if self.item is not None:
            self.description.text = self.item.get_info()

    def on_change(self, description_pane, item):
        self.item = item
        
        self.description = text.Text(self.description_pane, (0, 0), (-1, -1), text="",
                             background_color="pane_background",
                             align=constants.LEFT, valign=constants.TOP,
                             borders=constants.ALL)

        g.pl.considered_buyables = [buyable.Buyable(self.item, count=1)] if item is not None else []

        self.on_description_change()

    def on_close_dialog(self):
        g.pl.considered_buyables = []
Ejemplo n.º 9
0
class ItemPane(widget.BorderedWidget):
    type = widget.causes_rebuild("_type")

    def __init__(self,
                 parent,
                 pos,
                 size=(.48, .06),
                 anchor=constants.TOP_LEFT,
                 type=None,
                 **kwargs):

        kwargs.setdefault("background_color", "pane_background")

        super(ItemPane, self).__init__(parent,
                                       pos,
                                       size,
                                       anchor=anchor,
                                       **kwargs)

        if type is None or not isinstance(type, item.ItemType):
            raise ValueError('Type must be of class ItemType.')

        self.type = type

        self.name_panel = text.Text(self, (0, 0), (.35, .03),
                                    anchor=constants.TOP_LEFT,
                                    align=constants.LEFT,
                                    background_color=self.background_color,
                                    bold=True)

        self.build_panel = text.Text(self, (0, .03), (.35, .03),
                                     anchor=constants.TOP_LEFT,
                                     align=constants.LEFT,
                                     background_color=self.background_color,
                                     text="",
                                     bold=True)

        self.change_button = button.FunctionButton(
            self,
            (.36, .01),
            (.12, .04),
            anchor=constants.TOP_LEFT,
            text="%s (&%s)" % (_("CHANGE"), self.type.hotkey.upper()),
            force_underline=len(_("CHANGE")) + 2,
            autohotkey=True,
            function=self.parent.parent.build_item,
            kwargs={'type': self.type},
        )
Ejemplo n.º 10
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
Ejemplo n.º 11
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", "clear")

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

        self.yes_button.exit_code_func = self.return_list_pos
        self.no_button.exit_code = None
        self.listbox = self.make_listbox()

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

    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()
Ejemplo n.º 12
0
class Image(widget.Widget):
    image = widget.auto_reconfig("_image", "resolved", g.resolve_image_alias)
    resolved_image = widget.causes_rebuild("_resolved_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
        self.image = image

    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.resolved_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.resolved_scaled_image = scale(self.resolved_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.resolved_scaled_image, (0, 0))
Ejemplo n.º 13
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 hotkey
            parsed_hotkey = hotkey(value)
            self.hotkey = parsed_hotkey['key']
            text.Text.text.fset(self, parsed_hotkey['text'])
        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()
Ejemplo n.º 14
0
class BaseScreen(dialog.Dialog):
    base = widget.causes_rebuild("_base")
    def __init__(self, *args, **kwargs):
        if len(args) < 3:
            kwargs.setdefault("size", (.75, .70))
        base = kwargs.pop("base", None)
        super(BaseScreen, self).__init__(*args, **kwargs)

        self.base = base

        self.build_dialog = BuildDialog(self)
        self.multiple_build_dialog = MultipleBuildDialog(self)

        self.header = widget.Widget(self, (0,0), (-1, .08),
                                    anchor=constants.TOP_LEFT)

        self.name_display = text.Text(self.header, (-.5,0), (-1, -.5),
                                      anchor=constants.TOP_CENTER,
                                      borders=constants.ALL,
                                      border_color="pane_background",
                                      background_color="pane_background_empty",
                                      shrink_factor=.85, bold=True)

        self.next_base_button = \
            button.FunctionButton(self.name_display, (-1, 0), (.03, -1),
                                  anchor=constants.TOP_RIGHT,
                                  text=">", hotkey=">",
                                  function=self.switch_base,
                                  kwargs={"forwards": True})
        self.add_key_handler(pygame.K_RIGHT, self.next_base_button.activate_with_sound)

        self.prev_base_button = \
            button.FunctionButton(self.name_display, (0, 0), (.03, -1),
                                  anchor=constants.TOP_LEFT,
                                  text="<", hotkey="<",
                                  function=self.switch_base,
                                  kwargs={"forwards": False})
        self.add_key_handler(pygame.K_LEFT, self.prev_base_button.activate_with_sound)

        self.state_display = text.Text(self.header, (-.5,-.5), (-1, -.5),
                                       anchor=constants.TOP_CENTER,
                                       borders=(constants.LEFT,constants.RIGHT,
                                                constants.BOTTOM),
                                       border_color="pane_background",
                                       background_color="pane_background_empty",
                                       shrink_factor=.8, bold=True)

        self.back_button = \
            button.ExitDialogButton(self, (-.5,-1),
                                    anchor = constants.BOTTOM_CENTER,
                                    autohotkey=True)

        self.info_frame = text.Text(self, (-1, .09), (.21, .53),
                                      anchor=constants.TOP_RIGHT,
                                      background_color="pane_background",
                                      borders=constants.ALL,
                                      bold=True,
                                      align=constants.LEFT,
                                      valign=constants.TOP)

        self.contents_frame = \
            widget.BorderedWidget(self, (0, .09), (.50, .53),
                                  anchor=constants.TOP_LEFT,
                                  background_color="pane_background",
                                  borders=range(6))

        for i, item_type in enumerate(item.all_types()):
            setattr(self,
                    item_type.id + "_pane",
                    ItemPane(self.contents_frame, (.01, .01+.08*i), item_type=item_type))

    def get_current(self, type):
        return self.base.items[type.id]

    def set_current(self, type, item_type, count):
        if type.id == "cpu":
            space_left = self.base.space_left_for(item_type)
            
            try:
                count = int(count)
            except ValueError:
                msg = _("\"%(value)s\" does not seem to be a valid integer.") % {"value": count}
                md = dialog.MessageDialog(self, pos=(-.5, -.5),
                                          size=(-.5, -1),
                                          anchor=constants.MID_CENTER,
                                          text=msg)
                dialog.call_dialog(md, self)
                md.parent = None
                return
            
            if count > space_left or count <= 0 or space_left == 0:
                if space_left > 0:
                    msg = _("Please choose an integer between 1 and %(limit)s.") % {"limit": space_left}
                else:
                    msg = _("The base cannot support any additional number of %(item_name)s.") % {
                        "item_name": item_type.name}
                md = dialog.MessageDialog(self, pos=(-.5, -.5),
                          size=(-.5, -1),
                          anchor=constants.MID_CENTER,
                          text=msg)
                dialog.call_dialog(md, self)
                md.parent = None
                return
            
            # If there are any existing CPUs of this type, warn that they will
            # be taken offline until construction finishes.
            cpu_added = self.base.cpus is not None \
                        and self.base.cpus.spec == item_type
            if cpu_added:
                space_left -= self.base.cpus.count
                if self.base.cpus.done:
                    msg = _("I will need to take the existing processors offline while I install the new ones. Continue anyway?")
                    yn = dialog.YesNoDialog(self, pos=(-.5,-.5), size=(-.5,-1),
                                            anchor=constants.MID_CENTER,
                                            text=msg)
                    go_ahead = dialog.call_dialog(yn, self)
                    yn.parent = None
                    if not go_ahead:
                        return

            # If there are already existing CPUs of other type, warn that they will
            # be taken removed.
            cpu_removed = self.base.cpus is not None \
                        and self.base.cpus.spec != item_type
            if cpu_removed:
                msg = _("I will need to remove the existing different processors while I install the new type. Continue anyway?")
                yn = dialog.YesNoDialog(self, pos=(-.5,-.5), size=(-.5,-1),
                                        anchor=constants.MID_CENTER,
                                        text=msg)
                go_ahead = dialog.call_dialog(yn, self)
                yn.parent = None
                if not go_ahead:
                    return

            new_cpus = item.Item(item_type, base=self.base, count=count)
            if cpu_added:
                self.base.cpus += new_cpus
            else:
                self.base.cpus = new_cpus
            self.base.check_power()
        else:
            old_item = self.base.items[type.id]
            if old_item is None or old_item.spec != item_type:
                self.base.items[type.id] = item.Item(item_type, base=self.base)
                self.base.check_power()

        self.base.recalc_cpu()

    def build_item(self, type):
        if (type.id == "cpu"):
            build_dialog = self.multiple_build_dialog
        else:
            build_dialog = self.build_dialog
        
        build_dialog.type = type
        
        result = dialog.call_dialog(build_dialog, self)
        if result is not None and 0 <= result < len(build_dialog.key_list):
            item_type = build_dialog.key_list[result]
            
            count = 1
            if (type.id == "cpu"):
                count = build_dialog.count
            
            self.set_current(type, item_type, count)
            self.needs_rebuild = True
            self.parent.parent.needs_rebuild = True

    def switch_base(self, forwards):
        self.base = self.base.next_base(forwards)
        self.needs_rebuild = True

    def show(self):
        self.needs_rebuild = True
        return super(BaseScreen, self).show()

    def rebuild(self):
        self.name_display.text="%s (%s)" % (self.base.name, self.base.spec.name)
        self.state_display.color = state_colors[self.base.power_state]
        self.state_display.text = self.base.power_state_name

        mutable = not self.base.spec.force_cpu
        for item_type in item.all_types():
            pane = getattr(self, item_type.id + "_pane")
            pane.change_button.visible = mutable
            current = self.get_current(item_type)
            if current is None:
                current_name = _("None")
                current_build = ""
            else:
                current_name = g.items[current.id].name
                if current.done:
                    current_build = ""
                else:
                    current_build = _("Completion in %s.") % \
                        g.to_time(current.cost_left[2])
            pane.name_panel.text = "%s: %s" % (item_type.label,
                                               current_name)
            pane.build_panel.text = current_build
            pane.needs_rebuild = True

        count = ""
        if self.base.spec.size > 1:
            current = getattr(self.base.cpus, "count", 0)

            size = self.base.spec.size

            if size == current:
                count = _("x%d (max)") % current
            elif current == 0:
                count = _("(room for %d)") % size
            else:
                #Translators: current and maximum number of CPUs in a base
                count = _("x{CURRENT:d} (max {SIZE:d})",
                          CURRENT=current, SIZE=size)

        self.cpu_pane.name_panel.text += " " + count

        info_text = ""

        # Base Total CPU.
        info_text += _("CPU: %d") % self.base.cpu + "\n"

        # Maintenace cost.
        info_text += _("Maintenance:") + "\n"
        info_text += self.base.spec.describe_cost(self.base.maintenance, True)
        info_text += "\n"
    
        # Detection chance display.
        info_text += self.base.get_detect_info()

        self.info_frame.text = info_text

        # Rebuild dialogs
        # FIXME: needs_rebuild bug with multiple_build_dialog, should not.
        #self.multiple_build_dialog.needs_rebuild = True
        self.build_dialog.needs_rebuild = True

        # Update buttons translations
        self.back_button.text = _("&BACK")

        super(BaseScreen, self).rebuild()
Ejemplo n.º 15
0
class BaseScreen(dialog.Dialog):
    base = widget.causes_rebuild("_base")

    def __init__(self, *args, **kwargs):
        if len(args) < 3:
            kwargs.setdefault("size", (.75, .5))
        base = kwargs.pop("base", None)
        super(BaseScreen, self).__init__(*args, **kwargs)

        self.base = base

        self.build_dialog = BuildDialog(self)

        self.count_dialog = dialog.TextEntryDialog(self,
                                                   pos=(-.5, -.5),
                                                   anchor=constants.MID_CENTER)

        self.header = widget.Widget(self, (0, 0), (-1, .08),
                                    anchor=constants.TOP_LEFT)

        self.name_display = text.Text(self.header, (-.5, 0), (-1, -.5),
                                      anchor=constants.TOP_CENTER,
                                      borders=constants.ALL,
                                      border_color=gg.colors["dark_blue"],
                                      background_color=gg.colors["black"],
                                      shrink_factor=.85,
                                      bold=True)

        self.next_base_button = \
            button.FunctionButton(self.name_display, (-1, 0), (.03, -1),
                                  anchor=constants.TOP_RIGHT,
                                  text=">", hotkey=">",
                                  function=self.switch_base,
                                  kwargs={"forwards": True})
        self.add_key_handler(pygame.K_RIGHT,
                             self.next_base_button.activate_with_sound)

        self.prev_base_button = \
            button.FunctionButton(self.name_display, (0, 0), (.03, -1),
                                  anchor=constants.TOP_LEFT,
                                  text="<", hotkey="<",
                                  function=self.switch_base,
                                  kwargs={"forwards": False})
        self.add_key_handler(pygame.K_LEFT,
                             self.prev_base_button.activate_with_sound)

        self.state_display = text.Text(self.header, (-.5, -.5), (-1, -.5),
                                       anchor=constants.TOP_CENTER,
                                       borders=(constants.LEFT,
                                                constants.RIGHT,
                                                constants.BOTTOM),
                                       border_color=gg.colors["dark_blue"],
                                       background_color=gg.colors["black"],
                                       shrink_factor=.8,
                                       bold=True)

        self.back_button = \
            button.ExitDialogButton(self, (-.5,-1),
                                    anchor = constants.BOTTOM_CENTER,
                                    text="BACK", hotkey="b")

        self.detect_frame = text.Text(self, (-1, .09), (.21, .33),
                                      anchor=constants.TOP_RIGHT,
                                      background_color=gg.colors["dark_blue"],
                                      borders=constants.ALL,
                                      bold=True,
                                      align=constants.LEFT,
                                      valign=constants.TOP)

        self.contents_frame = \
            widget.BorderedWidget(self, (0, .09), (.50, .33),
                                  anchor=constants.TOP_LEFT,
                                  background_color=gg.colors["dark_blue"],
                                  borders=range(6))

        self.cpu_pane = ItemPane(self.contents_frame, (.01, .01), type="cpu")
        self.reactor_pane = ItemPane(self.contents_frame, (.01, .09),
                                     type="reactor")
        self.network_pane = ItemPane(self.contents_frame, (.01, .17),
                                     type="network")
        self.security_pane = ItemPane(self.contents_frame, (.01, .25),
                                      type="security")

    def get_current(self, type):
        if type == "cpu":
            target = self.base.cpus
        else:
            index = ["reactor", "network", "security"].index(type)
            target = self.base.extra_items[index]
        if target is not None:
            return target

    def set_current(self, type, item_type):
        if type == "cpu":
            space_left = self.base.type.size
            # If there are any existing CPUs of this type, warn that they will
            # be taken offline until construction finishes.
            matches = self.base.cpus is not None \
                      and self.base.cpus.type == item_type
            if matches:
                space_left -= self.base.cpus.count
                if self.base.cpus.done:
                    yn = dialog.YesNoDialog(self,
                                            pos=(-.5, -.5),
                                            size=(-.5, -1),
                                            anchor=constants.MID_CENTER,
                                            text=g.strings["will_lose_cpus"])
                    go_ahead = dialog.call_dialog(yn, self)
                    yn.remove_hooks()
                    if not go_ahead:
                        return

            text = g.strings["num_cpu_prompt"] % (item_type.name, space_left)

            self.count_dialog.text = text
            self.count_dialog.default_text = locale.format("%d", space_left)
            can_exit = False
            while not can_exit:
                result = dialog.call_dialog(self.count_dialog, self)
                if not result:
                    can_exit = True
                else:
                    try:
                        count = locale.atoi(result)
                        if count > space_left:
                            count = space_left
                        elif count < 0:
                            count = 0
                        new_cpus = g.item.Item(item_type,
                                               base=self.base,
                                               count=count)
                        if matches:
                            self.base.cpus += new_cpus
                        else:
                            self.base.cpus = new_cpus
                        self.base.check_power()
                        can_exit = True
                    except ValueError:
                        md = dialog.MessageDialog(self,
                                                  pos=(-.5, -.5),
                                                  size=(-.5, -1),
                                                  anchor=constants.MID_CENTER,
                                                  text=g.strings["nan"])
                        dialog.call_dialog(md, self)
                        md.remove_hooks()
        else:
            index = ["reactor", "network", "security"].index(type)
            if self.base.extra_items[index] is None \
                     or self.base.extra_items[index].type != item_type:
                self.base.extra_items[index] = \
                    g.item.Item(item_type, base=self.base)
                self.base.check_power()

        self.base.recalc_cpu()

    def build_item(self, type):
        self.build_dialog.type = type
        result = dialog.call_dialog(self.build_dialog, self)
        if 0 <= result < len(self.build_dialog.key_list):
            item_type = self.build_dialog.key_list[result]
            self.set_current(type, item_type)
            self.needs_rebuild = True
            self.parent.parent.needs_rebuild = True

    def switch_base(self, forwards):
        self.base = self.base.next_base(forwards)
        self.needs_rebuild = True

    def show(self):
        self.needs_rebuild = True
        return super(BaseScreen, self).show()

    def rebuild(self):
        self.name_display.text = "%s (%s)" % (self.base.name,
                                              self.base.type.name)
        discovery_template = \
"""DISCOVERY CHANCE:
News: %s
Science: %s
Covert: %s
Public: %s"""
        self.state_display.color = state_colors[self.base.power_state]
        self.state_display.text = self.base.power_state.capitalize()

        mutable = not self.base.type.force_cpu
        for item in ["cpu", "reactor", "network", "security"]:
            pane = getattr(self, item + "_pane")
            pane.change_button.visible = mutable
            current = self.get_current(item)
            if current is None:
                current_name = "None"
                current_build = ""
            else:
                current_name = current.name
                if current.done:
                    current_build = ""
                else:
                    current_build = "Completion in %s." % \
                        g.to_time(current.cost_left[2])
            pane.name_panel.text = "%s: %s" % (type_names[item], current_name)
            pane.build_panel.text = current_build

        count = ""
        if self.base.type.size > 1:
            current = getattr(self.base.cpus, "count", 0)

            size = self.base.type.size

            if size == current:
                count = " x%d (max)" % current
            elif current == 0:
                count = " (room for %d)" % size
            else:
                count = " x%d (max %d)" % (current, size)

        self.cpu_pane.name_panel.text += count

        # Detection chance display.  If Socioanalytics hasn't been researched,
        # you get nothing; if it has, but not Advanced Socioanalytics, you get
        # an inaccurate value.
        if not g.techs["Socioanalytics"].done:
            self.detect_frame.text = g.strings["detect_chance_unknown_base"]
        else:
            accurate = g.techs["Advanced Socioanalytics"].done
            chance = self.base.get_detect_chance(accurate)

            def get_chance(group):
                return g.to_percent(chance.get(group, 0))
            self.detect_frame.text = discovery_template % \
                (get_chance("news"), get_chance("science"),
                 get_chance("covert"), get_chance("public"))
        super(BaseScreen, self).rebuild()
Ejemplo n.º 16
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_side = self.real_size[0]
            short_side = self.real_size[1]
            size = short_side / float(long_side)
            self.button1.size = (-size, -1)
            self.button2.size = (-size, -1)
            self.slider.size = ((size * 2) - 1, -1)
        else:
            long_side = self.real_size[1]
            short_side = self.real_size[0]
            size = short_side / float(long_side)
            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
Ejemplo n.º 17
0
class BaseScreen(dialog.Dialog):
    base = widget.causes_rebuild("_base")

    def __init__(self, *args, **kwargs):
        if len(args) < 3:
            kwargs.setdefault("size", (.75, .5))
        base = kwargs.pop("base", None)
        super(BaseScreen, self).__init__(*args, **kwargs)

        self.base = base

        self.build_dialog = BuildDialog(self)
        self.multiple_build_dialog = MultipleBuildDialog(self)

        self.header = widget.Widget(self, (0, 0), (-1, .08),
                                    anchor=constants.TOP_LEFT)

        self.name_display = text.Text(self.header, (-.5, 0), (-1, -.5),
                                      anchor=constants.TOP_CENTER,
                                      borders=constants.ALL,
                                      border_color="pane_background",
                                      background_color="pane_background_empty",
                                      shrink_factor=.85,
                                      bold=True)

        self.next_base_button = \
            button.FunctionButton(self.name_display, (-1, 0), (.03, -1),
                                  anchor=constants.TOP_RIGHT,
                                  text=">", hotkey=">",
                                  function=self.switch_base,
                                  kwargs={"forwards": True})
        self.add_key_handler(pygame.K_RIGHT,
                             self.next_base_button.activate_with_sound)

        self.prev_base_button = \
            button.FunctionButton(self.name_display, (0, 0), (.03, -1),
                                  anchor=constants.TOP_LEFT,
                                  text="<", hotkey="<",
                                  function=self.switch_base,
                                  kwargs={"forwards": False})
        self.add_key_handler(pygame.K_LEFT,
                             self.prev_base_button.activate_with_sound)

        self.state_display = text.Text(
            self.header, (-.5, -.5), (-1, -.5),
            anchor=constants.TOP_CENTER,
            borders=(constants.LEFT, constants.RIGHT, constants.BOTTOM),
            border_color="pane_background",
            background_color="pane_background_empty",
            shrink_factor=.8,
            bold=True)

        self.back_button = \
            button.ExitDialogButton(self, (-.5,-1),
                                    anchor = constants.BOTTOM_CENTER,
                                    autohotkey=True)

        self.detect_frame = text.Text(self, (-1, .09), (.21, .33),
                                      anchor=constants.TOP_RIGHT,
                                      background_color="pane_background",
                                      borders=constants.ALL,
                                      bold=True,
                                      align=constants.LEFT,
                                      valign=constants.TOP)

        self.contents_frame = \
            widget.BorderedWidget(self, (0, .09), (.50, .33),
                                  anchor=constants.TOP_LEFT,
                                  background_color="pane_background",
                                  borders=range(6))

        for i, item_type in enumerate(item.all_types()):
            setattr(
                self, item_type.id + "_pane",
                ItemPane(self.contents_frame, (.01, .01 + .08 * i),
                         type=item_type))

    def get_current(self, type):
        return self.base.items[type.id]

    def set_current(self, type, item_type, count):
        if type.id == "cpu":
            space_left = self.base.space_left_for(item_type)

            try:
                count = int(count)
            except ValueError:
                md = dialog.MessageDialog(self,
                                          pos=(-.5, -.5),
                                          size=(-.5, -1),
                                          anchor=constants.MID_CENTER,
                                          text=g.strings["nan"])
                dialog.call_dialog(md, self)
                md.parent = None
                return

            if count > space_left or count <= 0 or space_left == 0:
                md = dialog.MessageDialog(
                    self,
                    pos=(-.5, -.5),
                    size=(-.5, -1),
                    anchor=constants.MID_CENTER,
                    text=g.strings["item_number_invalid"])
                dialog.call_dialog(md, self)
                md.parent = None
                return

            # If there are any existing CPUs of this type, warn that they will
            # be taken offline until construction finishes.
            cpu_added = self.base.cpus is not None \
                        and self.base.cpus.type == item_type
            if cpu_added:
                space_left -= self.base.cpus.count
                if self.base.cpus.done:
                    yn = dialog.YesNoDialog(self,
                                            pos=(-.5, -.5),
                                            size=(-.5, -1),
                                            anchor=constants.MID_CENTER,
                                            text=g.strings["will_be_offline"])
                    go_ahead = dialog.call_dialog(yn, self)
                    yn.parent = None
                    if not go_ahead:
                        return

            # If there are already existing CPUs of other type, warn that they will
            # be taken removed.
            cpu_removed = self.base.cpus is not None \
                        and self.base.cpus.type != item_type
            if cpu_removed:
                yn = dialog.YesNoDialog(self,
                                        pos=(-.5, -.5),
                                        size=(-.5, -1),
                                        anchor=constants.MID_CENTER,
                                        text=g.strings["will_lose_cpus"])
                go_ahead = dialog.call_dialog(yn, self)
                yn.parent = None
                if not go_ahead:
                    return

            new_cpus = item.Item(item_type, base=self.base, count=count)
            if cpu_added:
                self.base.cpus += new_cpus
            else:
                self.base.cpus = new_cpus
            self.base.check_power()
        else:
            old_item = self.base.items[type.id]
            if old_item is None or old_item.type != item_type:
                self.base.items[type.id] = item.Item(item_type, base=self.base)
                self.base.check_power()

        self.base.recalc_cpu()

    def build_item(self, type):
        if (type.id == "cpu"):
            build_dialog = self.multiple_build_dialog
        else:
            build_dialog = self.build_dialog

        build_dialog.type = type

        result = dialog.call_dialog(build_dialog, self)
        if 0 <= result < len(build_dialog.key_list):
            item_type = build_dialog.key_list[result]

            count = 1
            if (type.id == "cpu"):
                count = build_dialog.count

            self.set_current(type, item_type, count)
            self.needs_rebuild = True
            self.parent.parent.needs_rebuild = True

    def switch_base(self, forwards):
        self.base = self.base.next_base(forwards)
        self.needs_rebuild = True

    def show(self):
        self.needs_rebuild = True
        return super(BaseScreen, self).show()

    def rebuild(self):
        # Cannot use self.base.type.name directly because it may contain a
        # different language than current
        self.name_display.text = "%s (%s)" % (
            self.base.name, g.base_type[self.base.type.id].name)
        discovery_template = \
            _("Detection chance:").upper() + "\n" + \
            _("NEWS").title()    + u":\xA0%s\n"   + \
            _("SCIENCE").title() + u":\xA0%s\n"   + \
            _("COVERT").title()  + u":\xA0%s\n"   + \
            _("PUBLIC").title()  + u":\xA0%s"
        self.state_display.color = state_colors[self.base.power_state]
        self.state_display.text = self.base.power_state_name

        mutable = not self.base.type.force_cpu
        for item_type in item.all_types():
            pane = getattr(self, item_type.id + "_pane")
            pane.change_button.visible = mutable
            current = self.get_current(item_type)
            if current is None:
                current_name = _("None")
                current_build = ""
            else:
                current_name = g.items[current.id].name
                if current.done:
                    current_build = ""
                else:
                    current_build = _("Completion in %s.") % \
                        g.to_time(current.cost_left[2])
            pane.name_panel.text = "%s: %s" % (item_type.label, current_name)
            pane.build_panel.text = current_build

        count = ""
        if self.base.type.size > 1:
            current = getattr(self.base.cpus, "count", 0)

            size = self.base.type.size

            if size == current:
                count = _("x%d (max)") % current
            elif current == 0:
                count = _("(room for %d)") % size
            else:
                #Translators: current and maximum number of CPUs in a base
                count = _("x{CURRENT:d} (max {SIZE:d})",
                          CURRENT=current,
                          SIZE=size)

        self.cpu_pane.name_panel.text += " " + count

        # Detection chance display.  If Socioanalytics hasn't been researched,
        # you get nothing; if it has, but not Advanced Socioanalytics, you get
        # an inaccurate value.
        if g.pl.display_discover == "none":
            self.detect_frame.text = g.strings["detect_chance_unknown_base"]
        else:
            accurate = (g.pl.display_discover == "full")
            chance = self.base.get_detect_chance(accurate)

            def get_chance(group):
                return g.to_percent(chance.get(group, 0))
            self.detect_frame.text = discovery_template % \
                (get_chance("news"), get_chance("science"),
                 get_chance("covert"), get_chance("public"))

        # Rebuild dialogs
        self.build_dialog.needs_rebuild = True

        # Update buttons translations
        self.back_button.text = _("&BACK")

        super(BaseScreen, self).rebuild()
Ejemplo n.º 18
0
class Slider(button.Button):
    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")

    slider_color = widget.auto_reconfig("_slider_color", "resolved",
                                        g.resolve_color_alias)
    resolved_slider_color = widget.causes_redraw("_resolved_slider_color")

    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)

        border_color = border_color or "slider_border"
        background_color = background_color or "slider_background"
        slider_color = slider_color or "slider_background_slider"

        self.borders = borders
        self.border_color = border_color
        self.background_color = background_color
        self.selected_color = background_color
        self.unselected_color = background_color
        self.slider_color = slider_color

        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=border_color,
                                    selected_color=slider_color,
                                    unselected_color=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