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)
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)
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)
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']
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
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()
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
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
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))
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()
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"]
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
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
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 = ""
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
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()