feat(ui): outline per slot al text del menú

This commit is contained in:
JailDesigner committed 2026-05-14 10:11:50 +02:00
1 parent 0e8a195ddd
commit b6403e8de5
6 files changed
+103 -36

No files matched your search

+41 -14
View File
@@ -996,23 +996,38 @@ namespace {
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
// "..." si no cap a l'esquerra de la label. Extret de `renderPage`
// per a mantenir la complexitat cognitiva sota el llindar de
// 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 AVAILABLE_W = box_w - (2 * PAD_X) - cursor_width - LABEL_W - MIN_LABEL_VALUE_GAP;
if (AVAILABLE_W <= 0) { return; }
const std::string DISPLAY = truncateToFit(font, value, AVAILABLE_W, kerning);
if (DISPLAY.empty()) { return; }
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,
cursor_y,
DISPLAY,
kerning,
color);
color,
1,
outline);
}
// 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 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,
cursor_y,
Locale::get().text(page.title_key),
TITLE_KERNING,
UiConfig::get().menuTitle());
UiConfig::get().menuTitle(),
1,
UiConfig::get().menuTitleOutline());
cursor_y += TITLE_H + GAP_Y - TITLE_DESCENDER_TRIM;
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,
cursor_y,
page.subtitle,
TITLE_KERNING,
UiConfig::get().menuTitle());
UiConfig::get().menuTitle(),
1,
UiConfig::get().menuTitleOutline());
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);
Uint8 label_color = UiConfig::get().menuLabel();
Uint8 label_outline = UiConfig::get().menuLabelOutline();
if (item.kind == Item::Kind::LABEL) {
// 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
// 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_outline = UiConfig::get().menuTitleOutline();
} else if (SELECTED) {
label_color = UiConfig::get().menuCursor();
label_outline = UiConfig::get().menuCursorOutline();
}
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) {
items_font_->writeDX(Text::COLOR_FLAG,
items_font_->writeDX(Text::COLOR_FLAG | Text::STROKE_FLAG,
label_x - CURSOR_WIDTH,
cursor_y,
"> ",
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,
cursor_y,
LABEL_TEXT,
ITEM_KERNING,
label_color);
label_color,
1,
label_outline);
// Pàgines centrades no dibuixen columna de valor (s'assumeix que
// només tenen ACTION / LABEL); els Toggle/Cycle viuen en pàgines
// d'opcions amb layout esquerra-dreta.
if (!page.center_items && !VALUE.empty()) {
const Uint8 VALUE_COLOR = SELECTED ? UiConfig::get().menuCursor() : UiConfig::get().menuValue();
drawItemValue(*items_font_, box_x, box_w, cursor_y, CURSOR_WIDTH, LABEL_TEXT, VALUE, ITEM_KERNING, VALUE_COLOR);
const auto VALUE_COLORS = pickValueColors(SELECTED);
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;