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)
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 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 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)
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()
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
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}, )
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 = []
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}, )
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 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()
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))
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()
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()
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()
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
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()
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