Files
qelectrotech-source-mirror/tests/qttest/inkcontrast.h
T
Jeff Patterson 4e43cde063 Give icons a hover state through a proxy style
Hovering a tool button changed only its frame, and on the dark palette
Fusion's hover frame is too faint to notice, so nothing told the user
which button was under the pointer (GitHub #870, PR 3 of the plan).

QETStyle wraps the running style. For QIcon::Active it returns line-art
icons tinted in the palette's highlight color, lightened until the tint
reads at 3:1 on the Light role, which is the top of Fusion's hover
gradient. Colored icons keep their colors, using the same line-art rule
as misc/make_icon_themes.py. Every other mode goes to the base style.

Fusion also asks for QIcon::Active for the icon of a highlighted menu
item and paints it on the highlight bar, where the tint would vanish.
The generated pixmap is cached per icon and cannot tell a menu from a
toolbar, so drawControl(CE_MenuItem) hands the base style an icon whose
Active pixmap is in the HighlightedText color instead.

QETApp::initStyle() installs the proxy on every platform, keeping the
base style's object name so the Fusion checks still match.

tests/qttest/tst_qeticons: hovering changes the icon ink to the tint and
stays at 3:1 on both palettes, on a raised button and on a checked one
drawn sunken, where the frame gives no hover cue at all; a colored icon is unchanged on hover; a
highlighted menu item's icon reads at 3:1 on the highlight bar.
inkcontrast.h gains background() and ink() helpers for those checks.
2026-09-19 12:26:06 -05:00

104 lines
3.2 KiB
C++

/*
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 QET_TEST_INKCONTRAST_H
#define QET_TEST_INKCONTRAST_H
#include <QHash>
#include <QImage>
#include <QRect>
#include <QString>
#include <QTest>
#include <QWidget>
#include "qetpalette.h"
/**
Shared helpers for the rendering tests: paint a widget on the offscreen
platform and measure the ink against the background.
*/
namespace QET {
namespace Test {
/**
Grab a widget for measuring. With QET_TEST_DUMP_DIR set, the
image is also written there as <row>-<name>.png so a failure
can be looked at.
*/
inline QImage dump(const QImage &image, const char *name)
{
const QByteArray dir = qgetenv("QET_TEST_DUMP_DIR");
if (!dir.isEmpty())
image.save(QString("%1/%2-%3.png").arg(QString::fromLocal8Bit(dir),
QTest::currentDataTag(), name));
return image;
}
inline QImage grab(QWidget *widget, const char *name)
{
return dump(widget->grab().toImage(), name);
}
/**
Contrast between the background of a rendered widget (its most
frequent color) and the ink drawn on it (the pixel whose
luminance differs most from the background), inside rect.
*/
inline QHash<QRgb, int> histogram(const QImage &image, const QRect &rect)
{
QHash<QRgb, int> counts;
for (int y = rect.top(); y <= rect.bottom(); ++y)
for (int x = rect.left(); x <= rect.right(); ++x)
++counts[image.pixel(x, y)];
return counts;
}
/// The most frequent color inside rect.
inline QRgb background(const QImage &image, const QRect &rect)
{
const QHash<QRgb, int> counts = histogram(image, rect);
QRgb color = 0;
int best = -1;
for (auto it = counts.cbegin(); it != counts.cend(); ++it)
if (it.value() > best) { best = it.value(); color = it.key(); }
return color;
}
/// The color inside rect that contrasts most with the background: the ink.
inline QRgb ink(const QImage &image, const QRect &rect)
{
const QHash<QRgb, int> counts = histogram(image, rect);
const QColor bg(background(image, rect));
QRgb color = bg.rgb();
double contrast = 1.0;
for (auto it = counts.cbegin(); it != counts.cend(); ++it)
{
const double c = QET::Palette::contrastRatio(bg, QColor(it.key()));
if (c > contrast) { contrast = c; color = it.key(); }
}
return color;
}
inline double inkContrast(const QImage &image, const QRect &rect)
{
return QET::Palette::contrastRatio(QColor(background(image, rect)),
QColor(ink(image, rect)));
}
}
}
#endif