diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake
index ff89ebf68..b0df63774 100644
--- a/cmake/qet_compilation_vars.cmake
+++ b/cmake/qet_compilation_vars.cmake
@@ -434,6 +434,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ElementsCollection/elementcollectionhandler.h
${QET_DIR}/sources/ElementsCollection/elementcollectionitem.cpp
${QET_DIR}/sources/ElementsCollection/elementcollectionitem.h
+ ${QET_DIR}/sources/ElementsCollection/elementpreviewdelegate.cpp
+ ${QET_DIR}/sources/ElementsCollection/elementpreviewdelegate.h
${QET_DIR}/sources/ElementsCollection/elementscollectionmodel.cpp
${QET_DIR}/sources/ElementsCollection/elementscollectionmodel.h
${QET_DIR}/sources/ElementsCollection/elementscollectionwidget.cpp
diff --git a/sources/ElementsCollection/elementpreviewdelegate.cpp b/sources/ElementsCollection/elementpreviewdelegate.cpp
new file mode 100644
index 000000000..72ed74b6a
--- /dev/null
+++ b/sources/ElementsCollection/elementpreviewdelegate.cpp
@@ -0,0 +1,54 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#include "elementpreviewdelegate.h"
+
+#include
+
+#include "../qetpalette.h"
+
+/**
+ @brief ElementPreviewDelegate::initStyleOption
+ After the base class has filled the option from the model, replace
+ the icon with one that reads on a dark palette. The adapted icon is
+ built from the pixmap the view is about to draw, at the view's
+ decoration size and device pixel ratio.
+*/
+void ElementPreviewDelegate::initStyleOption(QStyleOptionViewItem *option,
+ const QModelIndex &index) const
+{
+ QStyledItemDelegate::initStyleOption(option, index);
+ if (option->icon.isNull() || !QET::Palette::isDark(option->palette))
+ return;
+
+ const qint64 key = option->icon.cacheKey();
+ const auto it = m_dark_icons.constFind(key);
+ if (it != m_dark_icons.constEnd())
+ {
+ option->icon = *it;
+ return;
+ }
+
+ const qreal dpr = option->widget ? option->widget->devicePixelRatio() : 1.0;
+ const QPixmap source = option->icon.pixmap(option->decorationSize, dpr);
+ const QPixmap adapted = QET::Palette::forPalette(source, option->palette);
+ QIcon icon = option->icon;
+ if (adapted.cacheKey() != source.cacheKey())
+ icon = QIcon(adapted);
+ m_dark_icons.insert(key, icon);
+ option->icon = icon;
+}
diff --git a/sources/ElementsCollection/elementpreviewdelegate.h b/sources/ElementsCollection/elementpreviewdelegate.h
new file mode 100644
index 000000000..1fb17c2a9
--- /dev/null
+++ b/sources/ElementsCollection/elementpreviewdelegate.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#ifndef ELEMENTPREVIEWDELEGATE_H
+#define ELEMENTPREVIEWDELEGATE_H
+
+#include
+#include
+#include
+
+/**
+ @brief The ElementPreviewDelegate class
+ Draws the items of an element collection tree with icons that read on
+ the current palette. Element previews are black line art drawn for a
+ white sheet; on a dark palette this delegate hands the view the same
+ picture with its lightness inverted (QET::Palette::forPalette), so the
+ ink is light on the dark row. Colored icons, such as folders, are left
+ alone, and nothing changes on a light palette. Adapted icons are kept
+ per source icon, so a repaint costs a hash lookup.
+*/
+class ElementPreviewDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+ public:
+ using QStyledItemDelegate::QStyledItemDelegate;
+
+ protected:
+ void initStyleOption(QStyleOptionViewItem *option,
+ const QModelIndex &index) const override;
+
+ private:
+ mutable QHash m_dark_icons;
+};
+
+#endif
diff --git a/sources/ElementsCollection/elementscollectionwidget.cpp b/sources/ElementsCollection/elementscollectionwidget.cpp
index 605489430..3c015c716 100644
--- a/sources/ElementsCollection/elementscollectionwidget.cpp
+++ b/sources/ElementsCollection/elementscollectionwidget.cpp
@@ -725,7 +725,10 @@ void ElementsCollectionWidget::showThisDir()
ElementCollectionItem *eci =
elementCollectionItemForIndex(m_showed_index);
if (eci)
+ {
eci->setBackground(QBrush());
+ eci->setForeground(QBrush());
+ }
}
m_showed_index = m_index_at_context_menu;
@@ -736,7 +739,11 @@ void ElementsCollectionWidget::showThisDir()
ElementCollectionItem *eci =
elementCollectionItemForIndex(m_showed_index);
if (eci)
+ {
+ // Amber under black, whatever the palette's text color.
eci->setBackground(QBrush(QColor(255, 204, 0, 255)));
+ eci->setForeground(QBrush(Qt::black));
+ }
search();
}
else
@@ -755,7 +762,10 @@ void ElementsCollectionWidget::resetShowThisDir()
ElementCollectionItem *eci = elementCollectionItemForIndex(
m_showed_index);
if (eci)
+ {
eci->setBackground(QBrush());
+ eci->setForeground(QBrush());
+ }
}
m_showed_index = QModelIndex();
diff --git a/sources/ElementsCollection/elementstreeview.cpp b/sources/ElementsCollection/elementstreeview.cpp
index 39fe4a70b..581bc1b64 100644
--- a/sources/ElementsCollection/elementstreeview.cpp
+++ b/sources/ElementsCollection/elementstreeview.cpp
@@ -16,6 +16,8 @@
along with QElectroTech. If not, see .
*/
#include "elementstreeview.h"
+#include "elementpreviewdelegate.h"
+#include "../qetpalette.h"
#include "../factory/elementfactory.h"
#include "../qetgraphicsitem/element.h"
@@ -41,24 +43,11 @@ static int MAX_DND_PIXMAP_HEIGHT = 375;
ElementsTreeView::ElementsTreeView(QWidget *parent) :
QTreeView(parent)
{
- // force du noir sur une alternance de blanc (comme le schema) et de gris
- // clair, avec du blanc sur bleu pas trop fonce pour la selection
- //
- // Element icons are rendered with colors read directly from each .elmt
- // file (almost always black linework, matching printed-schematic
- // convention) onto a transparent background -- so this view must keep
- // a light background regardless of the OS/desktop theme, or the icons
- // become invisible on dark themes. QAbstractItemView paints its rows
- // using the viewport's palette, not the view's own, so the palette
- // must be applied to both to actually take effect under every style.
- QPalette qp = palette();
- qp.setColor(QPalette::Text, Qt::black);
- qp.setColor(QPalette::Base, Qt::white);
- qp.setColor(QPalette::AlternateBase, QColor("#e8e8e8"));
- qp.setColor(QPalette::Highlight, QColor("#678db2"));
- qp.setColor(QPalette::HighlightedText, Qt::black);
- setPalette(qp);
- viewport()->setPalette(qp);
+ // Rows follow the application palette. Element previews are black
+ // line art drawn for a white sheet; ElementPreviewDelegate adapts them
+ // to a dark palette, so this view no longer has to force a light one
+ // (bugtracker #335).
+ setItemDelegate(new ElementPreviewDelegate(this));
}
/**
@@ -220,7 +209,7 @@ void ElementsTreeView::startElementDrag(const ElementsLocation &location)
&elmt_creation_state));
if (elmt_creation_state) { return; }
- QPixmap elmt_pixmap(temp_elmt->pixmap());
+ QPixmap elmt_pixmap(QET::Palette::forPalette(temp_elmt->pixmap(), palette()));
QPoint elmt_hotspot(temp_elmt->hotspot());
//Adjust the size of the pixmap if he is too big
diff --git a/sources/elementscollectioncache.cpp b/sources/elementscollectioncache.cpp
index 10a3ab92d..aa510b104 100644
--- a/sources/elementscollectioncache.cpp
+++ b/sources/elementscollectioncache.cpp
@@ -52,6 +52,18 @@ ElementsCollectionCache::ElementsCollectionCache(const QString &database_path, Q
QSqlQuery(cache_db_).exec("PRAGMA locking_mode = EXCLUSIVE");
QSqlQuery(cache_db_).exec("PRAGMA synchronous = OFF");
+ // Previews used to be stored on an opaque white sheet; they are
+ // transparent now, so the collection tree can adapt them to a
+ // dark palette. A cache written before that is dropped once.
+ QSqlQuery(cache_db_).exec("CREATE TABLE IF NOT EXISTS meta"
+ "(key VARCHAR(32) NOT NULL PRIMARY KEY, value VARCHAR(64));");
+ QSqlQuery meta(cache_db_);
+ meta.exec("SELECT value FROM meta WHERE key = 'pixmaps'");
+ if (!meta.next() || meta.value(0).toString() != QLatin1String("transparent")) {
+ QSqlQuery(cache_db_).exec("DROP TABLE IF EXISTS pixmaps");
+ QSqlQuery(cache_db_).exec("DROP TABLE IF EXISTS names");
+ QSqlQuery(cache_db_).exec("REPLACE INTO meta (key, value) VALUES ('pixmaps', 'transparent')");
+ }
#if TODO_LIST
#pragma message("@TODO the tables could already exist, handle that case.")
#endif
diff --git a/sources/elementspanel.cpp b/sources/elementspanel.cpp
index 9e20e2f37..114394f7d 100644
--- a/sources/elementspanel.cpp
+++ b/sources/elementspanel.cpp
@@ -54,24 +54,8 @@ ElementsPanel::ElementsPanel(QWidget *parent) :
setDropIndicatorShown(true);
setAutoExpandDelay(1000);
- // force du noir sur une alternance de blanc (comme le schema) et de gris
- // clair, avec du blanc sur bleu pas trop fonce pour la selection
- //
- // Element icons are rendered with colors read directly from each .elmt
- // file (almost always black linework, matching printed-schematic
- // convention) onto a transparent background -- so this view must keep
- // a light background regardless of the OS/desktop theme, or the icons
- // become invisible on dark themes. QAbstractItemView paints its rows
- // using the viewport's palette, not the view's own, so the palette
- // must be applied to both to actually take effect under every style.
- QPalette qp = palette();
- qp.setColor(QPalette::Text, Qt::black);
- qp.setColor(QPalette::Base, Qt::white);
- qp.setColor(QPalette::AlternateBase, QColor("#e8e8e8"));
- qp.setColor(QPalette::Highlight, QColor("#678db2"));
- qp.setColor(QPalette::HighlightedText, Qt::black);
- setPalette(qp);
- viewport()->setPalette(qp);
+ // Rows follow the application palette; the icons shown here come from
+ // the icon theme, which has a dark variant.
// we handle double click on items ourselves
connect(this, &ElementsPanel::itemDoubleClicked, this, &ElementsPanel::slot_doubleClick);
diff --git a/sources/factory/elementpicturefactory.cpp b/sources/factory/elementpicturefactory.cpp
index b3c55792f..c3b0f0745 100644
--- a/sources/factory/elementpicturefactory.cpp
+++ b/sources/factory/elementpicturefactory.cpp
@@ -146,12 +146,13 @@ QPixmap ElementPictureFactory::pixmap(const ElementsLocation &location)
QPixmap pix(w, h);
//Element definitions almost always draw with a hardcoded black
//stroke color, on the assumption of the white diagram sheet they
- //are normally placed on. A transparent background here makes
- //that stroke disappear against a dark widget/tree-view background
- //(bugtracker #335). Give it an opaque white background instead -
- //exactly what the element already assumes visually, in every
- //context this pixmap is used (tree icons, drag icon, previews).
- pix.fill(Qt::white);
+ //are normally placed on. The pixmap is kept as drawn, on a
+ //transparent background: the places that show it (the
+ //collection tree through ElementPreviewDelegate, the drag icon)
+ //adapt it to the palette with QET::Palette::forPalette(), so a
+ //dark palette gets light ink instead of black on black
+ //(bugtracker #335).
+ pix.fill(Qt::transparent);
QPainter painter(&pix);
painter.setRenderHint(QPainter::Antialiasing, true);
diff --git a/sources/qetpalette.cpp b/sources/qetpalette.cpp
index 6589e7603..63c5f98bb 100644
--- a/sources/qetpalette.cpp
+++ b/sources/qetpalette.cpp
@@ -17,6 +17,7 @@
*/
#include "qetpalette.h"
+#include
#include
#include
@@ -177,3 +178,68 @@ QPalette QET::Palette::forFusion(const QPalette &platform)
return withPlatformAccent(isDark(platform) ? fusionDark() : fusionLight(),
platform);
}
+
+bool QET::Palette::isLineArt(const QImage &image)
+{
+ const QImage source = image.convertToFormat(QImage::Format_ARGB32);
+ int visible = 0;
+ int saturated = 0;
+ for (int y = 0; y < source.height(); ++y)
+ {
+ const QRgb *line = reinterpret_cast(source.constScanLine(y));
+ for (int x = 0; x < source.width(); ++x)
+ {
+ if (qAlpha(line[x]) <= 64)
+ continue;
+ ++visible;
+ const QColor color(line[x]);
+ if (color.hslSaturationF() > 0.25 && color.value() > 60)
+ ++saturated;
+ }
+ }
+ return visible > 0 && saturated < visible * 0.20;
+}
+
+QImage QET::Palette::invertedLightness(const QImage &image)
+{
+ // Lightness of pure black after inversion: the dark palette's text.
+ const qreal ink = 220.0 / 255.0;
+ QImage result = image.convertToFormat(QImage::Format_ARGB32);
+ qreal darkest = 1.0;
+ for (int y = 0; y < result.height(); ++y)
+ {
+ const QRgb *line = reinterpret_cast(result.constScanLine(y));
+ for (int x = 0; x < result.width(); ++x)
+ if (qAlpha(line[x]) > 64)
+ darkest = qMin(darkest, QColor(line[x]).lightnessF());
+ }
+ const qreal span = qMax(1.0 - darkest, 1e-6);
+ for (int y = 0; y < result.height(); ++y)
+ {
+ QRgb *line = reinterpret_cast(result.scanLine(y));
+ for (int x = 0; x < result.width(); ++x)
+ {
+ const int alpha = qAlpha(line[x]);
+ if (alpha == 0)
+ continue;
+ const QColor color(line[x]);
+ const qreal lightness = qBound(0.0, ink * (1.0 - (color.lightnessF() - darkest) / span), 1.0);
+ QColor out = QColor::fromHslF(qMax(color.hslHueF(), 0.0), color.hslSaturationF(), lightness);
+ out.setAlpha(alpha);
+ line[x] = out.rgba();
+ }
+ }
+ return result;
+}
+
+QPixmap QET::Palette::forPalette(const QPixmap &pixmap, const QPalette &palette)
+{
+ if (pixmap.isNull() || !isDark(palette))
+ return pixmap;
+ const QImage image = pixmap.toImage();
+ if (!isLineArt(image))
+ return pixmap;
+ QPixmap result = QPixmap::fromImage(invertedLightness(image));
+ result.setDevicePixelRatio(pixmap.devicePixelRatio());
+ return result;
+}
diff --git a/sources/qetpalette.h b/sources/qetpalette.h
index cbc7e6edb..0df5bb3a8 100644
--- a/sources/qetpalette.h
+++ b/sources/qetpalette.h
@@ -18,7 +18,9 @@
#ifndef QET_PALETTE_H
#define QET_PALETTE_H
+#include
#include
+#include
class QStyle;
@@ -87,6 +89,29 @@ namespace QET {
platform's accent color kept when it is readable.
*/
QPalette forFusion(const QPalette &platform);
+
+ /**
+ True when fewer than a fifth of the visible pixels are
+ saturated: black or gray line art, which is what element
+ previews and most of QET's own icons are.
+ */
+ bool isLineArt(const QImage &image);
+
+ /**
+ The image with its lightness inverted and hue, saturation and
+ alpha kept: the darkest ink becomes light gray (220), white
+ becomes black. The rule misc/make_icon_themes.py applies when
+ it builds the dark icon theme.
+ */
+ QImage invertedLightness(const QImage &image);
+
+ /**
+ A picture drawn for a white sheet, made to read on palette:
+ returned as is on a light palette, and with its lightness
+ inverted on a dark one when it is line art. Colored art is
+ left alone either way.
+ */
+ QPixmap forPalette(const QPixmap &pixmap, const QPalette &palette);
}
}
diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt
index c06489b20..8bfccf9b1 100644
--- a/tests/qttest/CMakeLists.txt
+++ b/tests/qttest/CMakeLists.txt
@@ -96,7 +96,9 @@ add_executable(
tst_qetpalette.cpp
inkcontrast.h
${QET_DIR}/sources/qetpalette.cpp
- ${QET_DIR}/sources/qetpalette.h)
+ ${QET_DIR}/sources/qetpalette.h
+ ${QET_DIR}/sources/ElementsCollection/elementpreviewdelegate.cpp
+ ${QET_DIR}/sources/ElementsCollection/elementpreviewdelegate.h)
add_test(NAME tst_qetpalette COMMAND tst_qetpalette)
target_include_directories(tst_qetpalette PRIVATE ${QET_DIR}/sources)
target_link_libraries(tst_qetpalette PRIVATE Qt::Test Qt::Widgets)
diff --git a/tests/qttest/tst_qetpalette.cpp b/tests/qttest/tst_qetpalette.cpp
index ccbe07f15..4aea6d76d 100644
--- a/tests/qttest/tst_qetpalette.cpp
+++ b/tests/qttest/tst_qetpalette.cpp
@@ -24,11 +24,15 @@
#include
#include
#include
+#include
#include
+#include
+#include
#include
#include
#include "inkcontrast.h"
+#include "ElementsCollection/elementpreviewdelegate.h"
#include "qetpalette.h"
using QET::Palette::contrastRatio;
@@ -62,6 +66,10 @@ class tst_qetpalette : public QObject
void styleIsFusionMatchesObjectName();
void renderedWidgetsAreReadable_data();
void renderedWidgetsAreReadable();
+ void lineArtRuleSeparatesInkFromColor();
+ void invertedLightnessKeepsHueAndAlpha();
+ void elementPreviewReadsOnBothPalettes();
+ void previewDelegateAdaptsLineArtOnly();
private:
static void addPaletteRows();
@@ -308,6 +316,127 @@ void tst_qetpalette::renderedWidgetsAreReadable()
qPrintable(QString("line edit text: %1").arg(edit_contrast)));
}
+namespace {
+ /// A 40 x 40 transparent picture with a 3 px stroke square in color, as
+ /// an element preview is drawn for the white sheet.
+ QImage strokeSquare(const QColor &color)
+ {
+ QImage image(40, 40, QImage::Format_ARGB32);
+ image.fill(Qt::transparent);
+ QPainter painter(&image);
+ painter.setPen(QPen(color, 3));
+ painter.drawRect(6, 6, 27, 27);
+ return image;
+ }
+
+ /// The most frequent color of a rendering: its background.
+ QRgb dominant(const QImage &image)
+ {
+ QHash histogram;
+ for (int y = 0; y < image.height(); ++y)
+ for (int x = 0; x < image.width(); ++x)
+ ++histogram[image.pixel(x, y)];
+ QRgb best = 0;
+ int count = -1;
+ for (auto it = histogram.cbegin(); it != histogram.cend(); ++it)
+ if (it.value() > count) { count = it.value(); best = it.key(); }
+ return best;
+ }
+}
+
+void tst_qetpalette::lineArtRuleSeparatesInkFromColor()
+{
+ QVERIFY(QET::Palette::isLineArt(strokeSquare(Qt::black)));
+ QVERIFY(QET::Palette::isLineArt(strokeSquare(QColor(80, 80, 80))));
+ QVERIFY(!QET::Palette::isLineArt(strokeSquare(Qt::red)));
+ QVERIFY(!QET::Palette::isLineArt(strokeSquare(QColor(30, 96, 176))));
+ QVERIFY(!QET::Palette::isLineArt(QImage()));
+}
+
+/**
+ Black ink becomes the dark palette's light gray, a colored stroke keeps
+ its hue, and transparency is untouched.
+*/
+void tst_qetpalette::invertedLightnessKeepsHueAndAlpha()
+{
+ const QImage black = QET::Palette::invertedLightness(strokeSquare(Qt::black));
+ QCOMPARE(black.pixelColor(6, 20).alpha(), 255);
+ QVERIFY2(black.pixelColor(6, 20).lightnessF() > 0.8, "black ink did not become light");
+ QCOMPARE(black.pixelColor(20, 20).alpha(), 0);
+
+ const QImage red = QET::Palette::invertedLightness(strokeSquare(Qt::red));
+ const QColor stroke = red.pixelColor(6, 20);
+ QVERIFY2(qAbs(stroke.hslHueF() - QColor(Qt::red).hslHueF()) < 0.02, "hue changed");
+ QVERIFY(stroke.hslSaturationF() > 0.9);
+}
+
+/**
+ An element preview drawn for the white sheet must read at 3:1 on the
+ Base color of both palettes: unchanged on the light one, inverted on
+ the dark one.
+*/
+void tst_qetpalette::elementPreviewReadsOnBothPalettes()
+{
+ const QPixmap preview = QPixmap::fromImage(strokeSquare(Qt::black));
+ for (const QPalette &palette : {QET::Palette::fusionLight(), QET::Palette::fusionDark()})
+ {
+ const QColor base = palette.color(QPalette::Active, QPalette::Base);
+ const QPixmap shown = QET::Palette::forPalette(preview, palette);
+ QImage row(shown.size(), QImage::Format_ARGB32);
+ row.fill(base);
+ QPainter painter(&row);
+ painter.drawPixmap(0, 0, shown);
+ painter.end();
+ const double contrast = QET::Test::inkContrast(row, row.rect());
+ QVERIFY2(contrast >= 3.0, qPrintable(QString("preview reads %1:1 on Base %2").arg(contrast).arg(base.name())));
+ }
+ // A light palette hands the picture back untouched.
+ QCOMPARE(QET::Palette::forPalette(preview, QET::Palette::fusionLight()).cacheKey(), preview.cacheKey());
+}
+
+/**
+ In a tree on the dark palette, the delegate inverts a line-art icon so
+ it reads on the row, and leaves a colored icon (a folder) as it is.
+*/
+void tst_qetpalette::previewDelegateAdaptsLineArtOnly()
+{
+ QApplication::setStyle(QStyleFactory::create("Fusion"));
+ QApplication::setPalette(QET::Palette::fusionDark());
+
+ QStandardItemModel model;
+ auto *element = new QStandardItem(QIcon(QPixmap::fromImage(strokeSquare(Qt::black))), "element");
+ auto *folder = new QStandardItem(QIcon(QPixmap::fromImage(strokeSquare(QColor(30, 96, 176)))), "folder");
+ model.appendRow(element);
+ model.appendRow(folder);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.setIconSize(QSize(40, 40));
+ view.setItemDelegate(new ElementPreviewDelegate(&view));
+ view.resize(300, 200);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ const QImage image = view.viewport()->grab().toImage();
+ const QRect element_icon(view.visualRect(element->index()).topLeft(), QSize(40, 40));
+ const QRect folder_icon(view.visualRect(folder->index()).topLeft(), QSize(40, 40));
+ const QColor base = QET::Palette::fusionDark().color(QPalette::Active, QPalette::Base);
+ QCOMPARE(QColor(dominant(image)), base);
+
+ const double element_contrast = QET::Test::inkContrast(image, element_icon);
+ QVERIFY2(element_contrast >= 3.0, qPrintable(QString("element preview reads %1:1 on the dark row").arg(element_contrast)));
+
+ // The folder icon keeps its blue: some pixel in its slot is still saturated blue.
+ bool blue = false;
+ for (int y = folder_icon.top(); y <= folder_icon.bottom() && !blue; ++y)
+ for (int x = folder_icon.left(); x <= folder_icon.right() && !blue; ++x)
+ {
+ const QColor c = image.pixelColor(x, y);
+ blue = c.hslSaturationF() > 0.5 && c.blue() > c.red() + 60;
+ }
+ QVERIFY2(blue, "the colored icon lost its color");
+}
+
int main(int argc, char **argv)
{
// Widgets are painted for real, on Qt's offscreen platform so the test