mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 07:14:13 +02:00
Let the Collections and Projects panels follow the palette
Both panels forced a light palette on themselves (white rows, black text, their own selection blue) so that element previews, which are black line art drawn for the white sheet, would stay visible on a dark desktop (bugtracker 335). On a dark palette the two docks were the only white windows left. The forced palettes are gone. Element previews are now kept as drawn, on a transparent background, and adapted where they are shown: ElementPreviewDelegate, installed on the collection tree, hands the view a copy with its lightness inverted when the palette is dark (QET::Palette::forPalette), so black ink becomes the palette's light gray while colored icons such as folders stay as they are; the drag pixmap is adapted the same way. A light palette shows the previews untouched. This fixes bugtracker 335 on every dark desktop rather than masking it with a white panel. The preview cache stored the old white-sheet pictures; it records the format now and drops a cache written before this change once. The amber "show this directory" highlight sets black text so it reads on both palettes. The Projects panel only shows icons from the icon theme, which has a dark variant, so nothing else changes there. tests/qttest/tst_qetpalette: the line-art rule tells ink from color; inversion keeps hue and alpha; a preview reads at 3:1 on the Base color of both palettes; in a tree on the dark palette the delegate inverts a line-art icon and leaves a colored one alone. Fixes #945.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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