757 lines
27 KiB
Python
757 lines
27 KiB
Python
from art import UV_FLIPX, UV_FLIPY, UV_NORMAL, UV_ROTATE90, UV_ROTATE180, UV_ROTATE270
|
|
from renderable_line import SwatchSelectionBoxRenderable
|
|
from ui_button import TEXT_CENTER, UIButton
|
|
from ui_colors import UIColors
|
|
from ui_element import UIArt, UIElement
|
|
from ui_file_chooser_dialog import CharSetChooserDialog, PaletteChooserDialog
|
|
from ui_swatch import MIN_CHARSET_WIDTH, CharacterSetSwatch, PaletteSwatch
|
|
from ui_tool import FILL_BOUND_BG_COLOR, FILL_BOUND_CHAR, FILL_BOUND_FG_COLOR, FillTool
|
|
|
|
TOOL_PANE_WIDTH = 10
|
|
|
|
|
|
class ToolTabButton(UIButton):
|
|
x, y = 0, 0
|
|
caption_y = 1
|
|
# width is set on the fly by popup size in reset_art
|
|
height = 3
|
|
caption_justify = TEXT_CENTER
|
|
caption = "Tools"
|
|
|
|
|
|
class CharColorTabButton(UIButton):
|
|
caption_y = 1
|
|
height = ToolTabButton.height
|
|
caption_justify = TEXT_CENTER
|
|
caption = "Chars/Colors"
|
|
|
|
|
|
# charset view scale up/down buttons
|
|
|
|
|
|
class CharSetScaleUpButton(UIButton):
|
|
width, height = 3, 1
|
|
x, y = -width, ToolTabButton.height + 1
|
|
caption = "+"
|
|
caption_justify = TEXT_CENTER
|
|
|
|
|
|
class CharSetScaleDownButton(CharSetScaleUpButton):
|
|
x = -CharSetScaleUpButton.width + CharSetScaleUpButton.x
|
|
caption = "-"
|
|
|
|
|
|
# charset flip / rotate buttons
|
|
|
|
|
|
class CharXformButton(UIButton):
|
|
hovered_fg_color = UIColors.white
|
|
hovered_bg_color = UIColors.medgrey
|
|
|
|
|
|
class CharFlipNoButton(CharXformButton):
|
|
x = 3 + len("Flip:") + 1
|
|
y = CharSetScaleUpButton.y + 1
|
|
caption = "None"
|
|
width = len(caption) + 2
|
|
caption_justify = TEXT_CENTER
|
|
|
|
|
|
class CharFlipXButton(CharFlipNoButton):
|
|
x = CharFlipNoButton.x + CharFlipNoButton.width + 1
|
|
width = 3
|
|
caption = "X"
|
|
|
|
|
|
class CharFlipYButton(CharFlipXButton):
|
|
x = CharFlipXButton.x + CharFlipXButton.width + 1
|
|
caption = "Y"
|
|
|
|
|
|
class CharRot0Button(CharXformButton):
|
|
x = 3 + len("Rotation:") + 1
|
|
y = CharFlipNoButton.y + 1
|
|
width = 3
|
|
caption = "0"
|
|
caption_justify = TEXT_CENTER
|
|
|
|
|
|
class CharRot90Button(CharRot0Button):
|
|
x = CharRot0Button.x + CharRot0Button.width + 1
|
|
width = 4
|
|
caption = "90"
|
|
|
|
|
|
class CharRot180Button(CharRot0Button):
|
|
x = CharRot90Button.x + CharRot90Button.width + 1
|
|
width = 5
|
|
caption = "180"
|
|
|
|
|
|
class CharRot270Button(CharRot0Button):
|
|
x = CharRot180Button.x + CharRot180Button.width + 1
|
|
width = 5
|
|
caption = "270"
|
|
|
|
|
|
# tool and tool settings buttons
|
|
|
|
|
|
class ToolButton(UIButton):
|
|
"a tool entry in the tool tab's left hand pane. populated from UI.tools"
|
|
|
|
width = TOOL_PANE_WIDTH
|
|
caption = "TOOLZ"
|
|
y = ToolTabButton.height + 2
|
|
|
|
|
|
class BrushSizeUpButton(UIButton):
|
|
width = 3
|
|
y = ToolTabButton.height + 3
|
|
caption = "+"
|
|
caption_justify = TEXT_CENTER
|
|
normal_fg_color = UIColors.white
|
|
normal_bg_color = UIColors.medgrey
|
|
|
|
|
|
class BrushSizeDownButton(BrushSizeUpButton):
|
|
caption = "-"
|
|
|
|
|
|
class AffectCharToggleButton(UIButton):
|
|
width = 3
|
|
x = TOOL_PANE_WIDTH + 2
|
|
y = BrushSizeUpButton.y + 3
|
|
# don't paint caption from string
|
|
should_draw_caption = False
|
|
normal_fg_color = UIColors.white
|
|
normal_bg_color = UIColors.medgrey
|
|
|
|
|
|
class AffectFgToggleButton(AffectCharToggleButton):
|
|
y = AffectCharToggleButton.y + 1
|
|
|
|
|
|
class AffectBgToggleButton(AffectCharToggleButton):
|
|
y = AffectCharToggleButton.y + 2
|
|
|
|
|
|
class AffectXformToggleButton(AffectCharToggleButton):
|
|
y = AffectCharToggleButton.y + 3
|
|
|
|
|
|
# fill boundary mode items
|
|
class FillBoundaryModeCharButton(AffectCharToggleButton):
|
|
y = AffectXformToggleButton.y + 3
|
|
|
|
|
|
class FillBoundaryModeFGButton(AffectCharToggleButton):
|
|
y = FillBoundaryModeCharButton.y + 1
|
|
|
|
|
|
class FillBoundaryModeBGButton(AffectCharToggleButton):
|
|
y = FillBoundaryModeCharButton.y + 2
|
|
|
|
|
|
# charset / palette chooser buttons
|
|
|
|
|
|
class CharSetChooserButton(UIButton):
|
|
caption = "Set:"
|
|
x = 1
|
|
normal_fg_color = UIColors.black
|
|
normal_bg_color = UIColors.white
|
|
hovered_fg_color = UIColors.white
|
|
hovered_bg_color = UIColors.medgrey
|
|
|
|
|
|
class PaletteChooserButton(CharSetChooserButton):
|
|
caption = "Palette:"
|
|
|
|
|
|
TAB_TOOLS = 0
|
|
TAB_CHAR_COLOR = 1
|
|
|
|
|
|
class ToolPopup(UIElement):
|
|
visible = False
|
|
# actual width will be based on character set + palette size and scale
|
|
tile_width, tile_height = 20, 15
|
|
tab_height = ToolTabButton.height
|
|
swatch_margin = 0.05
|
|
fg_color = UIColors.black
|
|
bg_color = UIColors.lightgrey
|
|
highlight_color = UIColors.white
|
|
tool_settings_label = "Tool Settings:"
|
|
brush_size_label = "Brush size:"
|
|
affects_heading_label = "Affects:"
|
|
affects_char_label = "Character"
|
|
affects_fg_label = "Foreground Color"
|
|
affects_bg_label = "Background Color"
|
|
affects_xform_label = "Rotation/Flip"
|
|
fill_boundary_modes_label = "Fill boundary mode:"
|
|
fill_boundary_char_label = affects_char_label
|
|
fill_boundary_fg_label = affects_fg_label
|
|
fill_boundary_bg_label = affects_bg_label
|
|
flip_label = "Flip:"
|
|
rotation_label = "Rotation:"
|
|
# index of check mark character in UI charset
|
|
check_char_index = 131
|
|
# index of off and on radio button characters in UI charset
|
|
radio_char_0_index = 126
|
|
radio_char_1_index = 127
|
|
# map classes to member names / callbacks
|
|
button_names = {
|
|
ToolTabButton: "tool_tab",
|
|
CharColorTabButton: "char_color_tab",
|
|
}
|
|
char_color_tab_button_names = {
|
|
CharSetScaleUpButton: "scale_charset_up",
|
|
CharSetScaleDownButton: "scale_charset_down",
|
|
CharSetChooserButton: "choose_charset",
|
|
CharFlipNoButton: "xform_normal",
|
|
CharFlipXButton: "xform_flipX",
|
|
CharFlipYButton: "xform_flipY",
|
|
CharRot0Button: "xform_0",
|
|
CharRot90Button: "xform_90",
|
|
CharRot180Button: "xform_180",
|
|
CharRot270Button: "xform_270",
|
|
PaletteChooserButton: "choose_palette",
|
|
}
|
|
tool_tab_button_names = {
|
|
BrushSizeUpButton: "brush_size_up",
|
|
BrushSizeDownButton: "brush_size_down",
|
|
AffectCharToggleButton: "toggle_affect_char",
|
|
AffectFgToggleButton: "toggle_affect_fg",
|
|
AffectBgToggleButton: "toggle_affect_bg",
|
|
AffectXformToggleButton: "toggle_affect_xform",
|
|
}
|
|
fill_boundary_mode_button_names = {
|
|
FillBoundaryModeCharButton: "set_fill_boundary_char",
|
|
FillBoundaryModeFGButton: "set_fill_boundary_fg",
|
|
FillBoundaryModeBGButton: "set_fill_boundary_bg",
|
|
}
|
|
|
|
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.fill_boundary_mode_buttons = self.create_buttons_from_map(
|
|
self.fill_boundary_mode_button_names
|
|
)
|
|
self.tool_tab_buttons = (
|
|
self.create_buttons_from_map(self.tool_tab_button_names)
|
|
+ self.fill_boundary_mode_buttons
|
|
)
|
|
# 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 = " {}".format(tool.button_caption)
|
|
tool_button_name = "{}_tool_button".format(tool.name)
|
|
setattr(self, tool_button_name, tool_button)
|
|
cb_name = "{}_pressed".format(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 create_buttons_from_map(self, button_dict):
|
|
buttons = []
|
|
for button_class in button_dict:
|
|
button = button_class(self)
|
|
button_name = "{}_button".format(button_dict[button_class])
|
|
setattr(self, button_name, button)
|
|
cb_name = "{}_pressed".format(button_name)
|
|
button.callback = getattr(self, cb_name)
|
|
buttons.append(button)
|
|
return buttons
|
|
|
|
def tool_tab_button_pressed(self):
|
|
self.active_tab = TAB_TOOLS
|
|
self.char_color_tab_button.can_hover = True
|
|
self.char_color_tab_button.dimmed = True
|
|
self.tool_tab_button.can_hover = False
|
|
self.tool_tab_button.dimmed = False
|
|
self.buttons = self.common_buttons + self.tool_tab_buttons
|
|
self.draw_tool_tab()
|
|
self.draw_buttons()
|
|
|
|
def char_color_tab_button_pressed(self):
|
|
self.active_tab = TAB_CHAR_COLOR
|
|
self.tool_tab_button.can_hover = True
|
|
self.tool_tab_button.dimmed = True
|
|
self.char_color_tab_button.can_hover = False
|
|
self.char_color_tab_button.dimmed = False
|
|
self.buttons = self.common_buttons + self.char_color_tab_buttons
|
|
self.draw_char_color_tab()
|
|
self.draw_buttons()
|
|
|
|
def scale_charset_up_button_pressed(self):
|
|
self.charset_swatch.increase_scale()
|
|
self.reset_art()
|
|
self.charset_swatch.reset_loc()
|
|
self.palette_swatch.reset_loc()
|
|
|
|
def scale_charset_down_button_pressed(self):
|
|
self.charset_swatch.decrease_scale()
|
|
self.reset_art()
|
|
self.charset_swatch.reset_loc()
|
|
self.palette_swatch.reset_loc()
|
|
|
|
def brush_size_up_button_pressed(self):
|
|
# any changes to tool's setting will force redraw of settings tab
|
|
self.ui.selected_tool.increase_brush_size()
|
|
|
|
def brush_size_down_button_pressed(self):
|
|
self.ui.selected_tool.decrease_brush_size()
|
|
|
|
def toggle_affect_char_button_pressed(self):
|
|
self.ui.selected_tool.toggle_affects_char()
|
|
|
|
def toggle_affect_fg_button_pressed(self):
|
|
self.ui.selected_tool.toggle_affects_fg()
|
|
|
|
def toggle_affect_bg_button_pressed(self):
|
|
self.ui.selected_tool.toggle_affects_bg()
|
|
|
|
def toggle_affect_xform_button_pressed(self):
|
|
self.ui.selected_tool.toggle_affects_xform()
|
|
|
|
def set_fill_boundary_char_button_pressed(self):
|
|
self.ui.fill_tool.boundary_mode = FILL_BOUND_CHAR
|
|
self.ui.tool_settings_changed = True
|
|
|
|
def set_fill_boundary_fg_button_pressed(self):
|
|
self.ui.fill_tool.boundary_mode = FILL_BOUND_FG_COLOR
|
|
self.ui.tool_settings_changed = True
|
|
|
|
def set_fill_boundary_bg_button_pressed(self):
|
|
self.ui.fill_tool.boundary_mode = FILL_BOUND_BG_COLOR
|
|
self.ui.tool_settings_changed = True
|
|
|
|
def pencil_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.pencil_tool)
|
|
|
|
def erase_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.erase_tool)
|
|
|
|
def grab_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.grab_tool)
|
|
|
|
def rotate_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.rotate_tool)
|
|
|
|
def text_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.text_tool)
|
|
|
|
def select_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.select_tool)
|
|
|
|
def paste_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.paste_tool)
|
|
|
|
def fill_tool_button_pressed(self):
|
|
self.ui.set_selected_tool(self.ui.fill_tool)
|
|
|
|
def set_xform(self, new_xform):
|
|
"tells UI elements to respect new xform"
|
|
self.charset_swatch.set_xform(new_xform)
|
|
self.update_xform_buttons()
|
|
|
|
def update_xform_buttons(self):
|
|
# light up button for current selected option
|
|
button_map = {
|
|
UV_NORMAL: self.xform_normal_button,
|
|
UV_ROTATE90: self.xform_90_button,
|
|
UV_ROTATE180: self.xform_180_button,
|
|
UV_ROTATE270: self.xform_270_button,
|
|
UV_FLIPX: self.xform_flipX_button,
|
|
UV_FLIPY: self.xform_flipY_button,
|
|
}
|
|
for b in button_map:
|
|
if b == self.ui.selected_xform:
|
|
button_map[b].normal_bg_color = self.highlight_color
|
|
else:
|
|
button_map[b].normal_bg_color = self.bg_color
|
|
self.xform_0_button.normal_bg_color = self.xform_normal_button.normal_bg_color
|
|
self.draw_buttons()
|
|
|
|
def xform_normal_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_NORMAL)
|
|
|
|
def xform_flipX_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_FLIPX)
|
|
|
|
def xform_flipY_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_FLIPY)
|
|
|
|
def xform_0_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_NORMAL)
|
|
|
|
def xform_90_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_ROTATE90)
|
|
|
|
def xform_180_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_ROTATE180)
|
|
|
|
def xform_270_button_pressed(self):
|
|
self.ui.set_selected_xform(UV_ROTATE270)
|
|
|
|
def choose_charset_button_pressed(self):
|
|
self.hide()
|
|
self.ui.open_dialog(CharSetChooserDialog)
|
|
|
|
def choose_palette_button_pressed(self):
|
|
self.hide()
|
|
self.ui.open_dialog(PaletteChooserDialog)
|
|
|
|
def draw_char_color_tab(self):
|
|
"draw non-button bits of this tab"
|
|
# charset renderable location will be set in update()
|
|
charset = self.ui.active_art.charset
|
|
palette = self.ui.active_art.palette
|
|
cqw, cqh = (
|
|
self.charset_swatch.art.quad_width,
|
|
self.charset_swatch.art.quad_height,
|
|
)
|
|
self.art.clear_frame_layer(0, 0, self.bg_color, self.fg_color)
|
|
# position & caption charset button
|
|
y = self.tab_height + 1
|
|
self.choose_charset_button.y = y
|
|
self.choose_charset_button.caption = " {} {} ".format(
|
|
CharSetChooserButton.caption,
|
|
charset.name,
|
|
)
|
|
self.choose_charset_button.width = len(self.choose_charset_button.caption)
|
|
# charset scale
|
|
charset_scale = "{:.2f}x".format(self.charset_swatch.char_scale)
|
|
x = -self.scale_charset_up_button.width * 2
|
|
self.art.write_string(0, 0, x, y, charset_scale, None, None, True)
|
|
# transform labels and buttons, eg
|
|
# Transform: [Normal] [Flip X] [Flip Y]
|
|
# Rotation: [ 0 ] [ 90] [180] [270]
|
|
x = 3
|
|
y += 1
|
|
self.art.write_string(0, 0, x, y, self.flip_label)
|
|
y += 1
|
|
self.art.write_string(0, 0, x, y, self.rotation_label)
|
|
# position & caption palette button
|
|
pal_caption_y = (cqh * charset.map_height) / self.art.quad_height
|
|
pal_caption_y += self.tab_height + 5
|
|
self.choose_palette_button.y = int(pal_caption_y)
|
|
self.choose_palette_button.caption = " {} {} ".format(
|
|
PaletteChooserButton.caption,
|
|
palette.name,
|
|
)
|
|
self.choose_palette_button.width = len(self.choose_palette_button.caption)
|
|
# set button states so captions draw properly
|
|
tab_width = int(self.tile_width / 2)
|
|
self.tool_tab_button.width = tab_width
|
|
self.char_color_tab_button.width = int(self.tile_width) - tab_width
|
|
self.char_color_tab_button.x = tab_width
|
|
|
|
def draw_tool_tab(self):
|
|
self.art.clear_frame_layer(0, 0, self.bg_color, self.fg_color)
|
|
# fill tool bar with dimmer color, highlight selected tool
|
|
for y in range(self.art.height):
|
|
for x in range(TOOL_PANE_WIDTH):
|
|
self.art.set_color_at(0, 0, x, y, self.ui.colors.medgrey, False)
|
|
# set selected tool BG lighter
|
|
y = self.tab_height + 1
|
|
for i, tool in enumerate(self.ui.tools):
|
|
tool_button = None
|
|
for button in self.tool_tab_buttons:
|
|
try:
|
|
if button.tool_name == tool.name:
|
|
tool_button = button
|
|
except Exception:
|
|
pass
|
|
tool_button.y = y + i
|
|
if tool == self.ui.selected_tool:
|
|
tool_button.normal_bg_color = self.ui.colors.lightgrey
|
|
else:
|
|
tool_button.normal_bg_color = self.ui.colors.medgrey
|
|
# draw current tool settings
|
|
x = TOOL_PANE_WIDTH + 1
|
|
y = self.tab_height + 1
|
|
label = "{} {}".format(
|
|
self.ui.selected_tool.button_caption,
|
|
self.tool_settings_label,
|
|
)
|
|
self.art.write_string(0, 0, x, y, label)
|
|
x += 1
|
|
y += 2
|
|
# brush size (if applicable)
|
|
if self.ui.selected_tool.brush_size:
|
|
self.brush_size_down_button.visible = True
|
|
self.brush_size_up_button.visible = True
|
|
label = self.brush_size_label
|
|
# calculate X of + and - buttons based on size string
|
|
self.brush_size_down_button.x = TOOL_PANE_WIDTH + len(label) + 2
|
|
label += " " * (self.brush_size_down_button.width + 1)
|
|
label += "{}".format(self.ui.selected_tool.brush_size)
|
|
self.brush_size_up_button.x = TOOL_PANE_WIDTH + len(label) + 3
|
|
self.art.write_string(0, 0, x, y, label)
|
|
else:
|
|
# if inapplicable, hide those controls
|
|
self.brush_size_down_button.visible = False
|
|
self.brush_size_up_button.visible = False
|
|
if self.ui.selected_tool.affects_masks:
|
|
# affects char/fg/bg settings
|
|
self.toggle_affect_char_button.visible = True
|
|
self.toggle_affect_fg_button.visible = True
|
|
self.toggle_affect_bg_button.visible = True
|
|
self.toggle_affect_xform_button.visible = True
|
|
y += 2
|
|
self.art.write_string(0, 0, x, y, self.affects_heading_label)
|
|
y += 1
|
|
|
|
# set affects-* button labels AND captions
|
|
def get_affects_char(affects):
|
|
return [0, self.check_char_index][affects]
|
|
|
|
w = self.toggle_affect_char_button.width
|
|
label_toggle_pairs = []
|
|
label_toggle_pairs += [
|
|
(self.affects_char_label, self.ui.selected_tool.affects_char)
|
|
]
|
|
label_toggle_pairs += [
|
|
(self.affects_fg_label, self.ui.selected_tool.affects_fg_color)
|
|
]
|
|
label_toggle_pairs += [
|
|
(self.affects_bg_label, self.ui.selected_tool.affects_bg_color)
|
|
]
|
|
label_toggle_pairs += [
|
|
(self.affects_xform_label, self.ui.selected_tool.affects_xform)
|
|
]
|
|
for label, toggle in label_toggle_pairs:
|
|
self.art.write_string(0, 0, x + w + 1, y, "{}".format(label))
|
|
# self.art.set_tile_at(0, 0, x, y, get_affects_char(toggle), 4, 2)
|
|
self.art.set_char_index_at(0, 0, x + 1, y, get_affects_char(toggle))
|
|
y += 1
|
|
else:
|
|
self.toggle_affect_char_button.visible = False
|
|
self.toggle_affect_fg_button.visible = False
|
|
self.toggle_affect_bg_button.visible = False
|
|
self.toggle_affect_xform_button.visible = False
|
|
# custom setting for fill tool: boundary mode
|
|
if type(self.ui.selected_tool) is FillTool:
|
|
y += 1
|
|
self.art.write_string(0, 0, x, y, self.fill_boundary_modes_label)
|
|
y += 1
|
|
# boundary mode buttons + labels
|
|
# x +=
|
|
labels = [
|
|
self.fill_boundary_char_label,
|
|
self.fill_boundary_fg_label,
|
|
self.fill_boundary_bg_label,
|
|
]
|
|
for i, button in enumerate(self.fill_boundary_mode_buttons):
|
|
button.visible = True
|
|
char = [self.radio_char_0_index, self.radio_char_1_index][
|
|
i == self.ui.fill_tool.boundary_mode
|
|
]
|
|
# self.ui.app.log(char)
|
|
self.art.set_char_index_at(0, 0, x + 1, y, char)
|
|
self.art.write_string(
|
|
0, 0, x + FillBoundaryModeCharButton.width + 1, y, labels[i]
|
|
)
|
|
y += 1
|
|
else:
|
|
for button in self.fill_boundary_mode_buttons:
|
|
button.visible = False
|
|
|
|
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 show(self):
|
|
# if already visible, bail - key repeat probably triggered this
|
|
if self.visible:
|
|
return
|
|
if self.ui.active_dialog:
|
|
return
|
|
self.visible = True
|
|
# visible, grab keyboard focus
|
|
self.ui.keyboard_focus_element = self
|
|
# set cursor as starting point for keyboard navigation
|
|
self.charset_swatch.set_cursor_selection_index(self.ui.selected_char)
|
|
if self.ui.pulldown.visible:
|
|
self.ui.menu_bar.close_active_menu()
|
|
self.reset_loc()
|
|
|
|
def toggle(self):
|
|
if self.visible:
|
|
self.hide()
|
|
else:
|
|
self.show()
|
|
|
|
def reset_loc(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
x, y = self.ui.get_screen_coords(self.ui.app.mouse_x, self.ui.app.mouse_y)
|
|
# center on mouse
|
|
w, h = (
|
|
self.tile_width * self.art.quad_width,
|
|
self.tile_height * self.art.quad_height,
|
|
)
|
|
x -= w / 2
|
|
y += h / 2
|
|
# clamp to edges of screen
|
|
self.x = max(-1, min(1 - w, x))
|
|
self.y = min(1, max(-1 + h, y))
|
|
# set location for sub elements
|
|
self.renderable.x, self.renderable.y = self.x, self.y
|
|
self.charset_swatch.reset_loc()
|
|
self.palette_swatch.reset_loc()
|
|
|
|
def hide(self):
|
|
self.visible = False
|
|
self.ui.keyboard_focus_element = None
|
|
self.ui.refocus_keyboard()
|
|
|
|
def set_active_charset(self, new_charset):
|
|
self.charset_swatch.art.charset = new_charset
|
|
self.palette_swatch.art.charset = new_charset
|
|
# make sure selected char isn't out of bounds w/ new set
|
|
self.ui.selected_char %= new_charset.last_index
|
|
self.ui.status_bar.set_active_charset(new_charset)
|
|
self.charset_swatch.reset()
|
|
# charset width drives palette swatch width
|
|
self.palette_swatch.reset()
|
|
self.reset_art()
|
|
|
|
def set_active_palette(self, new_palette):
|
|
self.charset_swatch.art.palette = new_palette
|
|
self.palette_swatch.art.palette = new_palette
|
|
# make sure selected colors aren't out of bounds w/ new palette
|
|
self.ui.selected_fg_color %= len(new_palette.colors) - 1
|
|
self.ui.selected_bg_color %= len(new_palette.colors) - 1
|
|
self.ui.status_bar.set_active_palette(new_palette)
|
|
self.palette_swatch.reset()
|
|
self.reset_art()
|
|
|
|
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 keyboard_navigate(self, dx, dy):
|
|
active_swatch = (
|
|
self.charset_swatch if self.cursor_char != -1 else self.palette_swatch
|
|
)
|
|
# TODO: can't handle cross-swatch navigation properly, restrict to chars
|
|
active_swatch = self.charset_swatch
|
|
# reverse up/down direction
|
|
active_swatch.move_cursor(self.cursor_box, dx, -dy)
|
|
|
|
def keyboard_select_item(self):
|
|
# called as ui.keyboard_focus_element
|
|
# simulate left/right click in popup to select stuff
|
|
self.select_key_pressed(self.ui.app.il.shift_pressed)
|
|
|
|
def select_key_pressed(self, mod_pressed):
|
|
mouse_button = [1, 3][mod_pressed]
|
|
self.clicked(mouse_button)
|
|
|
|
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 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()
|