From d06c7be6064c8dcb1de7860ce29bd08c153530d4 Mon Sep 17 00:00:00 2001 From: Jeff Patterson Date: Sat, 19 Sep 2026 07:16:56 -0500 Subject: [PATCH] Leave light icons out of the dark theme misc/make_icon_themes.py sorted icons by saturation alone, so a white page with a small red mark counted as line art and its dark copy turned the page black: the PDF import icon read black on black (#919, Kellermorph), and the folio, diagram and label icons came out as dark pages with a light border. An icon whose visible pixels are at least 30% near white is now "light art" and inherits from the qet theme untouched; it already reads on a dark toolbar. The generator also removes dark files it no longer produces, so a reclassified icon falls back to the light theme instead of keeping a stale copy. Thirteen files leave ico/themes/qet-dark. tests/qttest/tst_qeticons: every dark theme file, taken as its mean visible color, reaches 3:1 on the dark palette's window color; the lightest-pixel check it replaces let a black page with a light border through. Asking the dark theme for pdf-import, diagram, label, the folio icons and diagram_bg returns the light art. --- ico/icon-themes.qrc | 16 ---- ico/themes/qet-dark/128x128/printer.png | Bin 3586 -> 0 bytes ico/themes/qet-dark/16x16/diagram.png | Bin 132 -> 0 bytes ico/themes/qet-dark/16x16/folio-delete.png | Bin 154 -> 0 bytes ico/themes/qet-dark/16x16/folio-new.png | Bin 176 -> 0 bytes .../qet-dark/16x16/folio-properties.png | Bin 163 -> 0 bytes ico/themes/qet-dark/16x16/label.png | Bin 141 -> 0 bytes ico/themes/qet-dark/22x22/diagram.png | Bin 146 -> 0 bytes ico/themes/qet-dark/22x22/diagram_add.png | Bin 196 -> 0 bytes ico/themes/qet-dark/22x22/diagram_bg.png | Bin 702 -> 0 bytes ico/themes/qet-dark/22x22/diagram_del.png | Bin 181 -> 0 bytes .../qet-dark/22x22/dialog-information.png | Bin 189 -> 0 bytes ico/themes/qet-dark/22x22/folio-delete.png | Bin 181 -> 0 bytes ico/themes/qet-dark/22x22/folio-new.png | Bin 196 -> 0 bytes .../qet-dark/22x22/folio-properties.png | Bin 189 -> 0 bytes ico/themes/qet-dark/22x22/label.png | Bin 165 -> 0 bytes ico/themes/qet-dark/22x22/pdf-import.png | Bin 667 -> 0 bytes misc/make_icon_themes.py | 39 +++++++- tests/qttest/tst_qeticons.cpp | 84 ++++++++++++++---- 19 files changed, 100 insertions(+), 39 deletions(-) delete mode 100644 ico/themes/qet-dark/128x128/printer.png delete mode 100644 ico/themes/qet-dark/16x16/diagram.png delete mode 100644 ico/themes/qet-dark/16x16/folio-delete.png delete mode 100644 ico/themes/qet-dark/16x16/folio-new.png delete mode 100644 ico/themes/qet-dark/16x16/folio-properties.png delete mode 100644 ico/themes/qet-dark/16x16/label.png delete mode 100644 ico/themes/qet-dark/22x22/diagram.png delete mode 100644 ico/themes/qet-dark/22x22/diagram_add.png delete mode 100644 ico/themes/qet-dark/22x22/diagram_bg.png delete mode 100644 ico/themes/qet-dark/22x22/diagram_del.png delete mode 100644 ico/themes/qet-dark/22x22/dialog-information.png delete mode 100644 ico/themes/qet-dark/22x22/folio-delete.png delete mode 100644 ico/themes/qet-dark/22x22/folio-new.png delete mode 100644 ico/themes/qet-dark/22x22/folio-properties.png delete mode 100644 ico/themes/qet-dark/22x22/label.png delete mode 100644 ico/themes/qet-dark/22x22/pdf-import.png diff --git a/ico/icon-themes.qrc b/ico/icon-themes.qrc index 84c781a0e..ce55438b9 100644 --- a/ico/icon-themes.qrc +++ b/ico/icon-themes.qrc @@ -290,7 +290,6 @@ themes/qet-dark/16x16/conductor-edit.png themes/qet-dark/16x16/configure-toolbars.png themes/qet-dark/16x16/configure.png - themes/qet-dark/16x16/diagram.png themes/qet-dark/16x16/dialog-cancel.png themes/qet-dark/16x16/dialog-ok.png themes/qet-dark/16x16/document-export.png @@ -331,9 +330,6 @@ themes/qet-dark/16x16/folder-open.png themes/qet-dark/16x16/folder-show-all.png themes/qet-dark/16x16/folder.png - themes/qet-dark/16x16/folio-delete.png - themes/qet-dark/16x16/folio-new.png - themes/qet-dark/16x16/folio-properties.png themes/qet-dark/16x16/folio-ref-coming.png themes/qet-dark/16x16/go-company.png themes/qet-dark/16x16/go-down-double.png @@ -348,7 +344,6 @@ themes/qet-dark/16x16/item-copy.png themes/qet-dark/16x16/item-move.png themes/qet-dark/16x16/kdenlive-show-video.png - themes/qet-dark/16x16/label.png themes/qet-dark/16x16/list-add.png themes/qet-dark/16x16/list-remove.png themes/qet-dark/16x16/masquer.png @@ -381,15 +376,7 @@ themes/qet-dark/22x22/arrow-right.png themes/qet-dark/22x22/configure-toolbars.png themes/qet-dark/22x22/configure.png - themes/qet-dark/22x22/diagram.png - themes/qet-dark/22x22/diagram_add.png - themes/qet-dark/22x22/folio-new.png - themes/qet-dark/22x22/diagram_bg.png - themes/qet-dark/22x22/diagram_del.png - themes/qet-dark/22x22/folio-delete.png themes/qet-dark/22x22/dialog-cancel.png - themes/qet-dark/22x22/dialog-information.png - themes/qet-dark/22x22/folio-properties.png themes/qet-dark/22x22/dialog-ok.png themes/qet-dark/22x22/document-export.png themes/qet-dark/22x22/document-import.png @@ -425,7 +412,6 @@ themes/qet-dark/22x22/go-up.png themes/qet-dark/22x22/grid.png themes/qet-dark/22x22/insert-image.png - themes/qet-dark/22x22/label.png themes/qet-dark/22x22/landscape.png themes/qet-dark/22x22/line.png themes/qet-dark/22x22/list-add.png @@ -436,7 +422,6 @@ themes/qet-dark/22x22/object-locked.png themes/qet-dark/22x22/object-rotate-right.png themes/qet-dark/22x22/object-unlocked.png - themes/qet-dark/22x22/pdf-import.png themes/qet-dark/22x22/polygon.png themes/qet-dark/22x22/portrait.png themes/qet-dark/22x22/preferences-desktop-user.png @@ -472,7 +457,6 @@ themes/qet-dark/48x48/user-away.png themes/qet-dark/48x48/view-pim-journal.png themes/qet-dark/128x128/plasmagik.png - themes/qet-dark/128x128/printer.png themes/qet-dark/128x128/settings.png themes/qet-dark/scalable/edit-opacity.svg themes/qet-dark/scalable/image-flip-horizontal.svg diff --git a/ico/themes/qet-dark/128x128/printer.png b/ico/themes/qet-dark/128x128/printer.png deleted file mode 100644 index 6270960f92ae3d2bf04e5af742b90c960e1eed01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3586 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 diff --git a/ico/themes/qet-dark/16x16/folio-delete.png b/ico/themes/qet-dark/16x16/folio-delete.png deleted file mode 100644 index f1a9f324add4802629ac9a54185a2eaa14053e00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`iJmTwAr-fh|NQ@N&#cgdvuAJLTl@Q)`N5Ww z=jY~D#c;lE>-Dz{WA0#;a^Ge$=`CxF=e&uR-taISN>pA{&X&Ks^Ksz~?!|08Y|Ikf{rmp=7dpM#Dwfm2DI#AR-+#uP o?M2a1_xsoR_lQeqo;}B~Bt`xVZ_bm$K=T8?=armRD1Ucn t0fU~lxppJ>YdN5^7(8A5T-G@yGywp*OimL3 diff --git a/ico/themes/qet-dark/22x22/diagram_bg.png b/ico/themes/qet-dark/22x22/diagram_bg.png deleted file mode 100644 index 069b8d7ebef27e58b0a7c8612862e84030d94d1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702 zcmV;v0zv(WP)I zNCB7zFhN90DOCaRm2-YDIXRgh7#OhIt*3S!7mLMtP1Dv&r4sG#?hfX1xkuG%bsPX9 zk;r)}l{#HsUcQP(qYmeMD;A68{(vZ@yfjUd2_e=E!`MqElb-;nzhyF+JTeU9!^z3X znrWKM&$NTr&*goe*Nx&tiW0mSw%nWHQg|_4@s84{2j# zto$QH#ax)i;IhC$8ql2wmrr02DeAa9>Rk>WA^}vqf%u z8;*!bO-)S|Ja8hBxMqxPXS3N#C=~k9qkI6k&~?2A;Eijew`@}gv1VD;Jm>rcfNuTb zA{Y#QB%-}|JigV|=o^j3_Q=SHl2Q)Zw*5d#`LvUKUDs^tYYq-k0cz%>!QjmP8pu5J+5@q)o%3P6g89s`)TDV-~&z5vKK k8jXA+k+|;qYWf%P3t@|8`qlx2>;M1&07*qoM6N<$g6uj@qW}N^ diff --git a/ico/themes/qet-dark/22x22/diagram_del.png b/ico/themes/qet-dark/22x22/diagram_del.png deleted file mode 100644 index a4c24e4b71694bfa61ae7c5f6b79ee8d71953ef6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fHJ&bxAr-fh|NQ@N&#cQJ%-nlm z#leCDO=osif7f$0T)Ae=nU1RoY(O9?I@PU+XK(RyzrPtzdZla}*o>8?=N(BCC@%iI zZF~Oxe{SOc|NpON4wQUyZm#v$9Y$v|c`u9T9NNLFCN|@qg)RHxqf^*e*RAQ75$RcM e%5j#LjiFxHXrlW`k$j*#7(8A5T-G@yGywof`$J6t diff --git a/ico/themes/qet-dark/22x22/dialog-information.png b/ico/themes/qet-dark/22x22/dialog-information.png deleted file mode 100644 index ba57924209c281784ffaf009ad1d2ffd5f31ad3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fEuJopAr-fh|NQ@N&#cQJ%-qXx zmM1MSAt7N$^>@9{!_JYBGbgeh3k(d*T++6%{N0_E%WDG?m{)nvyJ~D;U~s2zVphw> zjg7YrgL+jYeR`fXdW*Ik$OyQgrZK^kXVroPkh!&|yG~EnpZ@dLFTctU4-T5wJZ0Kt lH*3162{+fVi>o-<8167QmmiB-p$c>lgQu&X%Q~loCIF?|N>Km+ diff --git a/ico/themes/qet-dark/22x22/folio-delete.png b/ico/themes/qet-dark/22x22/folio-delete.png deleted file mode 100644 index a4c24e4b71694bfa61ae7c5f6b79ee8d71953ef6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fHJ&bxAr-fh|NQ@N&#cQJ%-nlm z#leCDO=osif7f$0T)Ae=nU1RoY(O9?I@PU+XK(RyzrPtzdZla}*o>8?=N(BCC@%iI zZF~Oxe{SOc|NpON4wQUyZm#v$9Y$v|c`u9T9NNLFCN|@qg)RHxqf^*e*RAQ75$RcM e%5j#LjiFxHXrlW`k$j*#7(8A5T-G@yGywof`$J6t diff --git a/ico/themes/qet-dark/22x22/folio-new.png b/ico/themes/qet-dark/22x22/folio-new.png deleted file mode 100644 index b9cd38bdc8dc7ba8054c18d805079c3a18d40718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4f-JULvAr-fh|NQ@N&#cQJ%-nlm z#leCDO=osif7f$0T)Ae=nU1RoY(O9?I@PU+XK(RyzrPtzdZl{oO#^cZUd@&K%3{p1 z?MnymjM=ld@2&m)&D_qk=fu%xcXyZnSFV0}Zm#v$9Y#E6)6WI@9iRKTgLQMLkyN7C ufg79uEf0fU~lxppJ>YdN5^7(8A5T-G@yGywp*OimL3 diff --git a/ico/themes/qet-dark/22x22/folio-properties.png b/ico/themes/qet-dark/22x22/folio-properties.png deleted file mode 100644 index ba57924209c281784ffaf009ad1d2ffd5f31ad3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fEuJopAr-fh|NQ@N&#cQJ%-qXx zmM1MSAt7N$^>@9{!_JYBGbgeh3k(d*T++6%{N0_E%WDG?m{)nvyJ~D;U~s2zVphw> zjg7YrgL+jYeR`fXdW*Ik$OyQgrZK^kXVroPkh!&|yG~EnpZ@dLFTctU4-T5wJZ0Kt lH*3162{+fVi>o-<8167QmmiB-p$c>lgQu&X%Q~loCIF?|N>Km+ diff --git a/ico/themes/qet-dark/22x22/label.png b/ico/themes/qet-dark/22x22/label.png deleted file mode 100644 index 32b9b0fea70fc30bcb33996cc970481a990e1722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fIi4<#Ar-fh|NQ@N&#cQJ%-nlm z#leCDO=osie}5kfSH-OxvT4xSP{?O;^%$mGoJKH**LHnD^1T+J#H5e za^-6Tr@^B$3VW|L^%|6Yy!id=iJFuKz3`eZwSO=0aUHuT)$)vunc>ZA4du^aEc!ru O89ZJ6T-G@yGywnrAU>r4 diff --git a/ico/themes/qet-dark/22x22/pdf-import.png b/ico/themes/qet-dark/22x22/pdf-import.png deleted file mode 100644 index 6b4bf74f0d7e637eedd5b94e0c3905b8aa809b2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667 zcmV;M0%ZM(P)Da4t7>Ip>(oa z!68G2n4!gXXd!fH3x;wRF1^NFa^$%rB_?N@NWbug-|x83@Auw)@AnibB>;+|00`hB z;0F}C%K}Qx_uHkm#S*v&ya0lY*h>QV0lZWc<-ijzrKIJ74_Ijcds!>M<#v{ovIBep z$o2vLC%hdT0&;zVJ>i10(SyeN>m!_%v~I2-scF0)9p%t<`DB_r9U7wOx_mKBo(v8m zgy7Y|0oii7Yn2Z0!%&FW>@4Ap4Q>t(b57T>(`oKbPctwwLVA9lO1*w+`<_QJFgAu7 z3Q^nNr<}>~I+sJ6oMd2poGX4mzk2|yUX|*{$Glr!MiYX^Gc%-ic5n>?Yju_Q))sCL z2i^hBmCJ+{7x{R2$ZbvI(a{mtCnoqjH^(Ja?Fs7j=l!pbCGfnJsd^Rj`FsQdfvyeJ zYBg-zZcRmi5#X1iDBj>mBtjyQ=n5{CO02D|QLR>+safC-@D}*$3Etb=quj-(uIo}L z6sR}cq3b%1Ym_uJWeW=LWoW`3Lz*I3M?%xVObUcu~-blFxcMS1{egM zd4kv1*U9Jeos-?NTDOj_>qMhbrlzJaO_N|S$mZrIl}hD?Cpa7qlSm{^w%Y78o6WMj zyL*yz90$WNFijJ~Feny_xULIOX}4Pt$n WHITE_LIGHTNESS) + return white / len(pixels) >= WHITE_FRACTION + + def invert_lightness(image): """Invert lightness, keeping hue, saturation and alpha. @@ -177,7 +196,7 @@ def main(): light = [] # (alias, source) pairs, paths relative to ico/ dark = [] # paths relative to ico/, files exist on disk changed = 0 - line_art = colored = 0 + line_art = colored = light_art = 0 for size in SIZES: folder = ICO / size @@ -188,7 +207,10 @@ def main(): names.append(ALIASES[rel]) image = Image.open(png).convert("RGBA") art = is_line_art(image) - if art: + if art and has_light_fill(image): + art = False + light_art += 1 + elif art: line_art += 1 dark_image = None else: @@ -213,6 +235,15 @@ def main(): changed += write_if_changed(target, text) dark.append(f"themes/qet-dark/scalable/{name}") + # Drop dark files from an earlier run that are no longer generated, so + # a reclassified icon falls back to the light theme instead of keeping + # a stale copy. + for stale in sorted((THEMES / "qet-dark").glob("*/*")): + if stale.is_file() and stale.name != "index.theme" \ + and str(stale.relative_to(ICO)) not in dark: + stale.unlink() + changed += 1 + dirs = SIZES + ["scalable"] changed += write_if_changed(THEMES / "qet" / "index.theme", index_theme("qet", "QElectroTech icons", dirs)) @@ -231,8 +262,8 @@ def main(): qrc += [" ", "", ""] changed += write_if_changed(QRC, "\n".join(qrc)) - print(f"{line_art} line-art icons, {colored} colored icons, {len(SVGS)} SVGs; " - f"{changed} files written") + print(f"{line_art} line-art icons, {light_art} light icons, {colored} colored icons, " + f"{len(SVGS)} SVGs; {changed} files written or removed") if __name__ == "__main__": diff --git a/tests/qttest/tst_qeticons.cpp b/tests/qttest/tst_qeticons.cpp index db3b95191..1825ffe72 100644 --- a/tests/qttest/tst_qeticons.cpp +++ b/tests/qttest/tst_qeticons.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -34,11 +35,14 @@ table that uses them. Every icon name must resolve in both themes. The dark theme's files - must be light ink. A toolbar button painted with Fusion must show its - icon at 3:1 (WCAG 1.4.11) in both themes, with the disabled state - reading weaker than the enabled one, which is what was reversed on - macOS before (GitHub #466, #870). And the icons the elements panel - draws in its 50 px slots must stay small. + must read on the dark palette, and an icon drawn as a light object + (a page sheet, the PDF import icon of GitHub #919) must come through + the dark theme untouched rather than inverted into a black page. A + toolbar button painted with Fusion must show its icon at 3:1 (WCAG + 1.4.11) in both themes, with the disabled state reading weaker than + the enabled one, which is what was reversed on macOS before (GitHub + #466, #870). And the icons the elements panel draws in its 50 px + slots must stay small. */ class tst_qeticons : public QObject { @@ -47,7 +51,8 @@ class tst_qeticons : public QObject private slots: void initTestCase(); void everyIconResolvesInBothThemes(); - void darkThemeFilesAreLightInk(); + void darkThemeFilesReadOnDarkPalette(); + void lightIconsStayLightInDarkTheme(); void toolbarIconIsReadable_data(); void toolbarIconIsReadable(); void panelProjectIconStaysSmall(); @@ -70,18 +75,26 @@ namespace { return names; } - /// Lightness of the lightest visible pixel: the ink, in a dark-theme file. - double maxLightness(const QImage &image) + /** + Mean color of the visible pixels: what the icon reads as, taken + as a whole, on the background behind it. A page-shaped icon whose + white body was inverted keeps a light border, so the lightest + pixel would pass it; the mean does not. + */ + QColor meanVisibleColor(const QImage &image) { - double max = 0.0; + qint64 r = 0, g = 0, b = 0, n = 0; for (int y = 0; y < image.height(); ++y) for (int x = 0; x < image.width(); ++x) { const QColor c = image.pixelColor(x, y); - if (c.alpha() > 64) max = qMax(max, c.lightnessF()); + if (c.alpha() <= 64) continue; + r += c.red(); g += c.green(); b += c.blue(); ++n; } - return max; + return n ? QColor(r / n, g / n, b / n) : QColor(); } + + const QStringList kSizes = {"16x16", "22x22", "32x32", "48x48", "128x128"}; } void tst_qeticons::initTestCase() @@ -111,27 +124,60 @@ void tst_qeticons::everyIconResolvesInBothThemes() QVERIFY2(!QIcon::fromTheme(name).pixmap(22).isNull(), qPrintable(name)); } -void tst_qeticons::darkThemeFilesAreLightInk() +/** + Every file the generator put in the dark theme, taken as a whole, + must reach 3:1 against the dark palette's window color. Inverted + line art passes by a wide margin; an inverted light object (a white + page turned black) sits below 2:1 and fails, which is the defect + misc/make_icon_themes.py now avoids by leaving such icons to the + light theme. +*/ +void tst_qeticons::darkThemeFilesReadOnDarkPalette() { + const QColor window = QET::Palette::fusionDark().color(QPalette::Active, QPalette::Window); int checked = 0; - for (const QString &size : {"16x16", "22x22", "32x32"}) + for (const QString &size : kSizes) { QDir dir(QString(":/ico/themes/qet-dark/%1").arg(size)); for (const QString &file : dir.entryList({"*.png"}, QDir::Files)) { const QImage dark(dir.filePath(file)); - const QImage light(QString(":/ico/themes/qet/%1/%2").arg(size, file)); - QVERIFY2(!dark.isNull() && !light.isNull(), qPrintable(file)); - // A page-shaped icon keeps its white fill dark after inversion, - // so the mean would mislead; what matters is that the ink is light. - QVERIFY2(maxLightness(dark) >= 0.8, - qPrintable(QString("%1/%2 has no light ink").arg(size, file))); + QVERIFY2(!dark.isNull(), qPrintable(file)); + const QColor mean = meanVisibleColor(dark); + QVERIFY2(mean.isValid(), qPrintable(file)); + const double contrast = QET::Palette::contrastRatio(mean, window); + QVERIFY2(contrast >= kIconRatio, + qPrintable(QString("%1/%2 reads %3:1 on the dark window").arg(size, file).arg(contrast))); ++checked; } } QVERIFY(checked > 100); } +/** + Icons drawn as a light object are not in the dark theme; the theme + inherits them from "qet". Asking the dark theme for them must return + the light art, not a dark page (GitHub #919, the PDF import icon). +*/ +void tst_qeticons::lightIconsStayLightInDarkTheme() +{ + const QColor window = QET::Palette::fusionDark().color(QPalette::Active, QPalette::Window); + QIcon::setThemeName(QStringLiteral("qet-dark")); + for (const QString &name : {"pdf-import", "diagram", "label", "folio-new", "folio-delete", + "folio-properties", "diagram_bg"}) + { + QVERIFY2(!QFile::exists(QString(":/ico/themes/qet-dark/22x22/%1.png").arg(name)), + qPrintable(QString("%1 has a dark copy; the generator inverted a light icon").arg(name))); + const QImage image = QIcon::fromTheme(name).pixmap(22).toImage(); + QVERIFY2(!image.isNull(), qPrintable(name)); + const QColor mean = meanVisibleColor(image); + QVERIFY2(mean.lightnessF() > 0.6, + qPrintable(QString("%1 comes out dark in the dark theme (lightness %2)") + .arg(name).arg(mean.lightnessF()))); + QVERIFY2(QET::Palette::contrastRatio(mean, window) >= kIconRatio, qPrintable(name)); + } +} + void tst_qeticons::toolbarIconIsReadable_data() { QTest::addColumn("theme");