168 lines
5.6 KiB
Python
168 lines
5.6 KiB
Python
from ui_colors import UIColors
|
|
|
|
TEXT_LEFT = 0
|
|
TEXT_CENTER = 1
|
|
TEXT_RIGHT = 2
|
|
|
|
BUTTON_STATES = ["normal", "hovered", "clicked", "dimmed"]
|
|
|
|
|
|
class UIButton:
|
|
"clickable button that does something in a UIElement"
|
|
|
|
# x/y/width/height given in tile scale
|
|
x, y = 0, 0
|
|
width, height = 1, 1
|
|
caption = "TEST"
|
|
caption_justify = TEXT_LEFT
|
|
# paint caption from string, or not
|
|
should_draw_caption = True
|
|
caption_y = 0
|
|
callback = None
|
|
normal_fg_color = UIColors.black
|
|
normal_bg_color = UIColors.lightgrey
|
|
hovered_fg_color = UIColors.black
|
|
hovered_bg_color = UIColors.white
|
|
clicked_fg_color = UIColors.white
|
|
clicked_bg_color = UIColors.black
|
|
dimmed_fg_color = UIColors.black
|
|
dimmed_bg_color = UIColors.medgrey
|
|
# dimmed is a special, alternative-to-normal state
|
|
dimmed = False
|
|
can_hover = True
|
|
can_click = True
|
|
visible = True
|
|
# if true, this button is invisible and used for special trickery
|
|
never_draw = False
|
|
# weird (gross?) thing: other code can stash an argument to callback here
|
|
cb_arg = None
|
|
# if True, pass in mouse button #
|
|
pass_mouse_button = False
|
|
# if true, clear all characters before painting a new caption
|
|
clear_before_caption_draw = False
|
|
# if true, display a tooltip when hovered, and dismiss it when unhovered.
|
|
# contents set from get_tooltip_text and positioned by get_tooltip_location.
|
|
tooltip_on_hover = False
|
|
|
|
def __init__(self, element, starting_state=None):
|
|
self.element = element
|
|
self.state = starting_state or "normal"
|
|
|
|
def log_event(self, event_type):
|
|
"common code for button event logging"
|
|
if self.element.ui.logg:
|
|
self.element.ui.app.log(
|
|
"UIButton: %s's %s %s"
|
|
% (self.element.__class__.__name__, self.__class__.__name__, event_type)
|
|
)
|
|
|
|
def set_state(self, new_state):
|
|
if new_state not in BUTTON_STATES:
|
|
self.element.ui.app.log(
|
|
"Unrecognized state for button %s: %s"
|
|
% (self.__class__.__name__, new_state)
|
|
)
|
|
return
|
|
self.dimmed = new_state == "dimmed"
|
|
self.state = new_state
|
|
self.set_state_colors()
|
|
|
|
def get_state_colors(self, state):
|
|
fg = getattr(self, "%s_fg_color" % state)
|
|
bg = getattr(self, "%s_bg_color" % state)
|
|
return fg, bg
|
|
|
|
def set_state_colors(self):
|
|
if self.never_draw:
|
|
return
|
|
# set colors for entire button area based on current state
|
|
if self.dimmed and self.state == "normal":
|
|
self.state = "dimmed"
|
|
# just bail if we're trying to draw something out of bounds
|
|
if (
|
|
self.x + self.width > self.element.art.width
|
|
or self.y + self.height > self.element.art.height
|
|
):
|
|
return
|
|
fg, bg = self.get_state_colors(self.state)
|
|
for y in range(self.height):
|
|
for x in range(self.width):
|
|
self.element.art.set_tile_at(0, 0, self.x + x, self.y + y, None, fg, bg)
|
|
|
|
def update_tooltip(self):
|
|
tt = self.element.ui.tooltip
|
|
tt.reset_art()
|
|
tt.set_text(self.get_tooltip_text())
|
|
tt.tile_x, tt.tile_y = self.get_tooltip_location()
|
|
tt.reset_loc()
|
|
|
|
def hover(self):
|
|
self.log_event("hovered")
|
|
self.set_state("hovered")
|
|
if self.tooltip_on_hover:
|
|
self.element.ui.tooltip.visible = True
|
|
self.update_tooltip()
|
|
|
|
def unhover(self):
|
|
self.log_event("unhovered")
|
|
if self.dimmed:
|
|
self.set_state("dimmed")
|
|
else:
|
|
self.set_state("normal")
|
|
if self.tooltip_on_hover:
|
|
# if two buttons are adjacent, we might be unhovering this one
|
|
# right after hovering the other in the same frame. if so,
|
|
# don't dismiss the tooltip
|
|
another_tooltip = False
|
|
for b in self.element.hovered_buttons:
|
|
if b is self:
|
|
continue
|
|
if b.tooltip_on_hover:
|
|
another_tooltip = True
|
|
if not another_tooltip:
|
|
self.element.ui.tooltip.visible = False
|
|
|
|
def click(self):
|
|
self.log_event("clicked")
|
|
self.set_state("clicked")
|
|
|
|
def unclick(self):
|
|
self.log_event("unclicked")
|
|
if self in self.element.hovered_buttons:
|
|
self.hover()
|
|
else:
|
|
self.unhover()
|
|
|
|
def get_tooltip_text(self):
|
|
"override in a subclass to define this button's tooltip text"
|
|
return "ERROR"
|
|
|
|
def get_tooltip_location(self):
|
|
"override in a subclass to define this button's tooltip screen location"
|
|
return 10, 10
|
|
|
|
def draw_caption(self):
|
|
y = self.y + self.caption_y
|
|
text = self.caption
|
|
# trim if too long
|
|
text = text[: self.width]
|
|
if self.caption_justify == TEXT_CENTER:
|
|
text = text.center(self.width)
|
|
elif self.caption_justify == TEXT_RIGHT:
|
|
text = text.rjust(self.width)
|
|
# just bail if we're trying to draw something out of bounds
|
|
if self.x + len(text) > self.element.art.width:
|
|
return
|
|
if self.clear_before_caption_draw:
|
|
for ty in range(self.height):
|
|
for tx in range(self.width):
|
|
self.element.art.set_char_index_at(0, 0, self.x + tx, y + ty, 0)
|
|
# leave FG color None; should already have been set
|
|
self.element.art.write_string(0, 0, self.x, y, text, None)
|
|
|
|
def draw(self):
|
|
if self.never_draw:
|
|
return
|
|
self.set_state_colors()
|
|
if self.should_draw_caption:
|
|
self.draw_caption()
|