mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 07:14:13 +02:00
Merge pull request #946 from jp2images/feature-panels-follow-palette
Let the Collections and Projects panels follow the palette
This commit is contained in:
@@ -436,6 +436,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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "elementpreviewdelegate.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef ELEMENTPREVIEWDELEGATE_H
|
||||
#define ELEMENTPREVIEWDELEGATE_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QIcon>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
/**
|
||||
@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<qint64, QIcon> m_dark_icons;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include "qetpalette.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QStyle>
|
||||
#include <cmath>
|
||||
|
||||
@@ -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<const QRgb *>(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<const QRgb *>(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<QRgb *>(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;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#ifndef QET_PALETTE_H
|
||||
#define QET_PALETTE_H
|
||||
|
||||
#include <QImage>
|
||||
#include <QPalette>
|
||||
#include <QPixmap>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -24,11 +24,15 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QPushButton>
|
||||
#include <QPainter>
|
||||
#include <QRadioButton>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTreeView>
|
||||
#include <QStyleFactory>
|
||||
#include <QToolBar>
|
||||
|
||||
#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<QRgb, int> 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
|
||||
|
||||
Reference in New Issue
Block a user