[Interface] Split the hover panel icons by emphasis and semantics

Category tabs only navigate between pages, so they take the quiet level; the
action grid is the surface the player works in and takes the primary level. Both
stay line glyphs: a filled treatment reads as a second state on every cell.

Hue marks an outcome where the glyph cannot. Play, clone and to hand are
Positive; reduce life and graveyard Negative; play face down and exile
Informational. Direction-only moves (top and bottom of library) stay neutral
because the arrow already carries the meaning.
This commit is contained in:
Lukas Brübach 2026-11-08 20:07:52 +01:00
parent 0aa0407f16
commit cc9c93eb57

View file

@ -77,16 +77,26 @@ enum HoverPanelPage
PAGE_TOKEN PAGE_TOKEN
}; };
// Category tabs are the panel's wayfinding, so they carry the primary ink; the // The action grid is what the panel exists for, so it carries the primary ink;
// action grid's glyphs label the button text beneath them and stay quiet. // the category tabs only navigate between pages and stay quiet. Both use the
// line glyph: the filled treatments read as a second state, and every cell in
// the grid would carry one.
static QIcon categoryTabIcon(const QString &prefix) static QIcon categoryTabIcon(const QString &prefix)
{ {
return loadThemedIcon(prefix, IconTint::primary()); return loadThemedIcon(prefix, IconTint::quiet());
} }
static QIcon actionGridIcon(const QString &prefix) static QIcon actionGridIcon(const QString &prefix)
{ {
return loadThemedIcon(prefix, IconTint::quiet()); return loadThemedIcon(prefix, IconTint::primary());
}
// A semantic action is the exception the icon rules allow: the hue names an
// outcome (play, gain, loss, hidden information). It keeps the grid's line
// glyph so hue is the only difference from its neighbours.
static QIcon semanticActionIcon(const QString &prefix, AppColor::Role role)
{
return loadThemedIcon(prefix, IconTint(role, IconEmphasis::Primary));
} }
CardHoverWidget::CardHoverWidget(QWidget *parent) : QFrame(parent), currentCard(nullptr), currentPlayer(nullptr) CardHoverWidget::CardHoverWidget(QWidget *parent) : QFrame(parent), currentCard(nullptr), currentPlayer(nullptr)
@ -842,10 +852,8 @@ void CardHoverWidget::buildMainTab(const QList<CardState *> &cardList, const QSt
grid.addButton(createGridButton( grid.addButton(createGridButton(
tr("Hide"), actionGridIcon(QStringLiteral("icons/arrow_left")), tr("Hide"), actionGridIcon(QStringLiteral("icons/arrow_left")),
[actions, cardList]() { actions->actHide(cardList); }, mainPage)); [actions, cardList]() { actions->actHide(cardList); }, mainPage));
grid.addButton( grid.addButton(createGridButton(tr("Clone"),
createGridButton(tr("Clone"), semanticActionIcon(QStringLiteral("icons/increment"), AppColor::Positive),
loadThemedIcon(QStringLiteral("icons/increment"),
IconTint(AppColor::Positive, IconEmphasis::Quiet), IconStyle::Sticker),
invoke(cmClone), mainPage)); invoke(cmClone), mainPage));
grid.addSection(createSectionHeader(tr("Select"), mainPage)); grid.addSection(createSectionHeader(tr("Select"), mainPage));
grid.addButton(createGridButton( grid.addButton(createGridButton(
@ -875,10 +883,8 @@ void CardHoverWidget::buildMainTab(const QList<CardState *> &cardList, const QSt
grid.addButton(createGridButton(tr("Peek at card face"), actionGridIcon(QStringLiteral("icons/view")), grid.addButton(createGridButton(tr("Peek at card face"), actionGridIcon(QStringLiteral("icons/view")),
invoke(cmPeek), mainPage)); invoke(cmPeek), mainPage));
} }
grid.addButton( grid.addButton(createGridButton(tr("Clone"),
createGridButton(tr("Clone"), semanticActionIcon(QStringLiteral("icons/increment"), AppColor::Positive),
loadThemedIcon(QStringLiteral("icons/increment"),
IconTint(AppColor::Positive, IconEmphasis::Quiet), IconStyle::Sticker),
invoke(cmClone), mainPage)); invoke(cmClone), mainPage));
grid.addButton( grid.addButton(
createGridButton(tr("Attach to card..."), actionGridIcon(QStringLiteral("icons/pin")), attach, mainPage)); createGridButton(tr("Attach to card..."), actionGridIcon(QStringLiteral("icons/pin")), attach, mainPage));
@ -893,8 +899,7 @@ void CardHoverWidget::buildMainTab(const QList<CardState *> &cardList, const QSt
tr("Set annotation..."), actionGridIcon(QStringLiteral("icons/pencil")), tr("Set annotation..."), actionGridIcon(QStringLiteral("icons/pencil")),
[actions, cardList]() { actions->actRequestSetAnnotationDialog(cardList); }, mainPage)); [actions, cardList]() { actions->actRequestSetAnnotationDialog(cardList); }, mainPage));
grid.addButton(createGridButton( grid.addButton(createGridButton(
tr("Reduce life"), tr("Reduce life"), semanticActionIcon(QStringLiteral("icons/arrow_down"), AppColor::Negative),
loadThemedIcon(QStringLiteral("icons/arrow_down"), IconTint(AppColor::Negative, IconEmphasis::Quiet)),
[actions, cardList]() { actions->actReduceLifeByPower(cardList); }, mainPage)); [actions, cardList]() { actions->actReduceLifeByPower(cardList); }, mainPage));
grid.addSection(createSectionHeader(tr("Select"), mainPage)); grid.addSection(createSectionHeader(tr("Select"), mainPage));
grid.addButton(createGridButton( grid.addButton(createGridButton(
@ -907,16 +912,13 @@ void CardHoverWidget::buildMainTab(const QList<CardState *> &cardList, const QSt
ButtonGrid grid(layout); ButtonGrid grid(layout);
grid.addSection(createSectionHeader(tr("Zone"), mainPage)); grid.addSection(createSectionHeader(tr("Zone"), mainPage));
grid.addButton(createGridButton( grid.addButton(createGridButton(
tr("Play"), tr("Play"), semanticActionIcon(QStringLiteral("icons/arrow_right"), AppColor::Positive),
loadThemedIcon(QStringLiteral("icons/arrow_right"), IconTint(AppColor::Positive, IconEmphasis::Quiet)),
[actions, cardList]() { actions->actPlay(cardList); }, mainPage)); [actions, cardList]() { actions->actPlay(cardList); }, mainPage));
grid.addButton(createGridButton( grid.addButton(createGridButton(
tr("Play Face Down"), actionGridIcon(QStringLiteral("icons/arrow_right")), tr("Play Face Down"), semanticActionIcon(QStringLiteral("icons/arrow_right"), AppColor::Informational),
[actions, cardList]() { actions->actPlayFacedown(cardList); }, mainPage)); [actions, cardList]() { actions->actPlayFacedown(cardList); }, mainPage));
grid.addButton( grid.addButton(createGridButton(tr("Clone"),
createGridButton(tr("Clone"), semanticActionIcon(QStringLiteral("icons/increment"), AppColor::Positive),
loadThemedIcon(QStringLiteral("icons/increment"),
IconTint(AppColor::Positive, IconEmphasis::Quiet), IconStyle::Sticker),
invoke(cmClone), mainPage)); invoke(cmClone), mainPage));
if (zoneName == ZoneNames::STACK || zoneName == ZoneNames::HAND || zoneName == ZoneNames::GRAVE || if (zoneName == ZoneNames::STACK || zoneName == ZoneNames::HAND || zoneName == ZoneNames::GRAVE ||
zoneName == ZoneNames::EXILE) { zoneName == ZoneNames::EXILE) {
@ -967,11 +969,14 @@ void CardHoverWidget::buildMoveTab(const QList<CardState *> &cardList, const QSt
invoke(cmMoveToBottomLibrary), movePage)); invoke(cmMoveToBottomLibrary), movePage));
grid.addButton( grid.addButton(
createGridButton(tr("Table"), actionGridIcon(QStringLiteral("icons/player")), invoke(cmMoveToTable), movePage)); createGridButton(tr("Table"), actionGridIcon(QStringLiteral("icons/player")), invoke(cmMoveToTable), movePage));
grid.addButton(createGridButton(tr("Hand"), actionGridIcon(QStringLiteral("icons/arrow_left")), grid.addButton(createGridButton(tr("Hand"),
semanticActionIcon(QStringLiteral("icons/arrow_left"), AppColor::Positive),
invoke(cmMoveToHand), movePage)); invoke(cmMoveToHand), movePage));
grid.addButton(createGridButton(tr("Graveyard"), actionGridIcon(QStringLiteral("icons/arrow_down")), grid.addButton(createGridButton(tr("Graveyard"),
semanticActionIcon(QStringLiteral("icons/arrow_down"), AppColor::Negative),
invoke(cmMoveToGraveyard), movePage)); invoke(cmMoveToGraveyard), movePage));
grid.addButton(createGridButton(tr("Exile"), actionGridIcon(QStringLiteral("icons/arrow_right")), grid.addButton(createGridButton(tr("Exile"),
semanticActionIcon(QStringLiteral("icons/arrow_right"), AppColor::Informational),
invoke(cmMoveToExile), movePage)); invoke(cmMoveToExile), movePage));
// Reveal actions live on hand / custom zones in CardMenu // Reveal actions live on hand / custom zones in CardMenu