Perceptual Color

helper.cpp
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4// Own header
5#include "helper.h"
6
7#include "absolutecolor.h"
8#include "genericcolor.h"
9#include "helperconversion.h"
10#include "initializelibraryresources.h"
11#include "rgbcolorspace.h"
12#include <array>
13#include <qchar.h>
14#include <qcolor.h>
15#include <qevent.h>
16#include <qkeysequence.h>
17#include <qpainter.h>
18#include <qpixmap.h>
19#include <qpoint.h>
20#include <qsize.h>
21#include <qstringliteral.h>
22#include <qstyle.h>
23#include <qstyleoption.h>
24#include <qwidget.h>
25
26#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
27#include <qlist.h>
28#else
29#include <qstringlist.h>
30#endif
31
32#ifndef PERCEPTUALCOLORINTERNAL
33#include <type_traits>
34#include <utility>
35#endif
36
37namespace PerceptualColor
38{
39/** @internal
40 *
41 * @brief Number of vertical <em>standard</em> wheel steps done by a
42 * wheel event
43 *
44 * As the QWheelEvent documentation explains, there is a common physical
45 * standard wheel step size for mouse wheels: 15°. But there are some
46 * mouse models which use non-standard physical wheel step sizes for
47 * their mouse wheel, for example because they have a higher wheel
48 * resolution.
49 *
50 * This function converts the values in a QMouseEvent to the
51 * <em>standard</em> wheel step count.
52 *
53 * @param event the QWheelEvent
54 * @returns the count of vertical <em>standard</em> wheel steps done
55 * within this mouse event. The value is positive for up-steps and
56 * negative for down-steps. On a standard mouse wheel, moving the wheel
57 * one physical step up will return the value 1. On a non-standard,
58 * higher resolution mouse wheel, moving the wheel one physical step up
59 * will return a smaller value, for example 0.7 */
60qreal standardWheelStepCount(QWheelEvent *event)
61{
62 // QWheelEvent::angleDelta() returns 8 units for each degree.
63 // The standard wheel step is 15°. So on a standard
64 // mouse, one wheel step results in (8 × 15) units.
65 return event->angleDelta().y() / static_cast<qreal>(8 * 15);
66}
67
68/** @internal
69 *
70 * @brief Background for semi-transparent colors.
71 *
72 * When showing a semi-transparent color, there has to be a background
73 * on which it is shown. This function provides a suitable background
74 * for showcasing a color.
75 *
76 * @param devicePixelRatioF The desired device-pixel ratio. Must be ≥ 1.
77 *
78 * @returns An image of a mosaic of neutral gray rectangles of different
79 * lightness. You can use this as tiles to paint a background, starting from
80 * the top-left corner. This image is made for LTR layouts. If you have an
81 * RTL layout, you should horizontally mirror your paint buffer after painting
82 * the tiles. The image has its device pixel ratio set to the value that was
83 * given in the parameter.
84 *
85 * @note The image is considering the given device-pixel ratio to deliver
86 * sharp (and correctly scaled) images also for HiDPI devices.
87 * The painting does not use floating point drawing, but rounds
88 * to full integers. Therefore, the result is always a sharp image.
89 * This function takes care that each square has the same pixel size,
90 * without scaling errors or anti-aliasing errors.
91 *
92 * @sa @ref AbstractDiagram::transparencyBackground()
93 *
94 * @todo Provide color management support! Currently, we use the same
95 * value for red, green and blue, this might <em>not</em> be perfectly
96 * neutral gray depending on the color profile of the monitor… And: We
97 * should make sure that transparent colors are not applied by Qt on top
98 * of this image. Instead, add a parameter to this function to get the
99 * transparent color to paint above, and do color-managed overlay of the
100 * transparent color, in Lch space. For each Lab (not Lch!) channel:
101 * result = opacity * foreground + (100% - opacity) * background. */
102QImage transparencyBackground(qreal devicePixelRatioF)
103{
104 // The valid lightness range is [0, 255]. The median is 127/128.
105 // We use two color with equal distance to this median to get a
106 // neutral gray.
107 constexpr int lightnessDistance = 15;
108 constexpr int lightnessOne = 127 - lightnessDistance;
109 constexpr int lightnessTwo = 128 + lightnessDistance;
110 constexpr int squareSizeInLogicalPixel = 10;
111 const int squareSize = qRound(squareSizeInLogicalPixel * devicePixelRatioF);
112
113 QImage temp(squareSize * 2, squareSize * 2, QImage::Format_RGB32);
114 temp.fill(QColor(lightnessOne, lightnessOne, lightnessOne));
115 QPainter painter(&temp);
116 QColor foregroundColor(lightnessTwo, lightnessTwo, lightnessTwo);
117 painter.fillRect(0, 0, squareSize, squareSize, foregroundColor);
118 painter.fillRect(squareSize, squareSize, squareSize, squareSize, foregroundColor);
119 temp.setDevicePixelRatio(devicePixelRatioF);
120 return temp;
121}
122
123/** @internal
124 *
125 * @brief Draws a QWidget respecting Qt Style Sheets.
126 *
127 * When subclassing QWidget-derived classes, the Qt Style Sheets are
128 * considered automatically. But when subclassing QWidget itself, the
129 * Qt Style Sheets are <em>not</em> considered automatically. Also,
130 * calling <tt>QWidget::paintEvent()</tt> from the subclass’s paint
131 * event does not help. Instead, call this function from within your
132 * subclass’s paint event. It uses some special code as documented
133 * in the <em>Qt Style Sheets Reference</em> in the section about QWidget.
134 *
135 * @warning This function creates a QPainter for the widget. As there
136 * should be not more than one QPainter at the same time for a given
137 * paint device, you may not call this function while a QPainter
138 * exists for the widget. Therefore, it is best to call this
139 * function as very first statement in your paintEvent() implementation,
140 * before initializing any QPainter.
141 *
142 * @param widget the widget */
143void drawQWidgetStyleSheetAware(QWidget *widget)
144{
145 QStyleOption opt;
146 opt.initFrom(widget);
147 QPainter p(widget);
148 widget->style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, widget);
149}
150
151/** @internal
152 *
153 * @brief Provides prefix and suffix of a value from a given format string.
154 *
155 * A typical use case: You want to put a percent value into a spinbox. The
156 * simple approach would be:
157 * @snippet testhelper.cpp percentTraditional
158 * It could be improved like this:
159 * @snippet testhelper.cpp percentImproved
160 * However, in some languages, the position of the prefix and suffix may be
161 * reversed compared to English. Example: In English, you write 50\%, but in
162 * Turkish, you write \%50. Qt does not offer an out-of-the-box solution for
163 * this. This helper now provides complete internationalization for prefixes
164 * and suffixes of spin boxes, allowing to do this easily in a fail-safe way:
165 * @snippet testhelper.cpp percentFullyInternationalized
166 *
167 * @param formatString A translated string in the format "prefix%1suffix". It
168 * should contain exactly <em>one</em> place marker as described in
169 * <tt>QString::arg()</tt> like <tt>\%1</tt> or <tt>\%L2</tt>. This place
170 * marker represents the value. Example: “Prefix\%1Suffix”. Prefix and suffix
171 * may be empty.
172 *
173 * @returns If the <tt>formatString</tt> parameter has the correct format,
174 * the prefix will be returned at <tt>QPair::first</tt> and the suffix will
175 * be returned at <tt>QPair::second</tt>. Otherwise, they will be set to an
176 * empty string. */
177[[nodiscard]] QPair<QString, QString> getPrefixSuffix(const QString &formatString)
178{
179 // QString::arg() support for %L2, %5 etc which translators might expect:
180 const auto list = formatString //
181 .arg(QStringLiteral("%1")) //
182 .split(QStringLiteral("%1"));
183 if (list.count() == 2) {
184 return QPair<QString, QString>(list.at(0), list.at(1));
185 }
186 return QPair<QString, QString>(QString(), QString());
187}
188
189/** @internal
190 *
191 * @brief Icon from theme.
192 *
193 * @param names List of names, preferred names first. The system’s icon
194 * themes are searched for this.
195 * @param fallback If the system icon themes do not provide an icon, use
196 * this fallback icon from the built-in resources.
197 * @param type Type of widget color scheme for which the fallback icon (if
198 * used) should be suitable.
199 *
200 * @returns An icon from the system icons and or a fallback icons. If none is
201 * available, an empty icon. */
202QIcon qIconFromTheme(const QStringList &names, const QString &fallback, ColorSchemeType type)
203{
204#ifdef PERCEPTUALCOLORINTERNAL
205 Q_UNUSED(names)
206#else
207 // Try to find icon in theme
208 for (auto const &name : std::as_const(names)) {
209 const QIcon myIcon = QIcon::fromTheme(name);
210 if (!myIcon.isNull()) {
211 return myIcon;
212 }
213 }
214#endif
215
216 // Return fallback icon
217 initializeLibraryResources();
218 QString path = QStringLiteral( //
219 ":/PerceptualColor/icons/lighttheme/%1.svg");
220 if (type == ColorSchemeType::Dark) {
221 path = QStringLiteral( //
222 ":/PerceptualColor/icons/darktheme/%1.svg");
223 }
224 return QIcon(path.arg(fallback));
225}
226
227/** @internal
228 *
229 * @brief Converts text with mnemonics to rich text rendering the mnemonics.
230 *
231 * At some places in Qt, mnemonics are used. For example, setting
232 * <tt>QPushButton::setText()</tt> to "T&est" will make appear the text
233 * "Test". If mnemonic support is enabled in the current platform theme,
234 * the "e" is underlined.
235 *
236 * At some other places in Qt, rich text is used. For example, setting
237 * <tt>QWidget::setToolTip()</tt> to "T<u>e</u>st" will make appear the text
238 * "Test", but with the "e" underlined.
239 *
240 * @param mnemonicText A text that might contain mnemonics
241 *
242 * @returns A rich text that will render in rich-text-functions with the same
243 * rendering as if the mnemonic text would have been
244 * used in mnemonic-functions: If currently in the platform theme,
245 * auto-mnemonics are enabled, the mnemonics are underlined. Otherwise,
246 * the mnemonics are not underlined nor is the “&” character visible
247 *
248 * @note This function mimics Qt’s algorithm form mnemonic rendering quite
249 * well, but there might be subtile differences in corner cases. Like Qt,
250 * this function accepts multiple occurrences of "&" in the same string, even
251 * before different characters, and underlines all of them, though
252 * <tt>QKeySequence::mnemonic()</tt> will return only one of them as
253 * shortcut. */
254QString fromMnemonicToRichText(const QString &mnemonicText)
255{
256 QString result;
257
258 const bool doUnderline = !QKeySequence::mnemonic(mnemonicText).isEmpty();
259 const auto underlineStart = doUnderline ? QStringLiteral("<u>") : QString();
260 const auto underlineStop = doUnderline ? QStringLiteral("</u>") : QString();
261
262 bool underlineNextChar = false;
263 for (int i = 0; i < mnemonicText.length(); ++i) {
264 if (mnemonicText[i] == QStringLiteral("&")) {
265 const auto nextChar = //
266 (i + 1 < mnemonicText.length()) //
267 ? mnemonicText[i + 1]
268 : QChar();
269 if (nextChar == QStringLiteral("&")) {
270 // Double ampersand: Escape the "&"
271 result.append(QStringLiteral("&"));
272 i++; // Skip the second "&"
273 } else {
274 // Single ampersand: Start underline
275 underlineNextChar = true;
276 }
277 } else {
278 if (underlineNextChar) {
279 // End underline
280 result.append(underlineStart);
281 result.append(mnemonicText[i]);
282 result.append(underlineStop);
283 underlineNextChar = false;
284 } else {
285 result.append(mnemonicText[i]);
286 }
287 }
288 }
289
290 return result;
291}
292
293/** @internal
294 *
295 * @brief Guess the actual @ref ColorSchemeType of a given widget.
296 *
297 * It guesses the color scheme type actually used by the current widget style,
298 * and not the type of the current color palette. This makes a difference
299 * for example on the Windows Vista style, which might ignore the palette and
300 * use always a light theme instead.
301 *
302 * @param widget The widget to evaluate
303 *
304 * @returns The guessed schema, or <tt>std::nullopt</tt> if
305 * nothing could be guessed.
306 *
307 * @note The exact implementation of the guess might change over time.
308 *
309 * @internal
310 *
311 * The current implementation takes a screenshot of the widget and calculates
312 * the average lightness of this screenshot and determines the color schema
313 * type accordingly. It returns <tt>std::nullopt</tt> if the widget
314 * is <tt>nullptr</tt> or its size is empty.
315 *
316 * @note With Qt 6.5, there is
317 * <a href="https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5">
318 * better access to color themes</a>. Apparently, the Windows Vista style
319 * now seems to polish the widgets by setting a light color palette, so
320 * also on Windows Vista style we could simply rely on the color palette
321 * and test if the text color is lighter or darker than the background color
322 * to determine the color scheme type. This would also give us more reliable
323 * results with color schemes that have background colors around 50% lightness,
324 * which our currently implementation has problems to get right. But on
325 * the other hand, other styles like Kvantum might still chose to ignore
326 * the color palette, so it seems safer to stay with the current
327 * implementation. */
328std::optional<ColorSchemeType> guessColorSchemeTypeFromWidget(QWidget *widget)
329{
330 if (widget == nullptr) {
331 return std::nullopt;
332 }
333
334 // Take a screenshot of the widget
335 const QImage screenshot = widget->grab().toImage();
336 if (screenshot.size().isEmpty()) {
337 return std::nullopt;
338 }
339
340 // Calculate the average lightness of the screenshot
341 QColorFloatType lightnessSum = 0;
342 for (int y = 0; y < screenshot.height(); ++y) {
343 for (int x = 0; x < screenshot.width(); ++x) {
344 lightnessSum += QColor(screenshot.pixel(x, y)).lightnessF();
345 }
346 }
347 const auto pixelCount = screenshot.width() * screenshot.height();
348 constexpr QColorFloatType threeshold = 0.5;
349 const bool isDark = //
350 (lightnessSum / static_cast<QColorFloatType>(pixelCount)) < threeshold;
351 if (isDark) {
353 }
355}
356
357/** @brief Swatch grid derived from the basic colors as by WCS (World color
358 * survey).
359 *
360 * The swatch grid contains various tints and shades of the
361 * basic colors. The choice of the basic colors is based on the
362 * <a href="https://en.wikipedia.org/wiki/Basic_Color_Terms:_Their_Universality_and_Evolution">
363 * study by Brent Berlin and Paul Kay</a>, who suggest that the
364 * basic color terms in almost all languages on earth follow a universal
365 * pattern. They propose that there are eleven basic color terms that appear
366 * in this order during the evolution of a language:
367 *
368 * 1. black, white
369 * 2. red
370 * 3. green, yellow
371 * 4. blue
372 * 5. brown
373 * 6. purple, pink, orange, gray
374 *
375 * Additionally, people worldwide seem to agree quite well on the typical
376 * values of each of these color terms. This theory is a fascinating one
377 * and forms a good basis for choosing basic colors for this swatch grid.
378 *
379 * This widget's colors have been arranged largely according to the color
380 * wheel of the perceptually uniform color space. We start with the saturated
381 * basic colors: red, orange, yellow, green, blue, and purple in order of
382 * their hue angles. Next, we have pink and brown, which have roughly the
383 * same hue as red or orange but are less saturated. These are simply the
384 * less chromatic parts of this hue but are nevertheless perceived by humans as
385 * independent colors. For each of these basic colors, there are five variants
386 * in the order of <a href="https://en.wikipedia.org/wiki/Tints_and_shades">
387 * tint, pure color, and shade</a>. Following the saturated colors and
388 * eventually the less saturated ones, the gray axis comes in last place.
389 *
390 * What exact colors are used? What exactly is a typical “red” or a
391 * “green”? Doesn’t every human have a slightly different
392 * feeling what a “typical” red or a “typical” blue is? We
393 * need a <em>focal color</em>, which is, according to
394 * <a href="https://www.oxfordreference.com/display/10.1093/oi/authority.20110803095825870">
395 * Oxford Reference</a>:
396 *
397 * > “A colour that is a prototypical instance of a particular colour name,
398 * > such as a shade of red that a majority of viewers consider to be the
399 * > best example of a red colour.”
400 *
401 * The <a href="https://www1.icsi.berkeley.edu/wcs/">World Color Survey</a>
402 * (WCS) is a significant study about focal colors of speakers of different
403 * languages across the world. The data from this survey is available online,
404 * and while it does not provide direct values for focal colors, various
405 * studies have used this data to determine focal colors for some
406 * <em>basic color terms</em> and a naming centroid for others.
407 *
408 * The table below shows the WCS grid coordinates for the basic color terms
409 * along with the corresponding Cielab values for the focal color (where
410 * available) or the naming centroid (where focal color data is unavailable).
411 *
412 * |Basic color term|WCS grid coordinates|Cielab³ L|Cielab³ a|Cielab³ b|
413 * | :--------------| -----------------: | ------: | ------: | ------: |
414 * | white¹ | A0 | 96.00 | -0.06 | 0.06 |
415 * | black¹ | J0 | 15.60 | -0.02 | 0.02 |
416 * | red¹ | G1 | 41.22 | 61.40 | 17.92 |
417 * | yellow¹ | C9 | 81.35 | 7.28 | 109.12 |
418 * | green¹ | F17 | 51.57 | -63.28 | 28.95 |
419 * | blue¹ | F29 | 51.57 | -3.41 | -48.08 |
420 * | brown³ | G7 | 41.22 | 17.04 | 45.95 |
421 * | purple³ | G34 | 41.22 | 33.08 | -30.50 |
422 * | pink³ | E1 | 61.70 | 49.42 | 18.23 |
423 * | orange³ | E6 | 61.70| 29.38 | 64.40 |
424 * | gray | not available | | | |
425 *
426 * ¹ Focal color as proposed by
427 * <a href="https://www.pnas.org/doi/10.1073/pnas.0503281102">Focal colors
428 * are universal after all</a>.
429 *
430 * ² Raw estimation of the naming centroid based on Fig. 4 of
431 * <a href="https://sites.socsci.uci.edu/~kjameson/ECST/Kay_Cook_WorldColorSurvey.pdf">
432 * this document</a>. (Fig. 5 would be the better choice, as it gives the
433 * focal color instead of the naming centroid, but unfortunately contains
434 * only red, yellow, green and blue, for which we have yet direct data.)
435 *
436 * ³ <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new.txt">
437 * Lookup table providing Lab values for WCS grid coordinates</a> and the
438 * <a href="https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new-README.txt">
439 * corresponding explanation</a>.
440 *
441 * From this data, the colors in our swatch grid have been derived as follows:
442 * - The gray axis has been defined manually, ignoring the WCS data. Chroma
443 * is 0. The lightness is 100% for white, 0% for black, and 75%, 50%,
444 * and 25% for the intermediate grays.
445 * - The other columns for chromatic colors use the WCS data for the swatch in
446 * the middle. Tints and shades are calculated by adding or reducing chroma
447 * and lightness within the Oklab color space. If the resulting color falls
448 * outside the color space, a nearby in-gamut color is chosen instead.
449 *
450 * @param colorSpace The color space in which the return value is calculated.
451 *
452 * @returns Swatch grid derived from the basic colors. Provides as a list of
453 * basic colors (in this order: red, orange, yellow, green, blue, purple, pink,
454 * brown, gray axis). Each basic color is a list of 5 swatches (starting with
455 * the lightest and finishing with the darkest: 2 tints, the tone itself,
456 * 2 shades).
457 *
458 * @note The RGB value is rounded to full integers in the range [0, 255]. */
460{
461 constexpr GenericColor red{41.22, 61.40, 17.92};
462 constexpr GenericColor orange{61.70, 29.38, 64.40};
463 constexpr GenericColor yellow{81.35, 07.28, 109.12};
464 constexpr GenericColor green{51.57, -63.28, 28.95};
465 constexpr GenericColor blue{51.57, -03.41, -48.08};
466 constexpr GenericColor purple{41.22, 33.08, -30.50};
467 constexpr GenericColor pink{61.70, 49.42, 18.23};
468 constexpr GenericColor brown{41.22, 17.04, 45.95};
469 constexpr std::array<GenericColor, 8> chromaticCielabColors //
470 {{red, orange, yellow, green, blue, purple, pink, brown}};
471
472 // Lowest common denominator of QList‘s and std::array’s size types:
473 using MySizeType = quint8;
474
475 constexpr MySizeType columnCount = //
476 chromaticCielabColors.size() + 1; // + 1 for gray axis
477 constexpr auto rowCount = 5;
478 Swatches wcsSwatches{columnCount, rowCount};
479
480 // Chromatic colors
481 constexpr double strongTint = 0.46;
482 constexpr double weakTint = 0.23;
483 constexpr double weakShade = 0.18;
484 constexpr double strongShade = 0.36;
485 std::array<GenericColor, rowCount> tintsAndShades;
486 for (MySizeType i = 0; i < chromaticCielabColors.size(); ++i) { //
487 const auto oklch = AbsoluteColor::convert( //
489 chromaticCielabColors.at(i),
491 )
492 .value_or(GenericColor());
493 tintsAndShades[0] = GenericColor //
494 {oklch.first + (1 - oklch.first) * strongTint, //
495 oklch.second * (1 - strongTint), //
496 oklch.third};
497 tintsAndShades[1] = GenericColor //
498 {oklch.first + (1 - oklch.first) * weakTint, //
499 oklch.second * (1 - weakTint), //
500 oklch.third};
501 tintsAndShades[2] = oklch;
502 tintsAndShades[3] = GenericColor //
503 {oklch.first * (1 - weakShade), //
504 oklch.second * (1 - weakShade), //
505 oklch.third};
506 tintsAndShades[4] = GenericColor //
507 {oklch.first * (1 - strongShade), //
508 oklch.second * (1 - strongShade), //
509 oklch.third};
510 for (MySizeType j = 0; j < rowCount; ++j) {
511 const auto variationCielchD50 = AbsoluteColor::convert( //
513 tintsAndShades.at(j), //
515 )
516 .value_or(GenericColor());
517 const auto variationRgb = colorSpace->fromCielchD50ToQRgbBound( //
518 variationCielchD50);
519 wcsSwatches.setValue(i, //
520 j,
521 variationRgb);
522 }
523 }
524
525 // Gray axis
526 QList<double> lightnesses{1, 0.75, 0.5, 0.25, 0};
527 for (int j = 0; j < lightnesses.count(); ++j) {
528 const GenericColor myOklab{lightnesses.at(j), 0, 0};
529 const auto cielchD50 = AbsoluteColor::convert( //
531 myOklab, //
533 )
534 .value_or(GenericColor());
535 const auto rgb = colorSpace->fromCielchD50ToQRgbBound(cielchD50);
536 wcsSwatches.setValue(columnCount - 1, j, rgb);
537 }
538
539 return wcsSwatches;
540}
541
542} // namespace PerceptualColor
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
The namespace of this library.
Array2D< QColor > Swatches
Swatches organized in a grid.
Definition helper.h:242
ColorSchemeType
Represents the appearance of a theme.
Definition helper.h:39
Swatches wcsBasicColors(const QSharedPointer< PerceptualColor::RgbColorSpace > &colorSpace)
Swatch grid derived from the basic colors as by WCS (World color survey).
Definition helper.cpp:459
@ OklabD65
The Oklab color space, which by definition always and exclusively uses a D65 illuminant.
@ CielabD50
The Cielab color space using a D50 illuminant.
@ OklchD65
The Oklch color space, which by definition always and exclusively uses a D65 illuminant.
@ CielchD50
The Cielch color space using a D50 illuminant.
float lightnessF() const const
QIcon fromTheme(const QString &name)
bool isNull() const const
int height() const const
QRgb pixel(const QPoint &position) const const
QSize size() const const
int width() const const
bool isEmpty() const const
QKeySequence mnemonic(const QString &text)
const_reference at(qsizetype i) const const
qsizetype count() const const
QImage toImage() const const
bool isEmpty() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
qsizetype length() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
void initFrom(const QWidget *widget)
QPixmap grab(const QRect &rectangle)
QStyle * style() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:18:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.