[Icons] Add a sticker icon treatment

The pre-#7595 add, remove and delete icons were filled shapes with a black
outline and no disc. A tint cannot bring that back, and the badge disc is a
different icon language, so add a third value to the existing style axis.

IconStyle::Sticker draws the sentinel glyph twice: a near-black rim at the
band width plus one rim on each side, then the tone pass clipped to the glyph
and filled with a vertical gradient. Both treatments keep the disabled
fallback to the line glyph.

The fill rule moves into theme_icon_fill: the semantic tones are tuned for
text, so a filled shape gives its tone a saturation floor, and a dark-scheme
tone is pulled down rather than up. IconTint::resolve() applies the same floor
to line glyphs, and the badge disc uses the same fill, so a line, a badge and
a sticker next to each other carry the same green or red instead of a pale
line beside a saturated glyph.

The badge's glyph ink follows the scheme: white on light-scheme tones, as in
the pre-#7595 art, and the saturated tone darkened to an outline-dark ink on
dark-scheme tones, where the shared saturated disc is light enough that white
would sit near 2.7:1. The new test pins both cases so the glyph cannot
silently flip.

resolveSvgStrokeWidth() moves the stroke-width rewrite into
libcockatrice_utility so the badge and the sticker share it; the badge's local
copy is gone.

The sticker is opted into at 17 call sites: increment, decrement and
remove_row in the deck editor, settings pages, token dialog, server dialogs
and replay/deck storage tabs. The visual database's clear-filter button keeps
its trash can (icons/delete) in the line treatment: that glyph is new in
#7595, but it is the clearer metaphor, and a sticker rim closes its lid gap
at toolbar sizes.

The preview generators also gain a semantic-tint sheet for #7775 and a
line/badge/sticker family strip, so the three PR descriptions can show the
same rendered colors.
This commit is contained in:
Lukas Brübach 2026-11-06 20:19:54 +01:00
parent 717f64a88b
commit 876d66ffc9
25 changed files with 1027 additions and 127 deletions

View file

@ -248,19 +248,19 @@ void DeckEditorDeckDockWidget::createDeckDock()
&DeckEditorDeckDockWidget::applyActiveGroupCriteria);
aIncrement = new QAction(QString(), this);
aIncrement->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive));
aIncrement->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive, IconStyle::Sticker));
connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection);
auto *tbIncrement = new QToolButton(this);
tbIncrement->setDefaultAction(aIncrement);
aDecrement = new QAction(QString(), this);
aDecrement->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative));
aDecrement->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative, IconStyle::Sticker));
connect(aDecrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actDecrementSelection);
auto *tbDecrement = new QToolButton(this);
tbDecrement->setDefaultAction(aDecrement);
aRemoveCard = new QAction(QString(), this);
aRemoveCard->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
aRemoveCard->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
connect(aRemoveCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actRemoveCard);
auto *tbRemoveCard = new QToolButton(this);
tbRemoveCard->setDefaultAction(aRemoveCard);

View file

@ -64,7 +64,7 @@ void DeckEditorFilterDockWidget::createFiltersDock()
connect(filterBuilder, &FilterBuilder::add, filterModel, &FilterTreeModel::addFilter);
aClearFilterOne = new QAction(QString(), this);
aClearFilterOne->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative));
aClearFilterOne->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative, IconStyle::Sticker));
connect(aClearFilterOne, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterOne);
aClearFilterAll = new QAction(QString(), this);

View file

@ -38,7 +38,8 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
previousHosts = new QComboBox(this);
btnDeleteServer = new QPushButton(this);
btnDeleteServer->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
btnDeleteServer->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
btnDeleteServer->setFixedWidth(30);

View file

@ -108,10 +108,10 @@ DlgEditTokens::DlgEditTokens(QWidget *parent) : QDialog(parent), currentCard(nul
&DlgEditTokens::tokenSelectionChanged);
QAction *aAddToken = new QAction(tr("Add token"), this);
aAddToken->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive));
aAddToken->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive, IconStyle::Sticker));
connect(aAddToken, &QAction::triggered, this, &DlgEditTokens::actAddToken);
QAction *aRemoveToken = new QAction(tr("Remove token"), this);
aRemoveToken->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative));
aRemoveToken->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative, IconStyle::Sticker));
connect(aRemoveToken, &QAction::triggered, this, &DlgEditTokens::actRemoveToken);
auto *databaseToolBar = new QToolBar;

View file

@ -34,7 +34,8 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
previousHosts = new QComboBox(this);
btnDeleteServer = new QPushButton(this);
btnDeleteServer->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
btnDeleteServer->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
btnDeleteServer->setToolTip(tr("Delete the currently selected saved server"));
btnDeleteServer->setFixedWidth(30);

View file

@ -80,7 +80,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
}
aAdd = new QAction(this);
aAdd->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive));
aAdd->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive, IconStyle::Sticker));
connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL);
aEdit = new QAction(this);
@ -88,7 +88,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL);
aRemove = new QAction(this);
aRemove->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative));
aRemove->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative, IconStyle::Sticker));
connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL);
aRateLimit = new QAction(this);

View file

@ -150,7 +150,7 @@ MessagesSettingsPage::MessagesSettingsPage()
}
aAdd = new QAction(this);
aAdd->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive));
aAdd->setIcon(loadThemedIcon(QStringLiteral("icons/increment"), AppColor::Positive, IconStyle::Sticker));
connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd);
aEdit = new QAction(this);
@ -158,7 +158,7 @@ MessagesSettingsPage::MessagesSettingsPage()
connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit);
aRemove = new QAction(this);
aRemove->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative));
aRemove->setIcon(loadThemedIcon(QStringLiteral("icons/decrement"), AppColor::Negative, IconStyle::Sticker));
connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove);
auto *messageToolBar = new QToolBar;

View file

@ -199,7 +199,8 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder);
aDeleteLocalDeck = new QAction(this);
aDeleteLocalDeck->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
aDeleteLocalDeck->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck);
aOpenDecksFolder = new QAction(this);
@ -217,7 +218,8 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder);
aDeleteRemoteDeck = new QAction(this);
aDeleteRemoteDeck->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
aDeleteRemoteDeck->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck);
aShareDecks = new QAction(this);

View file

@ -155,7 +155,8 @@ QGroupBox *TabReplays::createLeftLayout()
aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder);
aDeleteLocalReplay = new QAction(this);
aDeleteLocalReplay->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
aDeleteLocalReplay->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay);
aOpenReplaysFolder = new QAction(this);
@ -217,7 +218,8 @@ QGroupBox *TabReplays::createRightLayout()
aKeep->setIcon(loadThemedIcon(QStringLiteral("icons/lock")));
connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay);
aDeleteRemoteReplay = new QAction(this);
aDeleteRemoteReplay->setIcon(loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative));
aDeleteRemoteReplay->setIcon(
loadThemedIcon(QStringLiteral("icons/remove_row"), AppColor::Negative, IconStyle::Sticker));
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
aGetReplayCode = new QAction(this);
aGetReplayCode->setIcon(loadThemedIcon(QStringLiteral("icons/share")));