feat(ui): outline per slot al text del menú
This commit is contained in:
1 parent
0e8a195ddd
commit
b6403e8de5
6 files changed
+89
-22
No files matched your search
@@ -38,9 +38,13 @@ menu_themes:
|
|||||||
bg: 2 # marró-vermell fosc (#480000)
|
bg: 2 # marró-vermell fosc (#480000)
|
||||||
border: 232 # cian brillant (#00B6FF)
|
border: 232 # cian brillant (#00B6FF)
|
||||||
title: 237 # lavanda (#B6B6FF)
|
title: 237 # lavanda (#B6B6FF)
|
||||||
|
title_outline: 64 # navy fosc (#0000AA) — halo fred del títol
|
||||||
label: 58 # verd llima (#48FF00)
|
label: 58 # verd llima (#48FF00)
|
||||||
|
label_outline: 17 # verd molt fosc (#244800) — bord same-family del label
|
||||||
value: 232 # cian brillant (#00B6FF) — coincideix amb el border
|
value: 232 # cian brillant (#00B6FF) — coincideix amb el border
|
||||||
|
value_outline: 64 # navy fosc (#0000AA) — halo cian
|
||||||
cursor: 71 # magenta-vermell (#FF0055)
|
cursor: 71 # magenta-vermell (#FF0055)
|
||||||
|
cursor_outline: 1 # granat fosc (#240000) — halo càlid del cursor
|
||||||
disabled: 16 # verd fosc (#004800)
|
disabled: 16 # verd fosc (#004800)
|
||||||
# Tema "egípci": família or/ambre/marró coherent amb el HUD i el
|
# Tema "egípci": família or/ambre/marró coherent amb el HUD i el
|
||||||
# rètol de transició de sala (room_text). El bg comparteix to amb
|
# rètol de transició de sala (room_text). El bg comparteix to amb
|
||||||
@@ -50,9 +54,13 @@ menu_themes:
|
|||||||
bg: 10 # marró cremat (#482400)
|
bg: 10 # marró cremat (#482400)
|
||||||
border: 55 # or brillant (#FFDA00)
|
border: 55 # or brillant (#FFDA00)
|
||||||
title: 127 # groc llimona viu (#FFFF55)
|
title: 127 # groc llimona viu (#FFFF55)
|
||||||
|
title_outline: 19 # oliva fosc (#6D4800) — halo càlid del groc
|
||||||
label: 47 # ambre (#FFB600)
|
label: 47 # ambre (#FFB600)
|
||||||
|
label_outline: 11 # marró (#6D2400) — bord càlid del label
|
||||||
value: 191 # crema clar (#FFFFAA)
|
value: 191 # crema clar (#FFFFAA)
|
||||||
|
value_outline: 19 # oliva fosc (#6D4800) — recolza el crema
|
||||||
cursor: 23 # vermilló (#FF4800)
|
cursor: 23 # vermilló (#FF4800)
|
||||||
|
cursor_outline: 9 # marró molt fosc (#240000) — halo profund del cursor
|
||||||
disabled: 19 # oliva fosc (#6D4800)
|
disabled: 19 # oliva fosc (#6D4800)
|
||||||
|
|
||||||
timings:
|
timings:
|
||||||
|
|||||||
@@ -198,42 +198,50 @@ void DefineButtons::render() const {
|
|||||||
|
|
||||||
// Títol: nom del mando.
|
// Títol: nom del mando.
|
||||||
const std::string TITLE = pad_name_;
|
const std::string TITLE = pad_name_;
|
||||||
title_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
title_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
TITLE,
|
TITLE,
|
||||||
TITLE_KERNING,
|
TITLE_KERNING,
|
||||||
UiConfig::get().menuTitle());
|
UiConfig::get().menuTitle(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuTitleOutline());
|
||||||
cursor_y += TITLE_H + LINE_GAP;
|
cursor_y += TITLE_H + LINE_GAP;
|
||||||
|
|
||||||
if (message_shown_) {
|
if (message_shown_) {
|
||||||
// Pantalla final: missatge de configuració completa.
|
// Pantalla final: missatge de configuració completa.
|
||||||
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
Locale::get().text("menu.values.define_buttons_complete"),
|
Locale::get().text("menu.values.define_buttons_complete"),
|
||||||
ITEM_KERNING,
|
ITEM_KERNING,
|
||||||
UiConfig::get().menuValue());
|
UiConfig::get().menuValue(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuValueOutline());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instrucció + acció pendent. Replica visualment el WindowMessage de
|
// Instrucció + acció pendent. Replica visualment el WindowMessage de
|
||||||
// coffee_crisis_arcade_edition.
|
// coffee_crisis_arcade_edition.
|
||||||
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
Locale::get().text("menu.values.press_button"),
|
Locale::get().text("menu.values.press_button"),
|
||||||
ITEM_KERNING,
|
ITEM_KERNING,
|
||||||
UiConfig::get().menuLabel());
|
UiConfig::get().menuLabel(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuLabelOutline());
|
||||||
cursor_y += ITEM_H + LINE_GAP;
|
cursor_y += ITEM_H + LINE_GAP;
|
||||||
|
|
||||||
if (index_ < sequence_.size()) {
|
if (index_ < sequence_.size()) {
|
||||||
const auto& current = sequence_[index_];
|
const auto& current = sequence_[index_];
|
||||||
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
items_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
Locale::get().text(current.label_key),
|
Locale::get().text(current.label_key),
|
||||||
ITEM_KERNING,
|
ITEM_KERNING,
|
||||||
UiConfig::get().menuCursor());
|
UiConfig::get().menuCursor(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuCursorOutline());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,10 @@ UiConfig::UiConfig()
|
|||||||
menu_value_(Defaults::UI::MENU_VALUE),
|
menu_value_(Defaults::UI::MENU_VALUE),
|
||||||
menu_cursor_(Defaults::UI::MENU_CURSOR),
|
menu_cursor_(Defaults::UI::MENU_CURSOR),
|
||||||
menu_disabled_(Defaults::UI::MENU_DISABLED),
|
menu_disabled_(Defaults::UI::MENU_DISABLED),
|
||||||
|
menu_title_outline_(Defaults::UI::MENU_TITLE_OUTLINE),
|
||||||
|
menu_label_outline_(Defaults::UI::MENU_LABEL_OUTLINE),
|
||||||
|
menu_value_outline_(Defaults::UI::MENU_VALUE_OUTLINE),
|
||||||
|
menu_cursor_outline_(Defaults::UI::MENU_CURSOR_OUTLINE),
|
||||||
hud_bg_(Defaults::UI::HUD_BG),
|
hud_bg_(Defaults::UI::HUD_BG),
|
||||||
hud_p1_label_(Defaults::UI::HUD_P1_LABEL),
|
hud_p1_label_(Defaults::UI::HUD_P1_LABEL),
|
||||||
hud_p1_value_(Defaults::UI::HUD_P1_VALUE),
|
hud_p1_value_(Defaults::UI::HUD_P1_VALUE),
|
||||||
@@ -238,6 +242,10 @@ void UiConfig::load(const std::string& file_path) {
|
|||||||
readColorIndex(t, "value", menu_value_);
|
readColorIndex(t, "value", menu_value_);
|
||||||
readColorIndex(t, "cursor", menu_cursor_);
|
readColorIndex(t, "cursor", menu_cursor_);
|
||||||
readColorIndex(t, "disabled", menu_disabled_);
|
readColorIndex(t, "disabled", menu_disabled_);
|
||||||
|
readColorIndex(t, "title_outline", menu_title_outline_);
|
||||||
|
readColorIndex(t, "label_outline", menu_label_outline_);
|
||||||
|
readColorIndex(t, "value_outline", menu_value_outline_);
|
||||||
|
readColorIndex(t, "cursor_outline", menu_cursor_outline_);
|
||||||
} else {
|
} else {
|
||||||
Log::warn("UiConfig", "menu_theme '{}' no existeix a menu_themes — s'usen defaults", THEME_NAME);
|
Log::warn("UiConfig", "menu_theme '{}' no existeix a menu_themes — s'usen defaults", THEME_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ class UiConfig {
|
|||||||
[[nodiscard]] auto menuValue() const -> Uint8 { return menu_value_; }
|
[[nodiscard]] auto menuValue() const -> Uint8 { return menu_value_; }
|
||||||
[[nodiscard]] auto menuCursor() const -> Uint8 { return menu_cursor_; }
|
[[nodiscard]] auto menuCursor() const -> Uint8 { return menu_cursor_; }
|
||||||
[[nodiscard]] auto menuDisabled() const -> Uint8 { return menu_disabled_; }
|
[[nodiscard]] auto menuDisabled() const -> Uint8 { return menu_disabled_; }
|
||||||
|
[[nodiscard]] auto menuTitleOutline() const -> Uint8 { return menu_title_outline_; }
|
||||||
|
[[nodiscard]] auto menuLabelOutline() const -> Uint8 { return menu_label_outline_; }
|
||||||
|
[[nodiscard]] auto menuValueOutline() const -> Uint8 { return menu_value_outline_; }
|
||||||
|
[[nodiscard]] auto menuCursorOutline() const -> Uint8 { return menu_cursor_outline_; }
|
||||||
[[nodiscard]] auto hudBg() const -> Uint8 { return hud_bg_; }
|
[[nodiscard]] auto hudBg() const -> Uint8 { return hud_bg_; }
|
||||||
// Famílies de color del HUD per bloc (LEFT/CENTER/RIGHT). Cada
|
// Famílies de color del HUD per bloc (LEFT/CENTER/RIGHT). Cada
|
||||||
// família té label (etiqueta apagada), value (valor destacat) i
|
// família té label (etiqueta apagada), value (valor destacat) i
|
||||||
@@ -214,6 +218,10 @@ class UiConfig {
|
|||||||
Uint8 menu_value_;
|
Uint8 menu_value_;
|
||||||
Uint8 menu_cursor_;
|
Uint8 menu_cursor_;
|
||||||
Uint8 menu_disabled_;
|
Uint8 menu_disabled_;
|
||||||
|
Uint8 menu_title_outline_;
|
||||||
|
Uint8 menu_label_outline_;
|
||||||
|
Uint8 menu_value_outline_;
|
||||||
|
Uint8 menu_cursor_outline_;
|
||||||
Uint8 hud_bg_;
|
Uint8 hud_bg_;
|
||||||
Uint8 hud_p1_label_;
|
Uint8 hud_p1_label_;
|
||||||
Uint8 hud_p1_value_;
|
Uint8 hud_p1_value_;
|
||||||
|
|||||||
@@ -996,23 +996,38 @@ namespace {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parell (fill, outline) per a un text del menú. Extret per a
|
||||||
|
// collir-los en un sol pas i no inflar la complexitat cognitiva
|
||||||
|
// de `renderPage` amb dos ternaris en paral·lel.
|
||||||
|
struct MenuColorPair {
|
||||||
|
Uint8 fill;
|
||||||
|
Uint8 outline;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pickValueColors(bool selected) -> MenuColorPair {
|
||||||
|
if (selected) { return {.fill = UiConfig::get().menuCursor(), .outline = UiConfig::get().menuCursorOutline()}; }
|
||||||
|
return {.fill = UiConfig::get().menuValue(), .outline = UiConfig::get().menuValueOutline()};
|
||||||
|
}
|
||||||
|
|
||||||
// Dibuixa el value d'un item a la dreta del box, truncant-lo amb
|
// Dibuixa el value d'un item a la dreta del box, truncant-lo amb
|
||||||
// "..." si no cap a l'esquerra de la label. Extret de `renderPage`
|
// "..." si no cap a l'esquerra de la label. Extret de `renderPage`
|
||||||
// per a mantenir la complexitat cognitiva sota el llindar de
|
// per a mantenir la complexitat cognitiva sota el llindar de
|
||||||
// clang-tidy.
|
// clang-tidy.
|
||||||
void drawItemValue(Text& font, int box_x, int box_w, int cursor_y, int cursor_width, const std::string& label, const std::string& value, int kerning, Uint8 color) {
|
void drawItemValue(Text& font, int box_x, int box_w, int cursor_y, int cursor_width, const std::string& label, const std::string& value, int kerning, Uint8 color, Uint8 outline) {
|
||||||
const int LABEL_W = font.length(label, kerning);
|
const int LABEL_W = font.length(label, kerning);
|
||||||
const int AVAILABLE_W = box_w - (2 * PAD_X) - cursor_width - LABEL_W - MIN_LABEL_VALUE_GAP;
|
const int AVAILABLE_W = box_w - (2 * PAD_X) - cursor_width - LABEL_W - MIN_LABEL_VALUE_GAP;
|
||||||
if (AVAILABLE_W <= 0) { return; }
|
if (AVAILABLE_W <= 0) { return; }
|
||||||
const std::string DISPLAY = truncateToFit(font, value, AVAILABLE_W, kerning);
|
const std::string DISPLAY = truncateToFit(font, value, AVAILABLE_W, kerning);
|
||||||
if (DISPLAY.empty()) { return; }
|
if (DISPLAY.empty()) { return; }
|
||||||
const int VALUE_W = font.length(DISPLAY, kerning);
|
const int VALUE_W = font.length(DISPLAY, kerning);
|
||||||
font.writeDX(Text::COLOR_FLAG,
|
font.writeDX(Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
box_x + box_w - PAD_X - VALUE_W,
|
box_x + box_w - PAD_X - VALUE_W,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
DISPLAY,
|
DISPLAY,
|
||||||
kerning,
|
kerning,
|
||||||
color);
|
color,
|
||||||
|
1,
|
||||||
|
outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mandos visibles al cycle d'un jugador: tots els connectats
|
// Mandos visibles al cycle d'un jugador: tots els connectats
|
||||||
@@ -1305,21 +1320,25 @@ void ServiceMenu::renderPage(const Page& page, int box_x, int box_y, int box_w)
|
|||||||
const int CURSOR_WIDTH = items_font_->length("> ", ITEM_KERNING);
|
const int CURSOR_WIDTH = items_font_->length("> ", ITEM_KERNING);
|
||||||
const int LABEL_X = box_x + PAD_X + CURSOR_WIDTH;
|
const int LABEL_X = box_x + PAD_X + CURSOR_WIDTH;
|
||||||
|
|
||||||
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
Locale::get().text(page.title_key),
|
Locale::get().text(page.title_key),
|
||||||
TITLE_KERNING,
|
TITLE_KERNING,
|
||||||
UiConfig::get().menuTitle());
|
UiConfig::get().menuTitle(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuTitleOutline());
|
||||||
cursor_y += TITLE_H + GAP_Y - TITLE_DESCENDER_TRIM;
|
cursor_y += TITLE_H + GAP_Y - TITLE_DESCENDER_TRIM;
|
||||||
|
|
||||||
if (!page.subtitle.empty()) {
|
if (!page.subtitle.empty()) {
|
||||||
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
|
font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
CENTER_X,
|
CENTER_X,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
page.subtitle,
|
page.subtitle,
|
||||||
TITLE_KERNING,
|
TITLE_KERNING,
|
||||||
UiConfig::get().menuTitle());
|
UiConfig::get().menuTitle(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuTitleOutline());
|
||||||
cursor_y += TITLE_H + GAP_Y - TITLE_DESCENDER_TRIM;
|
cursor_y += TITLE_H + GAP_Y - TITLE_DESCENDER_TRIM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1344,13 +1363,17 @@ void ServiceMenu::renderPage(const Page& page, int box_x, int box_y, int box_w)
|
|||||||
: formatValue(item);
|
: formatValue(item);
|
||||||
|
|
||||||
Uint8 label_color = UiConfig::get().menuLabel();
|
Uint8 label_color = UiConfig::get().menuLabel();
|
||||||
|
Uint8 label_outline = UiConfig::get().menuLabelOutline();
|
||||||
if (item.kind == Item::Kind::LABEL) {
|
if (item.kind == Item::Kind::LABEL) {
|
||||||
// Mateix to que el títol de la pàgina: les LABELs actuen com
|
// Mateix to que el títol de la pàgina: les LABELs actuen com
|
||||||
// a subtítol-divisor (p.ex. "Jugador 1" a Teclat, "ver.X.Y" a
|
// a subtítol-divisor (p.ex. "Jugador 1" a Teclat, "ver.X.Y" a
|
||||||
// Sistema) i han de tenir la mateixa presència visual.
|
// Sistema) i han de tenir la mateixa presència visual — fill
|
||||||
|
// i outline van aparellats.
|
||||||
label_color = UiConfig::get().menuTitle();
|
label_color = UiConfig::get().menuTitle();
|
||||||
|
label_outline = UiConfig::get().menuTitleOutline();
|
||||||
} else if (SELECTED) {
|
} else if (SELECTED) {
|
||||||
label_color = UiConfig::get().menuCursor();
|
label_color = UiConfig::get().menuCursor();
|
||||||
|
label_outline = UiConfig::get().menuCursorOutline();
|
||||||
}
|
}
|
||||||
|
|
||||||
int label_x = LABEL_X;
|
int label_x = LABEL_X;
|
||||||
@@ -1362,27 +1385,31 @@ void ServiceMenu::renderPage(const Page& page, int box_x, int box_y, int box_w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (SELECTED) {
|
if (SELECTED) {
|
||||||
items_font_->writeDX(Text::COLOR_FLAG,
|
items_font_->writeDX(Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
label_x - CURSOR_WIDTH,
|
label_x - CURSOR_WIDTH,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
"> ",
|
"> ",
|
||||||
ITEM_KERNING,
|
ITEM_KERNING,
|
||||||
UiConfig::get().menuCursor());
|
UiConfig::get().menuCursor(),
|
||||||
|
1,
|
||||||
|
UiConfig::get().menuCursorOutline());
|
||||||
}
|
}
|
||||||
|
|
||||||
items_font_->writeDX(Text::COLOR_FLAG,
|
items_font_->writeDX(Text::COLOR_FLAG | Text::STROKE_FLAG,
|
||||||
label_x,
|
label_x,
|
||||||
cursor_y,
|
cursor_y,
|
||||||
LABEL_TEXT,
|
LABEL_TEXT,
|
||||||
ITEM_KERNING,
|
ITEM_KERNING,
|
||||||
label_color);
|
label_color,
|
||||||
|
1,
|
||||||
|
label_outline);
|
||||||
|
|
||||||
// Pàgines centrades no dibuixen columna de valor (s'assumeix que
|
// Pàgines centrades no dibuixen columna de valor (s'assumeix que
|
||||||
// només tenen ACTION / LABEL); els Toggle/Cycle viuen en pàgines
|
// només tenen ACTION / LABEL); els Toggle/Cycle viuen en pàgines
|
||||||
// d'opcions amb layout esquerra-dreta.
|
// d'opcions amb layout esquerra-dreta.
|
||||||
if (!page.center_items && !VALUE.empty()) {
|
if (!page.center_items && !VALUE.empty()) {
|
||||||
const Uint8 VALUE_COLOR = SELECTED ? UiConfig::get().menuCursor() : UiConfig::get().menuValue();
|
const auto VALUE_COLORS = pickValueColors(SELECTED);
|
||||||
drawItemValue(*items_font_, box_x, box_w, cursor_y, CURSOR_WIDTH, LABEL_TEXT, VALUE, ITEM_KERNING, VALUE_COLOR);
|
drawItemValue(*items_font_, box_x, box_w, cursor_y, CURSOR_WIDTH, LABEL_TEXT, VALUE, ITEM_KERNING, VALUE_COLORS.fill, VALUE_COLORS.outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor_y += ITEM_H;
|
cursor_y += ITEM_H;
|
||||||
|
|||||||
@@ -253,6 +253,14 @@ namespace Defaults {
|
|||||||
constexpr Uint8 MENU_VALUE = 232; // Valor actual.
|
constexpr Uint8 MENU_VALUE = 232; // Valor actual.
|
||||||
constexpr Uint8 MENU_CURSOR = 71; // Item seleccionat.
|
constexpr Uint8 MENU_CURSOR = 71; // Item seleccionat.
|
||||||
constexpr Uint8 MENU_DISABLED = 16; // Label en gris quan no aplica.
|
constexpr Uint8 MENU_DISABLED = 16; // Label en gris quan no aplica.
|
||||||
|
// Outlines del text del menú (8 veïns). Un per slot perquè
|
||||||
|
// cada to de fill demana un halo diferent: el contorn no és
|
||||||
|
// un negre uniform sinó una versió fosca de la família del
|
||||||
|
// fill (efecte "embossing").
|
||||||
|
constexpr Uint8 MENU_TITLE_OUTLINE = 0;
|
||||||
|
constexpr Uint8 MENU_LABEL_OUTLINE = 0;
|
||||||
|
constexpr Uint8 MENU_VALUE_OUTLINE = 0;
|
||||||
|
constexpr Uint8 MENU_CURSOR_OUTLINE = 0;
|
||||||
|
|
||||||
// Índexs de paleta per al marcador de joc (HUD de la GameScene).
|
// Índexs de paleta per al marcador de joc (HUD de la GameScene).
|
||||||
// Tres "famílies" de color — un parell (label, value) + sombra per
|
// Tres "famílies" de color — un parell (label, value) + sombra per
|
||||||
|
|||||||
Reference in new issue
Block a user