def reset_art(self): if not self.ui.active_art: return self.charset_swatch.reset_art() self.palette_swatch.reset_art() # set panel size based on charset size margin = self.swatch_margin * 2 charset = self.ui.active_art.charset cqw, cqh = self.charset_swatch.art.quad_width, self.charset_swatch.art.quad_height old_width, old_height = self.tile_width, self.tile_height # min width in case of tiny charsets charset_tile_width = max(charset.map_width, MIN_CHARSET_WIDTH) self.tile_width = (cqw * charset_tile_width + margin) / UIArt.quad_width # tile height = height of charset + distance from top of popup self.tile_height = (cqh * charset.map_height) / UIArt.quad_height + margin # account for popup info lines etc: charset name + palette name + 1 padding each extra_lines = 7 # account for size of palette + bottom margin palette_height = ((self.palette_swatch.art.height * self.palette_swatch.art.quad_height) + self.swatch_margin) / UIArt.quad_height self.tile_height += self.tab_height + palette_height + extra_lines if old_width != self.tile_width or old_height != self.tile_height: self.art.resize(int(self.tile_width), int(self.tile_height)) # panel text - position different elements based on selected tab if self.active_tab == TAB_CHAR_COLOR: self.draw_char_color_tab() elif self.active_tab == TAB_TOOLS: self.draw_tool_tab() self.update_xform_buttons() # draw button captions UIElement.reset_art(self)
def __init__(self, ui): self.ui = ui self.world = self.ui.app.gw UIElement.__init__(self, ui) self.buttons = [] self.create_buttons() self.keyboard_nav_index = 0
def update(self): if not self.ui.active_art: return # update buttons UIElement.update(self) # set color swatches for i in range(self.swatch_width): self.char_art.set_color_at(0, 0, i, 0, self.ui.selected_bg_color, False) self.fg_art.set_color_at(0, 0, i, 0, self.ui.selected_fg_color, False) self.bg_art.set_color_at(0, 0, i, 0, self.ui.selected_bg_color, False) # set char w/ correct FG color and xform self.char_art.set_char_index_at(0, 0, 1, 0, self.ui.selected_char) self.char_art.set_color_at(0, 0, 1, 0, self.ui.selected_fg_color, True) self.char_art.set_char_transform_at(0, 0, 1, 0, self.ui.selected_xform) # position elements self.position_swatch(self.char_renderable, self.char_swatch_x) self.position_swatch(self.fg_renderable, self.fg_swatch_x) self.position_swatch(self.bg_renderable, self.bg_swatch_x) # update buttons before redrawing art (ie non-interactive bits) self.update_button_captions() for art in [self.char_art, self.fg_art, self.bg_art]: art.update() self.rewrite_art() self.draw_buttons()
def __init__(self, ui): # bitmap icon for about menu button self.playscii_sprite = UISpriteRenderable(ui.app) self.mode_button = None UIElement.__init__(self, ui) self.active_menu_name = None # list of menu buttons that can be navigated etc self.menu_buttons = [] x = PlaysciiMenuButton.width + self.button_padding for button_class in self.button_classes: button = button_class(self) button.width = len(button.caption) + 2 button.x = x x += button.width + self.button_padding setattr(self, '%s_button' % button.name, button) # NOTE: callback already defined in MenuButton class, # menu data for pulldown with set in MenuButton subclass button.pulldown = self.ui.pulldown self.menu_buttons.append(button) playscii_button = PlaysciiMenuButton(self) playscii_button.callback = self.open_about # implement Playscii logo menu as a normal UIButton that opens # the About screen directly self.menu_buttons.append(playscii_button) self.reset_icon() # copy from menu buttons, any buttons past this point are not menus self.buttons = self.menu_buttons[:] # toggle mode button at far right if not self.mode_button_class: return self.mode_button = self.mode_button_class(self) self.mode_button.x = int( self.ui.width_tiles * self.ui.scale) - self.mode_button.width self.mode_button.callback = self.toggle_game_mode self.buttons.append(self.mode_button)
def __init__(self, ui): self.bg_color_index = ui.colors.darkgrey self.highlight_color = 8 # yellow UIElement.__init__(self, ui) # state stuff for console move/fade self.alpha = 0 self.target_alpha = 0 self.target_y = 2 # start off top of screen self.renderable.y = self.y = 2 # user input and log self.last_lines = [] self.history_filename = self.ui.app.config_dir + CONSOLE_HISTORY_FILENAME if os.path.exists(self.history_filename): self.history_file = open(self.history_filename, 'r') try: self.command_history = self.history_file.readlines() except: self.command_history = [] self.history_file = open(self.history_filename, 'a') else: self.history_file = open(self.history_filename, 'w+') self.command_history = [] self.history_index = 0 # junk data in last user line so it changes on first update self.last_user_line = 'test' # max line length = width of console minus prompt + _ self.max_line_length = int(self.art.width) - self.right_margin
def __init__(self, ui, options): self.ui = ui # apply options, eg passed in from UI.open_dialog for k, v in options.items(): setattr(self, k, v) self.confirm_button = ConfirmButton(self) self.other_button = OtherButton(self) self.cancel_button = CancelButton(self) # handle caption overrides def caption_override(button, alt_caption): if alt_caption and button.caption != alt_caption: button.caption = alt_caption button.width = len(alt_caption) + 2 caption_override(self.confirm_button, self.confirm_caption) caption_override(self.other_button, self.other_caption) caption_override(self.cancel_button, self.cancel_caption) self.confirm_button.callback = self.confirm_pressed self.other_button.callback = self.other_pressed self.cancel_button.callback = self.cancel_pressed self.buttons = [ self.confirm_button, self.other_button, self.cancel_button ] # populate fields with text self.field_texts = [] for i, field in enumerate(self.fields): self.field_texts.append(self.get_initial_field_text(i)) # field cursor starts on self.active_field = 0 UIElement.__init__(self, ui) if self.ui.menu_bar and self.ui.menu_bar.active_menu_name: self.ui.menu_bar.close_active_menu()
def reset_art(self, resize=True, clear_buttons=True): # get_message splits into >1 line if too long msg_lines = self.get_message() if self.message else [] if resize: self.tile_height = self.get_height(msg_lines) self.art.resize(self.tile_width, self.tile_height) if self.center_in_window: qw, qh = self.art.quad_width, self.art.quad_height self.x = -(self.tile_width * qw) / 2 self.y = (self.tile_height * qh) / 2 # draw window self.art.clear_frame_layer(0, 0, self.bg_color, self.fg_color) s = ' ' + self.title.ljust(self.tile_width - 1) # invert titlebar (if kb focus) fg = self.titlebar_fg_color bg = self.titlebar_bg_color if not self is self.ui.keyboard_focus_element and \ self is self.ui.active_dialog: fg = self.fg_color bg = self.bg_color self.art.write_string(0, 0, 0, 0, s, fg, bg) # message if self.message: y = 2 for i, line in enumerate(msg_lines): self.art.write_string(0, 0, 2, y + i, line) # field caption(s) self.draw_fields() # position buttons self.confirm_button.x = self.tile_width - self.confirm_button.width - 2 self.confirm_button.y = self.tile_height - 2 if self.other_button_visible: self.other_button.x = self.confirm_button.x self.other_button.x -= self.other_button.width + 2 self.other_button.y = self.confirm_button.y self.other_button.visible = True self.cancel_button.x = 2 self.cancel_button.y = self.tile_height - 2 # create field buttons so you can click em if clear_buttons: self.buttons = [ self.confirm_button, self.other_button, self.cancel_button ] for i, field in enumerate(self.fields): # None-type field = just a label if field.type is None: continue field_button = DialogFieldButton(self) field_button.field_number = i # field settings mean button can be in a variety of places field_button.width = 1 if field.type is bool else field.width field_button.x = 2 if not field.oneline or field.type is bool else len( field.label) + 1 field_button.y = self.get_field_y(i) if not field.oneline: field_button.y += 1 self.buttons.append(field_button) # draw buttons UIElement.reset_art(self)
def render(self): if not self.visible: return UIElement.render(self) if self.active_tab == TAB_CHAR_COLOR: self.charset_swatch.render() self.palette_swatch.render() if self.cursor_char != -1 or self.cursor_color != -1: self.cursor_box.render()
def __init__(self, ui): art = ui.active_art self.ui = ui # create 3 custom Arts w/ source charset and palette, renderables for each art_name = '%s_%s' % (int(time.time()), self.__class__.__name__) self.char_art = UIArt(art_name, ui.app, art.charset, art.palette, self.swatch_width, 1) self.char_renderable = UIRenderable(ui.app, self.char_art) self.fg_art = UIArt(art_name, ui.app, art.charset, art.palette, self.swatch_width, 1) self.fg_renderable = UIRenderable(ui.app, self.fg_art) self.bg_art = UIArt(art_name, ui.app, art.charset, art.palette, self.swatch_width, 1) self.bg_renderable = UIRenderable(ui.app, self.bg_art) # "dimmed out" box self.dim_art = UIArt(art_name, ui.app, ui.charset, ui.palette, self.swatch_width + self.char_swatch_x, 1) self.dim_renderable = UIRenderable(ui.app, self.dim_art) self.dim_renderable.alpha = 0.75 # separate dimmed out box for xform, easier this way xform_width = XformToggleButton.width + XformCycleButton.width self.dim_xform_art = UIArt(art_name, ui.app, ui.charset, ui.palette, xform_width, 1) self.dim_xform_renderable = UIRenderable(ui.app, self.dim_xform_art) self.dim_xform_renderable.alpha = 0.75 # create clickable buttons self.buttons = [] for button_class, button_name in self.button_names.items(): button = button_class(self) setattr(self, button_name + '_button', button) cb_name = '%s_button_pressed' % button_name button.callback = getattr(self, cb_name) self.buttons.append(button) # some button captions, widths, locations will be set in reset_art # determine total width of left-justified items self.left_items_width = self.tool_cycle_button.x + self.tool_cycle_button.width + 15 # set some properties in bulk self.renderables = [] for r in [ self.char_renderable, self.fg_renderable, self.bg_renderable, self.dim_renderable, self.dim_xform_renderable ]: r.ui = ui r.grain_strength = 0 # add to list of renderables to manage eg destroyed on quit self.renderables.append(r) # red X for transparent colors self.x_renderable = UIRenderableX(ui.app, self.char_art) # give it a special reference to this element self.x_renderable.status_bar = self self.renderables.append(self.x_renderable) UIElement.__init__(self, ui)
def reset_art(self): self.tile_width = ceil(self.ui.width_tiles * self.ui.scale) # must resize here, as window width will vary self.art.resize(self.tile_width, self.tile_height) # repaint bar contents bg = self.ui.colors.white fg = self.ui.colors.black self.art.clear_frame_layer(0, 0, bg, fg) # reposition right-justified mode switch button if self.mode_button: self.mode_button.x = int( self.ui.width_tiles * self.ui.scale) - self.mode_button.width # draw buttons, etc UIElement.reset_art(self) self.reset_icon()
def update(self): # disable prev/next buttons if we're at either end of the page list if self.page == 0: self.other_button.can_hover = False self.other_button.set_state('dimmed') elif self.page == len(self.message) - 1: self.confirm_button.can_hover = False self.confirm_button.set_state('dimmed') else: for button in [self.confirm_button, self.other_button]: button.can_hover = True button.dimmed = False if button.state != 'normal': button.set_state('normal') UIElement.update(self)
def update(self): # redraw fields every update for cursor blink # (seems a waste, no real perf impact tho) self.draw_fields(False) # don't allow confirmation if all field input isn't valid valid, reason = self.is_input_valid() # if input invalid, show reason in red along button of window bottom_y = self.tile_height - 1 # first clear any previous warnings self.art.clear_line(0, 0, bottom_y) self.confirm_button.set_state('normal') # some dialogs use reason for warning + valid input if reason: fg = self.ui.error_color_index self.art.write_string(0, 0, 1, bottom_y, reason, fg) if not valid: self.confirm_button.set_state('dimmed') UIElement.update(self)
def reset_art(self): UIElement.reset_art(self) self.tile_width = ceil(self.ui.width_tiles * self.ui.scale) # must resize here, as window width will vary self.art.resize(self.tile_width, self.tile_height) # write chars/colors to the art self.rewrite_art() self.x_renderable.scale_x = self.char_art.width self.x_renderable.scale_y = -self.char_art.height # dim box self.dim_art.clear_frame_layer(0, 0, self.ui.colors.white) self.dim_art.update() self.dim_xform_art.clear_frame_layer(0, 0, self.ui.colors.white) self.dim_xform_art.update() # rebuild geo, elements may be new dimensions self.dim_art.geo_changed = True self.dim_xform_art.geo_changed = True self.char_art.geo_changed = True self.fg_art.geo_changed = True self.bg_art.geo_changed = True
def render(self): if not self.ui.active_art: return UIElement.render(self) # draw wireframe red X /behind/ char if BG transparent if self.ui.selected_bg_color == 0: self.x_renderable.x = self.char_renderable.x self.x_renderable.y = self.char_renderable.y self.x_renderable.render() self.char_renderable.render() self.fg_renderable.render() self.bg_renderable.render() # draw red X for transparent FG or BG if self.ui.selected_fg_color == 0: self.x_renderable.x = self.fg_renderable.x self.x_renderable.y = self.fg_renderable.y self.x_renderable.render() if self.ui.selected_bg_color == 0: self.x_renderable.x = self.bg_renderable.x self.x_renderable.y = self.bg_renderable.y self.x_renderable.render() # dim out items if brush is set to not affect them self.dim_renderable.y = self.char_renderable.y swatch_width = self.art.quad_width * StatusBarCycleButton.width if not self.ui.selected_tool.affects_char: self.dim_renderable.x = self.char_renderable.x - swatch_width self.dim_renderable.render() if not self.ui.selected_tool.affects_fg_color: self.dim_renderable.x = self.fg_renderable.x - swatch_width self.dim_renderable.render() if not self.ui.selected_tool.affects_bg_color: self.dim_renderable.x = self.bg_renderable.x - swatch_width self.dim_renderable.render() if not self.ui.selected_tool.affects_xform: # separate dimmer renderable for xform's wider size self.dim_xform_renderable.y = self.char_renderable.y self.dim_xform_renderable.x = XformToggleButton.x * self.art.quad_width - 1 self.dim_xform_renderable.render()
def __init__(self, ui): self.ui = ui self.charset_swatch = CharacterSetSwatch(ui, self) self.palette_swatch = PaletteSwatch(ui, self) self.cursor_box = SwatchSelectionBoxRenderable(ui.app, self.charset_swatch.art) self.renderables = [self.cursor_box] # set by swatch.set_cursor_loc based on selection validity self.cursor_char = -1 self.cursor_color = -1 self.active_tab = TAB_CHAR_COLOR # create buttons from button:name map, button & callback names generated # group these into lists that can be combined into self.buttons self.common_buttons = self.create_buttons_from_map(self.button_names) self.char_color_tab_buttons = self.create_buttons_from_map( self.char_color_tab_button_names) self.tool_tab_buttons = self.create_buttons_from_map( self.tool_tab_button_names) # populate more tool tab buttons from UI's list of tools # similar to create_buttons_from_map, but class name isn't known # MAYBE-TODO: is there a way to unify this? for tool in self.ui.tools: tool_button = ToolButton(self) # caption: 1-space padding from left tool_button.caption = ' %s' % tool.button_caption tool_button_name = '%s_tool_button' % tool.name setattr(self, tool_button_name, tool_button) cb_name = '%s_pressed' % tool_button_name tool_button.callback = getattr(self, cb_name) # set a special property UI can refer to tool_button.tool_name = tool.name self.tool_tab_buttons.append(tool_button) UIElement.__init__(self, ui) # set initial tab state self.char_color_tab_button_pressed() self.xform_0_button.normal_bg_color = self.xform_normal_button.normal_bg_color = self.highlight_color
def update(self): UIElement.update(self) if not self.ui.active_art: return if self.active_tab == TAB_CHAR_COLOR: # bail if mouse didn't move, but also respect keyboard editing mouse_moved = self.ui.app.mouse_dx != 0 or self.ui.app.mouse_dy != 0 if self.ui.app.keyboard_editing: self.cursor_box.visible = True elif mouse_moved and self in self.ui.hovered_elements: self.cursor_box.visible = False x, y = self.ui.get_screen_coords(self.ui.app.mouse_x, self.ui.app.mouse_y) for e in [self.charset_swatch, self.palette_swatch]: if e.is_inside(x, y): self.cursor_box.visible = True e.set_cursor_loc_from_mouse(self.cursor_box, x, y) break # note: self.cursor_box updates in charset_swatch.update self.charset_swatch.update() self.palette_swatch.update() elif self.active_tab == TAB_TOOLS and self.ui.tool_settings_changed: self.draw_tool_tab() self.draw_buttons()
def clicked(self, mouse_button): handled = UIElement.clicked(self, mouse_button) if handled: return # if cursor is over a char or color, make it the ui's selected one if self.cursor_char != -1: self.ui.selected_char = self.cursor_char # update cursor, eg keyboard select when cursor isn't beneath popup self.ui.app.cursor.undo_preview_edits() self.ui.app.cursor.update_cursor_preview() elif self.cursor_color != -1: if mouse_button == 1: self.ui.selected_fg_color = self.cursor_color elif mouse_button == 3: self.ui.selected_bg_color = self.cursor_color return True
def destroy(self): UIElement.destroy(self) self.playscii_sprite.destroy()
def render(self): UIElement.render(self) self.playscii_sprite.render()
def reset_loc(self): UIElement.reset_loc(self)
def reset_art(self): self.art.clear_frame_layer(0, 0, self.bg_color, self.fg_color) self.draw_titlebar() self.refresh_items() UIElement.reset_art(self)
def clicked(self, mouse_button): # always handle input, even if we didn't hit a button UIElement.clicked(self, mouse_button) return True