Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindows11style.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <qstylehints.h>
7#include <private/qstyleanimation_p.h>
8#include <private/qstyle_p.h>
9#include <private/qstylehelper_p.h>
10#include <private/qapplication_p.h>
11#include <private/qcombobox_p.h>
12#include <qstyleoption.h>
13#include <qpainter.h>
14#include <qpainterstateguard.h>
15#include <QGraphicsDropShadowEffect>
16#include <QLatin1StringView>
17#include <QtWidgets/qcombobox.h>
18#if QT_CONFIG(commandlinkbutton)
19#include <QtWidgets/qcommandlinkbutton.h>
20#endif
21#include <QtWidgets/qgraphicsview.h>
22#include <QtWidgets/qlistview.h>
23#include <QtWidgets/qmenu.h>
24#if QT_CONFIG(mdiarea)
25#include <QtWidgets/qmdiarea.h>
26#endif
27#include <QtWidgets/qtextedit.h>
28#include <QtWidgets/qtreeview.h>
29#if QT_CONFIG(datetimeedit)
30# include <QtWidgets/qdatetimeedit.h>
31#endif
32#if QT_CONFIG(tabwidget)
33# include <QtWidgets/qtabwidget.h>
34#endif
35#if QT_CONFIG(menubar)
36# include <QtWidgets/qmenubar.h>
37#endif
38#include "qdrawutil.h"
39#include <chrono>
40
41QT_BEGIN_NAMESPACE
42
43using namespace Qt::StringLiterals;
44
45static constexpr int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners
46static constexpr int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners
47
49{
50inline bool isChecked(const QStyleOption *option)
51{
52 return option->state.testAnyFlags(QStyle::State_On | QStyle::State_NoChange);
53}
54inline bool isDisabled(const QStyleOption *option)
55{
56 return !option->state.testFlag(QStyle::State_Enabled);
57}
58inline bool isPressed(const QStyleOption *option)
59{
60 return option->state.testFlag(QStyle::State_Sunken);
61}
62inline bool isHover(const QStyleOption *option)
63{
64 return option->state.testFlag(QStyle::State_MouseOver);
65}
66inline bool isAutoRaise(const QStyleOption *option)
67{
68 return option->state.testFlag(QStyle::State_AutoRaise);
69}
71inline ControlState calcControlState(const QStyleOption *option)
72{
73 if (isDisabled(option))
75 if (isPressed(option))
77 if (isHover(option))
80};
81
82} // namespace StyleOptionHelper
83
84#define AcceptMedium u"\uF78C"_s
85// QStringLiteral(u"\uE73C")
86#define Dash12 u"\uE629"_s
87#define CheckMark u"\uE73E"_s
88
89#define CaretLeftSolid8 u"\uEDD9"_s
90#define CaretRightSolid8 u"\uEDDA"_s
91#define CaretUpSolid8 u"\uEDDB"_s
92#define CaretDownSolid8 u"\uEDDC"_s
93
94#define ChevronDown u"\uE70D"_s
95#define ChevronUp u"\uE70E"_s
96
97#define ChevronDownMed u"\uE972"_s
98#define ChevronLeftMed u"\uE973"_s
99#define ChevronRightMed u"\uE974"_s
100
101#define ChevronUpSmall u"\uE96D"_s
102#define ChevronDownSmall u"\uE96E"_s
103
104#define ChromeMinimize u"\uE921"_s
105#define ChromeMaximize u"\uE922"_s
106#define ChromeRestore u"\uE923"_s
107#define ChromeClose u"\uE8BB"_s
108
109#define Help u"\uE897"_s
110
111template <typename R, typename P, typename B>
112static inline void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush)
113{
114 p->setPen(pen);
115 p->setBrush(brush);
116 p->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
117}
118
119static constexpr int percentToAlpha(double percent)
120{
121 return qRound(percent * 255. / 100.);
122}
123
124static constexpr std::array<QColor, 32> WINUI3ColorsLight {
125 QColor(0x00,0x00,0x00,0x09), //subtleHighlightColor
126 QColor(0x00,0x00,0x00,0x06), //subtlePressedColor
127 QColor(0x00,0x00,0x00,0x0F), //frameColorLight
128 QColor(0x00,0x00,0x00,percentToAlpha(60.63)), //frameColorStrong
129 QColor(0x00,0x00,0x00,percentToAlpha(21.69)), //frameColorStrongDisabled
130 QColor(0x00,0x00,0x00,0x72), //controlStrongFill
131 QColor(0x00,0x00,0x00,0x29), //controlStrokeSecondary
132 QColor(0x00,0x00,0x00,0x14), //controlStrokePrimary
133 QColor(0xFF,0xFF,0xFF,0xFF), //menuPanelFill
134 QColor(0x00,0x00,0x00,0x66), //controlStrokeOnAccentSecondary
135 QColor(0xFF,0xFF,0xFF,0xFF), //controlFillSolid
136 QColor(0x75,0x75,0x75,0x66), //surfaceStroke
137 QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameInnerStroke
138 QColor(0x00,0x00,0x00,0xFF), //focusFrameOuterStroke
139 QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // fillControlDefault
140 QColor(0xF9,0xF9,0xF9,percentToAlpha(50)), // fillControlSecondary
141 QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlTertiary
142 QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlDisabled
143 QColor(0x00,0x00,0x00,percentToAlpha(2.41)), // fillControlAltSecondary
144 QColor(0x00,0x00,0x00,percentToAlpha(5.78)), // fillControlAltTertiary
145 QColor(0x00,0x00,0x00,percentToAlpha(9.24)), // fillControlAltQuarternary
146 QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // fillControlAltDisabled
147 QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault
148 QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary
149 QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary
150 QColor(0x00,0x00,0x00,percentToAlpha(21.69)), // fillAccentDisabled
151 QColor(0x00,0x00,0x00,percentToAlpha(89.56)), // textPrimary
152 QColor(0x00,0x00,0x00,percentToAlpha(60.63)), // textSecondary
153 QColor(0x00,0x00,0x00,percentToAlpha(36.14)), // textDisabled
154 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentPrimary
155 QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // textOnAccentSecondary
156 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentDisabled
157};
158
159static constexpr std::array<QColor, 32> WINUI3ColorsDark {
160 QColor(0xFF,0xFF,0xFF,0x0F), //subtleHighlightColor
161 QColor(0xFF,0xFF,0xFF,0x0A), //subtlePressedColor
162 QColor(0xFF,0xFF,0xFF,0x12), //frameColorLight
163 QColor(0xFF,0xFF,0xFF,percentToAlpha(60.47)), //frameColorStrong
164 QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), //frameColorStrongDisabled
165 QColor(0xFF,0xFF,0xFF,0x8B), //controlStrongFill
166 QColor(0xFF,0xFF,0xFF,0x18), //controlStrokeSecondary
167 QColor(0xFF,0xFF,0xFF,0x12), //controlStrokePrimary
168 QColor(0x0F,0x0F,0x0F,0xFF), //menuPanelFill
169 QColor(0xFF,0xFF,0xFF,0x14), //controlStrokeOnAccentSecondary
170 QColor(0x45,0x45,0x45,0xFF), //controlFillSolid
171 QColor(0x75,0x75,0x75,0x66), //surfaceStroke
172 QColor(0x00,0x00,0x00,0xFF), //focusFrameInnerStroke
173 QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameOuterStroke
174 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // fillControlDefault
175 QColor(0xFF,0xFF,0xFF,percentToAlpha(8.37)), // fillControlSecondary
176 QColor(0xFF,0xFF,0xFF,percentToAlpha(3.26)), // fillControlTertiary
177 QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlDisabled
178 QColor(0x00,0x00,0x00,percentToAlpha(10.0)), // fillControlAltDefault
179 QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlAltSecondary
180 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.98)), // fillControlAltTertiafillCy
181 QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // controlAltDisabled
182 QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault
183 QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary
184 QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary
185 QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), // fillAccentDisabled
186 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textPrimary
187 QColor(0xFF,0xFF,0xFF,percentToAlpha(78.6)), // textSecondary
188 QColor(0xFF,0xFF,0xFF,percentToAlpha(36.28)), // textDisabled
189 QColor(0x00,0x00,0x00,percentToAlpha(100)), // textOnAccentPrimary
190 QColor(0x00,0x00,0x00,percentToAlpha(70)), // textOnAccentSecondary
191 QColor(0xFF,0xFF,0xFF,percentToAlpha(53.02)), // textOnAccentDisabled
192};
193
194static constexpr std::array<std::array<QColor,32>, 2> WINUI3Colors {
195 WINUI3ColorsLight,
196 WINUI3ColorsDark
197};
198
199// Color of close Button in Titlebar (default + hover)
200static constexpr QColor shellCaptionCloseFillColorPrimary(0xC4,0x2B,0x1C,0xFF);
201static constexpr QColor shellCaptionCloseTextFillColorPrimary(0xFF,0xFF,0xFF,0xFF);
202// Color of close Button in Titlebar (pressed + disabled)
203static constexpr QColor shellCaptionCloseFillColorSecondary(0xC4,0x2B,0x1C,0xE6);
204static constexpr QColor shellCaptionCloseTextFillColorSecondary(0xFF,0xFF,0xFF,0xB3);
205
206
207#if QT_CONFIG(toolbutton)
208static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton,
209 const QRect &rect, QPainter *painter, const QWidget *widget = nullptr)
210{
211 QStyle::PrimitiveElement pe;
212 switch (toolbutton->arrowType) {
213 case Qt::LeftArrow:
214 pe = QStyle::PE_IndicatorArrowLeft;
215 break;
216 case Qt::RightArrow:
217 pe = QStyle::PE_IndicatorArrowRight;
218 break;
219 case Qt::UpArrow:
220 pe = QStyle::PE_IndicatorArrowUp;
221 break;
222 case Qt::DownArrow:
223 pe = QStyle::PE_IndicatorArrowDown;
224 break;
225 default:
226 return;
227 }
228 QStyleOption arrowOpt = *toolbutton;
229 arrowOpt.rect = rect;
230 style->drawPrimitive(pe, &arrowOpt, painter, widget);
231}
232#endif // QT_CONFIG(toolbutton)
233
235{
236 qreal radius = 7.0;
237 if (state & QStyle::State_Sunken)
238 radius = 4.0f;
239 else if (state & QStyle::State_MouseOver && !(state & QStyle::State_On))
240 radius = 7.0f;
241 else if (state & QStyle::State_MouseOver && (state & QStyle::State_On))
242 radius = 5.0f;
243 else if (state & QStyle::State_On)
244 radius = 4.0f;
245 return radius;
246}
247
248static qreal sliderInnerRadius(QStyle::State state, bool insideHandle)
249{
250 if (state & QStyle::State_Sunken)
251 return 0.29;
252 else if (insideHandle)
253 return 0.71;
254 return 0.43;
255}
256/*!
257 \class QWindows11Style
258 \brief The QWindows11Style class provides a look and feel suitable for applications on Microsoft Windows 11.
259 \since 6.6
260 \ingroup appearance
261 \inmodule QtWidgets
262 \internal
263
264 \warning This style is only available on the Windows 11 platform and above.
265
266 \sa QWindows11Style QWindowsVistaStyle, QMacStyle, QFusionStyle
267*/
268
269/*!
270 Constructs a QWindows11Style object.
271*/
272QWindows11Style::QWindows11Style() : QWindows11Style(*new QWindows11StylePrivate)
273{
274}
275
276/*!
277 \internal
278 Constructs a QWindows11Style object.
279*/
280QWindows11Style::QWindows11Style(QWindows11StylePrivate &dd) : QWindowsVistaStyle(dd)
281{
282 Q_D(QWindows11Style);
283 d->assetFont = QFont("Segoe Fluent Icons");
284 d->assetFont.setStyleStrategy(QFont::NoFontMerging);
285 highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown;
286 colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
287}
288
289/*!
290 Destructor.
291*/
292QWindows11Style::~QWindows11Style() = default;
293
294/*!
295 \internal
296 see drawPrimitive for comments on the animation support
297
298 */
299void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
300 QPainter *painter, const QWidget *widget) const
301{
302 QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func());
303
304 const auto drawTitleBarButton = [&](ComplexControl control, SubControl sc, const QString &str) {
305 using namespace StyleOptionHelper;
306 const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget);
307 if (buttonRect.isValid()) {
308 const bool hover = option->activeSubControls == sc && isHover(option);
309 if (hover)
310 painter->fillRect(buttonRect, winUI3Color(subtleHighlightColor));
311 painter->setPen(option->palette.color(QPalette::WindowText));
312 painter->drawText(buttonRect, Qt::AlignCenter, str);
313 }
314 };
315 const auto drawTitleBarCloseButton = [&](ComplexControl control, SubControl sc, const QString &str) {
316 using namespace StyleOptionHelper;
317 const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget);
318 if (buttonRect.isValid()) {
319 const auto state = (option->activeSubControls == sc) ? calcControlState(option)
320 : ControlState::Normal;
321 QPen pen;
322 switch (state) {
324 painter->fillRect(buttonRect, shellCaptionCloseFillColorPrimary);
325 pen = shellCaptionCloseTextFillColorPrimary;
326 break;
328 painter->fillRect(buttonRect, shellCaptionCloseFillColorSecondary);
329 pen = shellCaptionCloseTextFillColorSecondary;
330 break;
331 case ControlState::Disabled:
332 case ControlState::Normal:
333 pen = option->palette.color(QPalette::WindowText);
334 break;
335 }
336 painter->setPen(pen);
337 painter->drawText(buttonRect, Qt::AlignCenter, str);
338 }
339 };
340
341
342 State state = option->state;
343 SubControls sub = option->subControls;
344 State flags = option->state;
345 if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
346 flags |= State_MouseOver;
347
348 painter->save();
349 painter->setRenderHint(QPainter::Antialiasing);
350 if (d->transitionsEnabled() && option->styleObject) {
351 if (control == CC_Slider) {
352 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
353 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
354
355 QRectF thumbRect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
356 const qreal outerRadius = qMin(8.0, (slider->orientation == Qt::Horizontal ? thumbRect.height() / 2.0 : thumbRect.width() / 2.0) - 1);
357 bool isInsideHandle = option->activeSubControls == SC_SliderHandle;
358
359 bool oldIsInsideHandle = styleObject->property("_q_insidehandle").toBool();
360 State oldState = State(styleObject->property("_q_stylestate").toInt());
361 SubControls oldActiveControls = SubControls(styleObject->property("_q_stylecontrols").toInt());
362
363 QRectF oldRect = styleObject->property("_q_stylerect").toRect();
364 styleObject->setProperty("_q_insidehandle", isInsideHandle);
365 styleObject->setProperty("_q_stylestate", int(state));
366 styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls));
367 styleObject->setProperty("_q_stylerect", option->rect);
368 if (option->styleObject->property("_q_end_radius").isNull())
369 option->styleObject->setProperty("_q_end_radius", outerRadius * 0.43);
370
371 bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken)
372 || (oldIsInsideHandle != isInsideHandle)
373 || (oldActiveControls != option->activeSubControls))
374 && state & State_Enabled);
375
376 if (oldRect != option->rect) {
377 doTransition = false;
378 d->stopAnimation(styleObject);
379 styleObject->setProperty("_q_inner_radius", outerRadius * 0.43);
380 }
381
382 if (doTransition) {
383 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
384 t->setStartValue(styleObject->property("_q_inner_radius").toFloat());
385 t->setEndValue(outerRadius * sliderInnerRadius(state, isInsideHandle));
386 styleObject->setProperty("_q_end_radius", t->endValue());
387
388 t->setStartTime(d->animationTime());
389 t->setDuration(150);
390 d->startAnimation(t);
391 }
392 }
393 }
394 }
395
396 switch (control) {
397#if QT_CONFIG(spinbox)
398 case CC_SpinBox:
399 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
400 QCachedPainter cp(painter, QLatin1StringView("win11_spinbox") % HexString<uint8_t>(colorSchemeIndex),
401 sb, sb->rect.size());
402 if (cp.needsPainting()) {
403 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
404 drawRoundedRect(cp.painter(), frameRect, Qt::NoPen, option->palette.brush(QPalette::Base));
405
406 if (sb->frame && (sub & SC_SpinBoxFrame))
407 drawLineEditFrame(cp.painter(), frameRect, option);
408
409 const bool isMouseOver = state & State_MouseOver;
410 const bool hasFocus = state & State_HasFocus;
411 if (isMouseOver && !hasFocus && !highContrastTheme)
412 drawRoundedRect(cp.painter(), frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor));
413
414 const auto drawUpDown = [&](QStyle::SubControl sc) {
415 const bool isUp = sc == SC_SpinBoxUp;
416 const QRect rect = proxy()->subControlRect(CC_SpinBox, option, sc, widget);
417 if (sb->activeSubControls & sc)
418 drawRoundedRect(cp.painter(), rect.adjusted(1, 1, -1, -2), Qt::NoPen,
419 winUI3Color(subtleHighlightColor));
420
421 cp->setFont(d->assetFont);
422 cp->setPen(sb->palette.buttonText().color());
423 cp->setBrush(Qt::NoBrush);
424 cp->drawText(rect, Qt::AlignCenter, isUp ? ChevronUp : ChevronDown);
425 };
426 if (sub & SC_SpinBoxUp) drawUpDown(SC_SpinBoxUp);
427 if (sub & SC_SpinBoxDown) drawUpDown(SC_SpinBoxDown);
428 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
429 QStyleOptionFocusRect fropt;
430 fropt.QStyleOption::operator=(*option);
431 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, cp.painter(), widget);
432 }
433 }
434 }
435 break;
436#endif // QT_CONFIG(spinbox)
437#if QT_CONFIG(slider)
438 case CC_Slider:
439 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
440 const auto &slrect = slider->rect;
441 const bool isHorizontal = slider->orientation == Qt::Horizontal;
442 const QRectF handleRect(proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget));
443 const QPointF handleCenter(handleRect.center());
444
445 if (sub & SC_SliderGroove) {
446 QRectF rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
447 QRectF leftRect;
448 QRectF rightRect;
449
450 if (isHorizontal) {
451 rect = QRectF(rect.left() + 2, rect.center().y() - 2, rect.width() - 2, 4);
452 leftRect = QRectF(rect.left(), rect.top(), handleCenter.x() - rect.left(),
453 rect.height());
454 rightRect = QRectF(handleCenter.x(), rect.top(),
455 rect.width() - handleCenter.x(),
456 rect.height());
457 } else {
458 rect = QRect(rect.center().x() - 2, rect.top() + 2, 4, rect.height() - 2);
459 leftRect = QRectF(rect.left(), rect.top(), rect.width(),
460 handleCenter.y() - rect.top());
461 rightRect = QRectF(rect.left(), handleCenter.y(), rect.width(),
462 rect.height() - handleCenter.y());
463 }
464 if (slider->upsideDown)
465 qSwap(leftRect, rightRect);
466
467 painter->setPen(Qt::NoPen);
468 painter->setBrush(calculateAccentColor(option));
469 painter->drawRoundedRect(leftRect,1,1);
470 painter->setBrush(WINUI3Colors[colorSchemeIndex][controlStrongFill]);
471 painter->drawRoundedRect(rightRect,1,1);
472 }
473 if (sub & SC_SliderTickmarks) {
474 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
475 int ticks = slider->tickPosition;
476 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
477 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
478 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
479 int interval = slider->tickInterval;
480 if (interval <= 0) {
481 interval = slider->singleStep;
482 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
483 available)
484 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
485 0, available) < 3)
486 interval = slider->pageStep;
487 }
488 if (!interval)
489 interval = 1;
490 int fudge = len / 2;
491 painter->setPen(slider->palette.text().color());
492 QVarLengthArray<QLineF, 32> lines;
493 int v = slider->minimum;
494 while (v <= slider->maximum + 1) {
495 if (v == slider->maximum + 1 && interval == 1)
496 break;
497 const int v_ = qMin(v, slider->maximum);
498 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
499 int pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, v_,
500 available, slider->upsideDown);
501 pos += fudge;
502 if (isHorizontal) {
503 if (ticks & QSlider::TicksAbove) {
504 lines.append(QLineF(pos, tickOffset - 0.5,
505 pos, tickOffset - tickLength - 0.5));
506 }
507
508 if (ticks & QSlider::TicksBelow) {
509 lines.append(QLineF(pos, tickOffset + thickness + 0.5,
510 pos, tickOffset + thickness + tickLength + 0.5));
511 }
512 } else {
513 if (ticks & QSlider::TicksAbove) {
514 lines.append(QLineF(tickOffset - 0.5, pos,
515 tickOffset - tickLength - 0.5, pos));
516 }
517
518 if (ticks & QSlider::TicksBelow) {
519 lines.append(QLineF(tickOffset + thickness + 0.5, pos,
520 tickOffset + thickness + tickLength + 0.5, pos));
521 }
522 }
523 // in the case where maximum is max int
524 int nextInterval = v + interval;
525 if (nextInterval < v)
526 break;
527 v = nextInterval;
528 }
529 if (!lines.isEmpty()) {
530 painter->save();
531 painter->translate(slrect.topLeft());
532 painter->drawLines(lines.constData(), lines.size());
533 painter->restore();
534 }
535 }
536 if (sub & SC_SliderHandle) {
537 const qreal outerRadius = qMin(8.0, (isHorizontal ? handleRect.height() / 2.0 : handleRect.width() / 2.0) - 1);
538 float innerRadius = outerRadius * 0.43;
539
540 if (option->styleObject) {
541 const QNumberStyleAnimation* animation = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
542 if (animation != nullptr) {
543 innerRadius = animation->currentValue();
544 option->styleObject->setProperty("_q_inner_radius", innerRadius);
545 } else {
546 bool isInsideHandle = option->activeSubControls == SC_SliderHandle;
547 innerRadius = outerRadius * sliderInnerRadius(state, isInsideHandle);
548 }
549 }
550
551 painter->setPen(Qt::NoPen);
552 painter->setBrush(winUI3Color(controlFillSolid));
553 painter->drawEllipse(handleCenter, outerRadius, outerRadius);
554 painter->setBrush(calculateAccentColor(option));
555 painter->drawEllipse(handleCenter, innerRadius, innerRadius);
556
557 painter->setPen(winUI3Color(controlStrokeSecondary));
558 painter->setBrush(Qt::NoBrush);
559 painter->drawEllipse(handleCenter, outerRadius + 0.5, outerRadius + 0.5);
560 }
561 if (slider->state & State_HasFocus) {
562 QStyleOptionFocusRect fropt;
563 fropt.QStyleOption::operator=(*slider);
564 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
565 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
566 }
567 }
568 break;
569#endif
570#if QT_CONFIG(combobox)
571 case CC_ComboBox:
572 if (const QStyleOptionComboBox *combobox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
573 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
574 drawRoundedRect(painter, frameRect, Qt::NoPen, option->palette.brush(QPalette::Base));
575
576 if (combobox->frame)
577 drawLineEditFrame(painter, frameRect, combobox, combobox->editable);
578
579 const bool isMouseOver = state & State_MouseOver;
580 const bool hasFocus = state & State_HasFocus;
581 if (isMouseOver && !hasFocus && !highContrastTheme)
582 drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor));
583
584 if (sub & SC_ComboBoxArrow) {
585 QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget).adjusted(4, 0, -4, 1);
586 painter->setFont(d->assetFont);
587 painter->setPen(combobox->palette.text().color());
588 painter->drawText(rect, Qt::AlignCenter, ChevronDownMed);
589 }
590 if (state & State_HasFocus) {
591 drawPrimitive(PE_FrameFocusRect, option, painter, widget);
592 }
593 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
594 QStyleOptionFocusRect fropt;
595 fropt.QStyleOption::operator=(*option);
596 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
597 }
598 }
599 break;
600#endif // QT_CONFIG(combobox)
601 case CC_ScrollBar:
602 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
603 QCachedPainter cp(painter, QLatin1StringView("win11_scrollbar")
604 % HexString<uint8_t>(colorSchemeIndex)
605 % HexString<int>(scrollbar->minimum)
606 % HexString<int>(scrollbar->maximum)
607 % HexString<int>(scrollbar->sliderPosition),
608 scrollbar, scrollbar->rect.size());
609 if (cp.needsPainting()) {
610 const bool vertical = scrollbar->orientation == Qt::Vertical;
611 const bool horizontal = scrollbar->orientation == Qt::Horizontal;
612 const bool isMouseOver = state & State_MouseOver;
613 const bool isRtl = option->direction == Qt::RightToLeft;
614
615 if (isMouseOver) {
616 QRectF rect = scrollbar->rect;
617 const QPointF center = rect.center();
618 if (vertical && rect.width() > 24) {
619 rect.marginsRemoved(QMargins(0, 2, 2, 2));
620 rect.setWidth(rect.width() / 2);
621 } else if (horizontal && rect.height() > 24) {
622 rect.marginsRemoved(QMargins(2, 0, 2, 2));
623 rect.setHeight(rect.height() / 2);
624 }
625 rect.moveCenter(center);
626 cp->setBrush(scrollbar->palette.base());
627 cp->setPen(Qt::NoPen);
628 cp->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius);
629 rect = rect.marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5));
630 cp->setBrush(Qt::NoBrush);
631 cp->setPen(WINUI3Colors[colorSchemeIndex][frameColorLight]);
632 cp->drawRoundedRect(rect, topLevelRoundingRadius + 0.5, topLevelRoundingRadius + 0.5);
633 }
634 if (sub & SC_ScrollBarSlider) {
635 QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
636 const QPointF center = rect.center();
637 if (vertical)
638 rect.setWidth(isMouseOver ? rect.width() / 2 : 1);
639 else
640 rect.setHeight(isMouseOver ? rect.height() / 2 : 1);
641 rect.moveCenter(center);
642 cp->setBrush(Qt::gray);
643 cp->setPen(Qt::NoPen);
644 cp->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
645 }
646 if (sub & SC_ScrollBarAddLine) {
647 if (isMouseOver) {
648 const QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
649 QFont f = QFont(d->assetFont);
650 f.setPointSize(6);
651 cp->setFont(f);
652 cp->setPen(Qt::gray);
653 const auto str = vertical ? CaretDownSolid8
654 : (isRtl ? CaretLeftSolid8 : CaretRightSolid8);
655 cp->drawText(rect, Qt::AlignCenter, str);
656 }
657 }
658 if (sub & SC_ScrollBarSubLine) {
659 if (isMouseOver) {
660 const QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
661 QFont f = QFont(d->assetFont);
662 f.setPointSize(6);
663 cp->setFont(f);
664 cp->setPen(Qt::gray);
665 const auto str = vertical ? CaretUpSolid8
666 : (isRtl ? CaretRightSolid8 : CaretLeftSolid8);
667 cp->drawText(rect, Qt::AlignCenter, str);
668 }
669 }
670 }
671 }
672 break;
673 case CC_MdiControls:{
674 QFont buttonFont = QFont(d->assetFont);
675 buttonFont.setPointSize(8);
676 painter->setFont(buttonFont);
677 drawTitleBarCloseButton(CC_MdiControls, SC_MdiCloseButton, ChromeClose);
678 drawTitleBarButton(CC_MdiControls, SC_MdiNormalButton, ChromeRestore);
679 drawTitleBarButton(CC_MdiControls, SC_MdiMinButton, ChromeMinimize);
680 }
681 break;
682 case CC_TitleBar:
683 if (const auto* titlebar = qstyleoption_cast<const QStyleOptionTitleBar*>(option)) {
684 painter->setPen(Qt::NoPen);
685 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
686 painter->setBrush(titlebar->palette.button());
687 painter->drawRect(titlebar->rect);
688
689 // draw title
690 QRect textRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel, widget);
691 QColor textColor = titlebar->palette.color(titlebar->titleBarState & Qt::WindowActive ? QPalette::Active : QPalette::Disabled,QPalette::WindowText);
692 painter->setPen(textColor);
693 // Note workspace also does elliding but it does not use the correct font
694 QString title = painter->fontMetrics().elidedText(titlebar->text, Qt::ElideRight, textRect.width() - 14);
695 painter->drawText(textRect.adjusted(1, 1, -1, -1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter));
696
697 QFont buttonFont = QFont(d->assetFont);
698 buttonFont.setPointSize(8);
699 painter->setFont(buttonFont);
700 auto shouldDrawButton = [titlebar](SubControl sc, Qt::WindowType flag) {
701 return (titlebar->subControls & sc) && (titlebar->titleBarFlags & flag);
702 };
703
704 // min button
705 if (shouldDrawButton(SC_TitleBarMinButton, Qt::WindowMinimizeButtonHint) &&
706 !(titlebar->titleBarState & Qt::WindowMinimized)) {
707 drawTitleBarButton(CC_TitleBar, SC_TitleBarMinButton, ChromeMinimize);
708 }
709
710 // max button
711 if (shouldDrawButton(SC_TitleBarMaxButton, Qt::WindowMaximizeButtonHint) &&
712 !(titlebar->titleBarState & Qt::WindowMaximized)) {
713 drawTitleBarButton(CC_TitleBar, SC_TitleBarMaxButton, ChromeMaximize);
714 }
715
716 // close button
717 if (shouldDrawButton(SC_TitleBarCloseButton, Qt::WindowSystemMenuHint))
718 drawTitleBarCloseButton(CC_TitleBar, SC_TitleBarCloseButton, ChromeClose);
719
720 // normalize button
721 if ((titlebar->subControls & SC_TitleBarNormalButton) &&
722 (((titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) &&
723 (titlebar->titleBarState & Qt::WindowMinimized)) ||
724 ((titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) &&
725 (titlebar->titleBarState & Qt::WindowMaximized)))) {
726 drawTitleBarButton(CC_TitleBar, SC_TitleBarNormalButton, ChromeRestore);
727 }
728
729 // context help button
730 if (shouldDrawButton(SC_TitleBarContextHelpButton, Qt::WindowContextHelpButtonHint))
731 drawTitleBarButton(CC_TitleBar, SC_TitleBarContextHelpButton, Help);
732
733 // shade button
734 if (shouldDrawButton(SC_TitleBarShadeButton, Qt::WindowShadeButtonHint))
735 drawTitleBarButton(CC_TitleBar, SC_TitleBarShadeButton, ChevronUpSmall);
736
737 // unshade button
738 if (shouldDrawButton(SC_TitleBarUnshadeButton, Qt::WindowShadeButtonHint))
739 drawTitleBarButton(CC_TitleBar, SC_TitleBarUnshadeButton, ChevronDownSmall);
740
741 // window icon for system menu
742 if (shouldDrawButton(SC_TitleBarSysMenu, Qt::WindowSystemMenuHint)) {
743 const QRect iconRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarSysMenu, widget);
744 if (iconRect.isValid()) {
745 if (!titlebar->icon.isNull()) {
746 titlebar->icon.paint(painter, iconRect);
747 } else {
748 QStyleOption tool = *titlebar;
749 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, &tool, widget);
750 const auto dpr = QStyleHelper::getDpr(widget);
751 const auto icon = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget);
752 const auto pm = icon.pixmap(QSize(extent, extent), dpr);
753 proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm);
754 }
755 }
756 }
757 }
758 break;
759 default:
760 QWindowsVistaStyle::drawComplexControl(control, option, painter, widget);
761 }
762 painter->restore();
763}
764
765void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
766 QPainter *painter,
767 const QWidget *widget) const {
768 QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func());
769
770 int state = option->state;
771 painter->save();
772 painter->setRenderHint(QPainter::Antialiasing);
773 if (d->transitionsEnabled() && option->styleObject && (element == PE_IndicatorCheckBox || element == PE_IndicatorRadioButton)) {
774 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
775 int oldState = styleObject->property("_q_stylestate").toInt();
776 styleObject->setProperty("_q_stylestate", int(option->state));
777 styleObject->setProperty("_q_stylerect", option->rect);
778 bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken)
779 || ((state & State_MouseOver) != (oldState & State_MouseOver))
780 || (state & State_On) != (oldState & State_On))
781 && state & State_Enabled);
782 if (doTransition) {
783 if (element == PE_IndicatorRadioButton) {
784 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
785 t->setStartValue(styleObject->property("_q_inner_radius").toFloat());
786 t->setEndValue(radioButtonInnerRadius(state));
787 styleObject->setProperty("_q_end_radius", t->endValue());
788 t->setStartTime(d->animationTime());
789 t->setDuration(150);
790 d->startAnimation(t);
791 }
792 else if (element == PE_IndicatorCheckBox) {
793 if ((oldState & State_Off && state & State_On) || (oldState & State_NoChange && state & State_On)) {
794 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
795 t->setStartValue(0.0f);
796 t->setEndValue(1.0f);
797 t->setStartTime(d->animationTime());
798 t->setDuration(150);
799 d->startAnimation(t);
800 }
801 }
802 }
803 }
804
805 switch (element) {
806 case PE_FrameFocusRect: {
807 if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
808 if (!(fropt->state & State_KeyboardFocusChange))
809 break;
810 QRectF focusRect = option->rect;
811 focusRect = focusRect.marginsRemoved(QMarginsF(1.5,1.5,1.5,1.5));
812 painter->setPen(winUI3Color(focusFrameInnerStroke));
813 painter->drawRoundedRect(focusRect,4,4);
814
815 focusRect = focusRect.marginsAdded(QMarginsF(1.0,1.0,1.0,1.0));
816 painter->setPen(QPen(winUI3Color(focusFrameOuterStroke),1));
817 painter->drawRoundedRect(focusRect,4,4);
818 }
819 break;
820 }
821 case PE_PanelTipLabel: {
822 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5));
823 const auto pen = highContrastTheme ? option->palette.buttonText().color()
824 : winUI3Color(frameColorLight);
825 drawRoundedRect(painter, rect, pen, option->palette.toolTipBase());
826 break;
827 }
828 case PE_FrameTabWidget:
829#if QT_CONFIG(tabwidget)
830 if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
831 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5));
832 const auto pen = highContrastTheme ? frame->palette.buttonText().color()
833 : winUI3Color(frameColorLight);
834 drawRoundedRect(painter, rect, pen, frame->palette.base());
835 }
836#endif // QT_CONFIG(tabwidget)
837 break;
838 case PE_FrameGroupBox:
839 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
840 const auto pen = highContrastTheme ? frame->palette.buttonText().color()
841 : winUI3Color(frameColorStrong);
842 if (frame->features & QStyleOptionFrame::Flat) {
843 painter->setBrush(Qt::NoBrush);
844 painter->setPen(pen);
845 const QRect &fr = frame->rect;
846 QPoint p1(fr.x(), fr.y() + 1);
847 QPoint p2(fr.x() + fr.width(), p1.y());
848 painter->drawLine(p1, p2);
849 } else {
850 const auto frameRect = QRectF(frame->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
851 drawRoundedRect(painter, frameRect, pen, Qt::NoBrush);
852 }
853 }
854 break;
855 case PE_IndicatorHeaderArrow:
856 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
857 QFont f(d->assetFont);
858 f.setPointSize(6);
859 painter->setFont(f);
860 painter->setPen(header->palette.text().color());
861 QRectF rect = option->rect;
862 if (header->sortIndicator & QStyleOptionHeader::SortUp) {
863 painter->drawText(rect, Qt::AlignCenter, ChevronUpSmall);
864 } else if (header->sortIndicator & QStyleOptionHeader::SortDown) {
865 painter->drawText(rect, Qt::AlignCenter, ChevronDownSmall);
866 }
867 }
868 break;
869 case PE_IndicatorCheckBox: {
870 const bool isRtl = option->direction == Qt::RightToLeft;
871 const bool isOn = option->state & State_On;
872 const bool isPartial = option->state & State_NoChange;
873
874 QRectF rect = isRtl ? option->rect.adjusted(0, 0, -2, 0) : option->rect.adjusted(2, 0, 0, 0);
875 const QPointF center = rect.center();
876 rect.setWidth(15);
877 rect.setHeight(15);
878 rect.moveCenter(center);
879
880 drawRoundedRect(painter, rect, borderPenControlAlt(option),
881 controlFillBrush(option, ControlType::ControlAlt));
882
883 if (isOn) {
884 painter->setFont(d->assetFont);
885 painter->setPen(controlTextColor(option, QPalette::Window));
886 qreal clipWidth = 1.0;
887 QFontMetrics fm(d->assetFont);
888 QRectF clipRect = fm.boundingRect(AcceptMedium);
889 if (d->transitionsEnabled() && option->styleObject) {
890 QNumberStyleAnimation *animation = qobject_cast<QNumberStyleAnimation *>(
891 d->animation(option->styleObject));
892 if (animation)
893 clipWidth = animation->currentValue();
894 }
895
896 clipRect.moveCenter(center);
897 clipRect.setLeft(rect.x() + (rect.width() - clipRect.width()) / 2.0 + 0.5);
898 clipRect.setWidth(clipWidth * clipRect.width());
899 painter->drawText(clipRect, Qt::AlignVCenter | Qt::AlignLeft, AcceptMedium);
900 } else if (isPartial) {
901 QFont f(d->assetFont);
902 f.setPointSize(6);
903 painter->setFont(f);
904 painter->setPen(controlTextColor(option, QPalette::Window));
905 painter->drawText(rect, Qt::AlignCenter, Dash12);
906 }
907 }
908 break;
909 case PE_IndicatorBranch: {
910 if (option->state & State_Children) {
911 const bool isReverse = option->direction == Qt::RightToLeft;
912 const bool isOpen = option->state & QStyle::State_Open;
913 QFont f(d->assetFont);
914 f.setPointSize(6);
915 painter->setFont(f);
916 painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled,
917 QPalette::WindowText));
918 const auto str = isOpen ? ChevronDownMed : (isReverse ? ChevronLeftMed : ChevronRightMed);
919 painter->drawText(option->rect, Qt::AlignCenter, str);
920 }
921 }
922 break;
923 case PE_IndicatorRadioButton: {
924 const bool isRtl = option->direction == Qt::RightToLeft;
925 const bool isOn = option->state & State_On;
926 qreal innerRadius = 7.0f;
927 if (d->transitionsEnabled() && option->styleObject) {
928 if (option->styleObject->property("_q_end_radius").isNull())
929 option->styleObject->setProperty("_q_end_radius", innerRadius);
930 QNumberStyleAnimation *animation = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
931 innerRadius = animation ? animation->currentValue() : option->styleObject->property("_q_end_radius").toFloat();
932 option->styleObject->setProperty("_q_inner_radius", innerRadius);
933 } else {
934 innerRadius = radioButtonInnerRadius(state);
935 }
936
937 QRectF rect = isRtl ? option->rect.adjusted(0, 0, -2, 0) : option->rect.adjusted(2, 0, 0, 0);
938 const QPointF center = rect.center();
939
940 painter->setPen(borderPenControlAlt(option));
941 painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
942 if (isOn) {
943 QPainterPath path;
944 path.addEllipse(center, 7.5, 7.5);
945 path.addEllipse(center, innerRadius, innerRadius);
946 painter->drawPath(path);
947 // Text On Accent/Primary
948 painter->setBrush(option->palette.window().color());
949 painter->drawEllipse(center, innerRadius, innerRadius);
950 } else {
951 painter->drawEllipse(center, 7.5, 7.5);
952 }
953 }
954 break;
955 case PE_PanelButtonTool:
956 case PE_PanelButtonBevel:{
957 const bool isEnabled = state & QStyle::State_Enabled;
958 const bool isMouseOver = state & QStyle::State_MouseOver;
959 const bool isRaised = state & QStyle::State_Raised;
960 const QRectF rect = option->rect.marginsRemoved(QMargins(2,2,2,2));
961 if (element == PE_PanelButtonTool && ((!isMouseOver && !isRaised) || !isEnabled))
962 painter->setPen(Qt::NoPen);
963 else
964 painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
965 painter->setBrush(controlFillBrush(option, ControlType::Control));
966 painter->drawRoundedRect(rect,
967 secondLevelRoundingRadius, secondLevelRoundingRadius);
968
969 if (isRaised) {
970 const qreal sublineOffset = secondLevelRoundingRadius - 0.5;
971 painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]);
972 painter->drawLine(rect.bottomLeft() + QPointF(sublineOffset, 0.5), rect.bottomRight() + QPointF(-sublineOffset, 0.5));
973 }
974 }
975 break;
976 case PE_FrameDefaultButton:
977 painter->setPen(option->palette.accent().color());
978 painter->setBrush(Qt::NoBrush);
979 painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
980 break;
981 case PE_FrameMenu:
982 break;
983 case PE_PanelMenu: {
984 const QRect rect = option->rect.marginsRemoved(QMargins(2, 2, 2, 2));
985 painter->setPen(highContrastTheme ? QPen(option->palette.windowText().color(), 2)
986 : winUI3Color(frameColorLight));
987 painter->setBrush(winUI3Color(menuPanelFill));
988 painter->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius);
989 break;
990 }
991 case PE_PanelLineEdit:
992 if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
993 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
994 drawRoundedRect(painter, frameRect, Qt::NoPen, option->palette.brush(QPalette::Base));
995
996 if (panel->lineWidth > 0)
997 proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
998
999 const bool isMouseOver = state & State_MouseOver;
1000 const bool hasFocus = state & State_HasFocus;
1001 if (isMouseOver && !hasFocus && !highContrastTheme)
1002 drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor));
1003 }
1004 break;
1005 case PE_FrameLineEdit: {
1006 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1007 drawLineEditFrame(painter, frameRect, option);
1008 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
1009 QStyleOptionFocusRect fropt;
1010 fropt.QStyleOption::operator=(*option);
1011 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
1012 }
1013 break;
1014 }
1015 case PE_Frame: {
1016 if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1017 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1018 if (qobject_cast<const QComboBoxPrivateContainer *>(widget)) {
1019 QPen pen;
1020 if (highContrastTheme)
1021 pen = QPen(option->palette.windowText().color(), 2);
1022 else
1023 pen = Qt::NoPen;
1024 drawRoundedRect(painter, rect, pen, WINUI3Colors[colorSchemeIndex][menuPanelFill]);
1025 } else
1026 drawRoundedRect(painter, rect, Qt::NoPen, option->palette.brush(QPalette::Base));
1027
1028 if (frame->frameShape == QFrame::NoFrame)
1029 break;
1030
1031 drawLineEditFrame(painter, rect, option, qobject_cast<const QTextEdit *>(widget) != nullptr);
1032 }
1033 break;
1034 }
1035 case PE_PanelItemViewItem:
1036 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1037 if (vopt->backgroundBrush.style() != Qt::NoBrush) {
1038 QPainterStateGuard psg(painter);
1039 painter->setBrushOrigin(vopt->rect.topLeft());
1040 painter->fillRect(vopt->rect, vopt->backgroundBrush);
1041 }
1042 }
1043 break;
1044 case PE_PanelItemViewRow:
1045 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1046 // this is only called from a QTreeView to paint
1047 // - the tree branch decoration (incl. selected/hovered or not)
1048 // - the (alternate) background of the item in always unselected state
1049 const QRect &rect = vopt->rect;
1050 const bool isRtl = option->direction == Qt::RightToLeft;
1051 if (rect.width() <= 0)
1052 break;
1053
1054 painter->setPen(Qt::NoPen);
1055 if (vopt->features & QStyleOptionViewItem::Alternate)
1056 painter->setBrush(vopt->palette.alternateBase());
1057 else
1058 painter->setBrush(vopt->palette.base());
1059 painter->drawRect(rect);
1060
1061 const bool isTreeDecoration = vopt->features.testFlag(
1062 QStyleOptionViewItem::IsDecorationForRootColumn);
1063 if (isTreeDecoration && vopt->state.testAnyFlags(State_Selected | State_MouseOver) &&
1064 vopt->showDecorationSelected) {
1065 const bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1066 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1067 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1068 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1069
1070 if (onlyOne)
1071 isFirst = true;
1072
1073 if (isRtl) {
1074 isFirst = !isFirst;
1075 isLast = !isLast;
1076 }
1077
1078 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1079 painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1080 painter->setPen(Qt::NoPen);
1081 if (isFirst) {
1082 painter->save();
1083 painter->setClipRect(rect);
1084 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1085 secondLevelRoundingRadius, secondLevelRoundingRadius);
1086 painter->restore();
1087 } else if (isLast) {
1088 painter->save();
1089 painter->setClipRect(rect);
1090 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1091 secondLevelRoundingRadius, secondLevelRoundingRadius);
1092 painter->restore();
1093 } else {
1094 painter->drawRect(vopt->rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1095 }
1096 }
1097 }
1098 break;
1099 case QStyle::PE_Widget: {
1100 if (widget && widget->palette().isBrushSet(QPalette::Active, widget->backgroundRole())) {
1101 const QBrush bg = widget->palette().brush(widget->backgroundRole());
1102 auto wp = QWidgetPrivate::get(widget);
1103 QPainterStateGuard psg(painter);
1104 wp->updateBrushOrigin(painter, bg);
1105 painter->fillRect(option->rect, bg);
1106 }
1107 break;
1108 }
1109 case QStyle::PE_FrameWindow:
1110 if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1111
1112 QRectF rect= option->rect;
1113 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1114
1115 QRectF bottomLeftCorner = QRectF(rect.left() + 1.0,
1116 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1117 secondLevelRoundingRadius,
1118 secondLevelRoundingRadius);
1119 QRectF bottomRightCorner = QRectF(rect.right() - 1.0 - secondLevelRoundingRadius,
1120 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1121 secondLevelRoundingRadius,
1122 secondLevelRoundingRadius);
1123
1124 //Draw Mask
1125 if (widget != nullptr) {
1126 QBitmap mask(widget->width(), widget->height());
1127 mask.clear();
1128
1129 QPainter maskPainter(&mask);
1130 maskPainter.setRenderHint(QPainter::Antialiasing);
1131 maskPainter.setBrush(Qt::color1);
1132 maskPainter.setPen(Qt::NoPen);
1133 maskPainter.drawRoundedRect(option->rect,secondLevelRoundingRadius,secondLevelRoundingRadius);
1134 const_cast<QWidget*>(widget)->setMask(mask);
1135 }
1136
1137 //Draw Window
1138 painter->setPen(QPen(frm->palette.base(), fwidth));
1139 painter->drawLine(QPointF(rect.left(), rect.top()),
1140 QPointF(rect.left(), rect.bottom() - fwidth));
1141 painter->drawLine(QPointF(rect.left() + fwidth, rect.bottom()),
1142 QPointF(rect.right() - fwidth, rect.bottom()));
1143 painter->drawLine(QPointF(rect.right(), rect.top()),
1144 QPointF(rect.right(), rect.bottom() - fwidth));
1145
1146 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1147 painter->drawLine(QPointF(rect.left() + 0.5, rect.top() + 0.5),
1148 QPointF(rect.left() + 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1149 painter->drawLine(QPointF(rect.left() + 0.5 + secondLevelRoundingRadius, rect.bottom() - 0.5),
1150 QPointF(rect.right() - 0.5 - secondLevelRoundingRadius, rect.bottom() - 0.5));
1151 painter->drawLine(QPointF(rect.right() - 0.5, rect.top() + 1.5),
1152 QPointF(rect.right() - 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1153
1154 painter->setPen(Qt::NoPen);
1155 painter->setBrush(frm->palette.base());
1156 painter->drawPie(bottomRightCorner.marginsAdded(QMarginsF(2.5,2.5,0.0,0.0)),
1157 270 * 16,90 * 16);
1158 painter->drawPie(bottomLeftCorner.marginsAdded(QMarginsF(0.0,2.5,2.5,0.0)),
1159 -90 * 16,-90 * 16);
1160
1161 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1162 painter->setBrush(Qt::NoBrush);
1163 painter->drawArc(bottomRightCorner,
1164 0 * 16,-90 * 16);
1165 painter->drawArc(bottomLeftCorner,
1166 -90 * 16,-90 * 16);
1167 }
1168 break;
1169 default:
1170 QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
1171 }
1172 painter->restore();
1173}
1174
1175/*!
1176 \internal
1177*/
1178void QWindows11Style::drawControl(ControlElement element, const QStyleOption *option,
1179 QPainter *painter, const QWidget *widget) const
1180{
1181 Q_D(const QWindows11Style);
1182 State flags = option->state;
1183
1184 painter->save();
1185 painter->setRenderHint(QPainter::Antialiasing);
1186 switch (element) {
1187 case QStyle::CE_ComboBoxLabel:
1188 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1189 QStyleOptionComboBox newOption = *cb;
1190 newOption.rect.adjust(4,0,-4,0);
1191 QCommonStyle::drawControl(element, &newOption, painter, widget);
1192 }
1193 break;
1194 case QStyle::CE_TabBarTabShape:
1195#if QT_CONFIG(tabbar)
1196 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
1197 QRectF tabRect = tab->rect.marginsRemoved(QMargins(2,2,0,0));
1198 painter->setPen(Qt::NoPen);
1199 painter->setBrush(tab->palette.base());
1200 if (tab->state & State_MouseOver){
1201 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1202 } else if (tab->state & State_Selected) {
1203 painter->setBrush(tab->palette.base());
1204 } else {
1205 painter->setBrush(tab->palette.window());
1206 }
1207 painter->drawRoundedRect(tabRect,2,2);
1208
1209 painter->setBrush(Qt::NoBrush);
1210 painter->setPen(highContrastTheme == true ? tab->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]);
1211 painter->drawRoundedRect(tabRect.adjusted(0.5,0.5,-0.5,-0.5),2,2);
1212
1213 }
1214#endif // QT_CONFIG(tabbar)
1215 break;
1216 case CE_ToolButtonLabel:
1217#if QT_CONFIG(toolbutton)
1218 if (const QStyleOptionToolButton *toolbutton
1219 = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
1220 QRect rect = toolbutton->rect;
1221 int shiftX = 0;
1222 int shiftY = 0;
1223 if (toolbutton->state & (State_Sunken | State_On)) {
1224 shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget);
1225 shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton, widget);
1226 }
1227 // Arrow type always overrules and is always shown
1228 bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow;
1229 if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty())
1230 || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) {
1231 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
1232 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1233 alignment |= Qt::TextHideMnemonic;
1234 rect.translate(shiftX, shiftY);
1235 painter->setFont(toolbutton->font);
1236 const QString text = d->toolButtonElideText(toolbutton, rect, alignment);
1237 // option->state has no State_Sunken here, windowsvistastyle/CC_ToolButton removes it
1238 painter->setPen(controlTextColor(option));
1239 proxy()->drawItemText(painter, rect, alignment, toolbutton->palette,
1240 toolbutton->state & State_Enabled, text);
1241 } else {
1242 QPixmap pm;
1243 QSize pmSize = toolbutton->iconSize;
1244 if (!toolbutton->icon.isNull()) {
1245 QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off;
1246 QIcon::Mode mode;
1247 if (!(toolbutton->state & State_Enabled))
1248 mode = QIcon::Disabled;
1249 else if ((toolbutton->state & State_MouseOver) && (toolbutton->state & State_AutoRaise))
1250 mode = QIcon::Active;
1251 else
1252 mode = QIcon::Normal;
1253 pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), painter->device()->devicePixelRatio(),
1254 mode, state);
1255 pmSize = pm.size() / pm.devicePixelRatio();
1256 }
1257
1258 if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) {
1259 painter->setFont(toolbutton->font);
1260 QRect pr = rect,
1261 tr = rect;
1262 int alignment = Qt::TextShowMnemonic;
1263 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1264 alignment |= Qt::TextHideMnemonic;
1265
1266 if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
1267 pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1268 tr.adjust(0, pr.height() - 1, 0, -1);
1269 pr.translate(shiftX, shiftY);
1270 if (!hasArrow) {
1271 proxy()->drawItemPixmap(painter, pr, Qt::AlignCenter, pm);
1272 } else {
1273 drawArrow(proxy(), toolbutton, pr, painter, widget);
1274 }
1275 alignment |= Qt::AlignCenter;
1276 } else {
1277 pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1278 tr.adjust(pr.width(), 0, 0, 0);
1279 pr.translate(shiftX, shiftY);
1280 if (!hasArrow) {
1281 proxy()->drawItemPixmap(painter, QStyle::visualRect(toolbutton->direction, rect, pr), Qt::AlignCenter, pm);
1282 } else {
1283 drawArrow(proxy(), toolbutton, pr, painter, widget);
1284 }
1285 alignment |= Qt::AlignLeft | Qt::AlignVCenter;
1286 }
1287 tr.translate(shiftX, shiftY);
1288 const QString text = d->toolButtonElideText(toolbutton, tr, alignment);
1289 painter->setPen(controlTextColor(option));
1290 proxy()->drawItemText(painter, QStyle::visualRect(toolbutton->direction, rect, tr), alignment, toolbutton->palette,
1291 toolbutton->state & State_Enabled, text);
1292 } else {
1293 rect.translate(shiftX, shiftY);
1294 if (hasArrow) {
1295 drawArrow(proxy(), toolbutton, rect, painter, widget);
1296 } else {
1297 proxy()->drawItemPixmap(painter, rect, Qt::AlignCenter, pm);
1298 }
1299 }
1300 }
1301 }
1302#endif // QT_CONFIG(toolbutton)
1303 break;
1304 case QStyle::CE_ShapedFrame:
1305 if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1306 int frameShape = f->frameShape;
1307 int frameShadow = QFrame::Plain;
1308 if (f->state & QStyle::State_Sunken)
1309 frameShadow = QFrame::Sunken;
1310 else if (f->state & QStyle::State_Raised)
1311 frameShadow = QFrame::Raised;
1312
1313 int lw = f->lineWidth;
1314 int mlw = f->midLineWidth;
1315
1316 switch (frameShape) {
1317 case QFrame::Box:
1318 if (frameShadow == QFrame::Plain)
1319 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1320 else
1321 qDrawShadeRect(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw);
1322 break;
1323 case QFrame::Panel:
1324 if (frameShadow == QFrame::Plain)
1325 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1326 else
1327 qDrawShadePanel(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw);
1328 break;
1329 default:
1330 QWindowsVistaStyle::drawControl(element, option, painter, widget);
1331 }
1332 }
1333 break;
1334 case QStyle::CE_ProgressBarGroove:{
1335 if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
1336 QRect rect = subElementRect(SE_ProgressBarContents, progbaropt, widget);
1337 QPointF center = rect.center();
1338 if (progbaropt->state & QStyle::State_Horizontal) {
1339 rect.setHeight(1);
1340 rect.moveTop(center.y());
1341 } else {
1342 rect.setWidth(1);
1343 rect.moveLeft(center.x());
1344 }
1345 painter->setPen(Qt::NoPen);
1346 painter->setBrush(Qt::gray);
1347 painter->drawRect(rect);
1348 }
1349 break;
1350 }
1351 case QStyle::CE_ProgressBarContents:
1352 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1353 QPainterStateGuard psg(painter);
1354 QRectF rect = subElementRect(SE_ProgressBarContents, baropt, widget);
1355 painter->translate(rect.topLeft());
1356 rect.translate(-rect.topLeft());
1357
1358 constexpr qreal progressBarThickness = 3;
1359 constexpr qreal progressBarHalfThickness = progressBarThickness / 2.0;
1360
1361 const auto isIndeterminate = baropt->maximum == 0 && baropt->minimum == 0;
1362 const auto orientation =
1363 (baropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical;
1364 const auto inverted = baropt->invertedAppearance;
1365 const auto reverse = (baropt->direction == Qt::RightToLeft) ^ inverted;
1366 // If the orientation is vertical, we use a transform to rotate
1367 // the progress bar 90 degrees (counter)clockwise. This way we can use the
1368 // same rendering code for both orientations.
1369 if (orientation == Qt::Vertical) {
1370 rect = QRectF(rect.left(), rect.top(), rect.height(),
1371 rect.width()); // flip width and height
1372 QTransform m;
1373 if (inverted) {
1374 m.rotate(90);
1375 m.translate(0, -rect.height() + 1);
1376 } else {
1377 m.rotate(-90);
1378 m.translate(-rect.width(), 0);
1379 }
1380 painter->setTransform(m, true);
1381 } else if (reverse) {
1382 QTransform m = QTransform::fromScale(-1, 1);
1383 m.translate(-rect.width(), 0);
1384 painter->setTransform(m, true);
1385 }
1386 const qreal offset = (int(rect.height()) % 2 == 0) ? 0.5f : 0.0f;
1387
1388 if (isIndeterminate) {
1389 constexpr auto loopDurationMSec = 4000;
1390 const auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(
1391 std::chrono::system_clock::now());
1392 const auto elapsed = elapsedTime.time_since_epoch().count();
1393 const auto handleCenter = (elapsed % loopDurationMSec) / float(loopDurationMSec);
1394 const auto isLongHandle = (elapsed / loopDurationMSec) % 2 == 0;
1395 const auto lengthFactor = (isLongHandle ? 33.0f : 25.0f) / 100.0f;
1396 const auto begin = qMax(handleCenter * (1 + lengthFactor) - lengthFactor, 0.0f);
1397 const auto end = qMin(handleCenter * (1 + lengthFactor), 1.0f);
1398 const auto barBegin = begin * rect.width();
1399 const auto barEnd = end * rect.width();
1400 rect = QRectF(QPointF(rect.left() + barBegin, rect.top()),
1401 QPointF(rect.left() + barEnd, rect.bottom()));
1402 const_cast<QWidget *>(widget)->update();
1403 } else {
1404 const auto fillPercentage = (float(baropt->progress - baropt->minimum))
1405 / (float(baropt->maximum - baropt->minimum));
1406 rect.setWidth(rect.width() * fillPercentage);
1407 }
1408 const QPointF center = rect.center();
1409 rect.setHeight(progressBarThickness);
1410 rect.moveTop(center.y() - progressBarHalfThickness - offset);
1411 drawRoundedRect(painter, rect, Qt::NoPen, baropt->palette.accent());
1412 }
1413 break;
1414 case QStyle::CE_ProgressBarLabel:
1415 if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
1416 const bool vertical = !(progbaropt->state & QStyle::State_Horizontal);
1417 if (!vertical) {
1418 QRect rect = subElementRect(SE_ProgressBarLabel, progbaropt, widget);
1419 painter->setPen(progbaropt->palette.text().color());
1420 painter->drawText(rect, progbaropt->text, progbaropt->textAlignment);
1421 }
1422 }
1423 break;
1424 case CE_PushButtonLabel:
1425 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1426 QRect textRect = btn->rect;
1427
1428 int tf = Qt::AlignVCenter|Qt::TextShowMnemonic;
1429 if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget))
1430 tf |= Qt::TextHideMnemonic;
1431
1432 if (btn->features & QStyleOptionButton::HasMenu) {
1433 int indicatorSize = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget);
1434 QLineF menuSplitter;
1435 QRectF indicatorRect;
1436 painter->save();
1437 painter->setFont(d->assetFont);
1438
1439 if (btn->direction == Qt::LeftToRight) {
1440 indicatorRect = QRect(textRect.x() + textRect.width() - indicatorSize - 4, textRect.y(),2 * 4 + indicatorSize, textRect.height());
1441 indicatorRect.adjust(0.5,-0.5,0.5,0.5);
1442 menuSplitter = QLineF(indicatorRect.topLeft(),indicatorRect.bottomLeft());
1443 textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
1444 } else {
1445 indicatorRect = QRect(textRect.x(), textRect.y(), textRect.x() + indicatorSize + 4, textRect.height());
1446 indicatorRect.adjust(-0.5,-0.5,-0.5,0.5);
1447 menuSplitter = QLineF(indicatorRect.topRight(),indicatorRect.bottomRight());
1448 textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
1449 }
1450 painter->drawText(indicatorRect, Qt::AlignCenter, ChevronDownMed);
1451 painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
1452 painter->drawLine(menuSplitter);
1453 painter->restore();
1454 }
1455 if (!btn->icon.isNull()) {
1456 //Center both icon and text
1457 QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
1458 if (mode == QIcon::Normal && btn->state & State_HasFocus)
1459 mode = QIcon::Active;
1460 QIcon::State state = QIcon::Off;
1461 if (btn->state & State_On)
1462 state = QIcon::On;
1463
1464 QPixmap pixmap = btn->icon.pixmap(btn->iconSize, painter->device()->devicePixelRatio(), mode, state);
1465 int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
1466 int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
1467 int labelWidth = pixmapWidth;
1468 int labelHeight = pixmapHeight;
1469 int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint()
1470 if (!btn->text.isEmpty()) {
1471 int textWidth = btn->fontMetrics.boundingRect(option->rect, tf, btn->text).width();
1472 labelWidth += (textWidth + iconSpacing);
1473 }
1474
1475 QRect iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2,
1476 textRect.y() + (textRect.height() - labelHeight) / 2,
1477 pixmapWidth, pixmapHeight);
1478
1479 iconRect = visualRect(btn->direction, textRect, iconRect);
1480
1481 if (btn->direction == Qt::RightToLeft) {
1482 tf |= Qt::AlignRight;
1483 textRect.setRight(iconRect.left() - iconSpacing / 2);
1484 } else {
1485 tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead
1486 textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing / 2);
1487 }
1488
1489 if (btn->state & (State_On | State_Sunken))
1490 iconRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, option, widget),
1491 proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget));
1492 painter->drawPixmap(iconRect, pixmap);
1493 } else {
1494 tf |= Qt::AlignHCenter;
1495 }
1496
1497 painter->setPen(controlTextColor(option));
1498 proxy()->drawItemText(painter, textRect, tf, option->palette,btn->state & State_Enabled, btn->text);
1499 }
1500 break;
1501 case CE_PushButtonBevel:
1502 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1503 QRectF rect = btn->rect.marginsRemoved(QMargins(2, 2, 2, 2));
1504 painter->setPen(Qt::NoPen);
1505 if (btn->features.testFlag(QStyleOptionButton::Flat)) {
1506 painter->setBrush(btn->palette.button());
1507 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1508 if (flags & (State_Sunken | State_On)) {
1509 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtlePressedColor]);
1510 }
1511 else if (flags & State_MouseOver) {
1512 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1513 }
1514 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1515 } else {
1516 painter->setBrush(controlFillBrush(option, ControlType::Control));
1517 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1518
1519 rect.adjust(0.5,0.5,-0.5,-0.5);
1520 const bool defaultButton = btn->features.testFlag(QStyleOptionButton::DefaultButton);
1521 painter->setBrush(Qt::NoBrush);
1522 painter->setPen(defaultButton ? option->palette.accent().color()
1523 : WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
1524 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1525
1526 painter->setPen(defaultButton ? WINUI3Colors[colorSchemeIndex][controlStrokeOnAccentSecondary]
1527 : WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]);
1528 if (flags & State_Raised)
1529 painter->drawLine(rect.bottomLeft() + QPointF(4.0,0.0), rect.bottomRight() + QPointF(-4,0.0));
1530 }
1531 }
1532 break;
1533 case CE_MenuBarItem:
1534 if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1535 constexpr int hPadding = 11;
1536 constexpr int topPadding = 4;
1537 constexpr int bottomPadding = 6;
1538 bool active = mbi->state & State_Selected;
1539 bool hasFocus = mbi->state & State_HasFocus;
1540 bool down = mbi->state & State_Sunken;
1541 bool enabled = mbi->state & State_Enabled;
1542 QStyleOptionMenuItem newMbi = *mbi;
1543 newMbi.font.setPointSize(10);
1544 if (enabled && active) {
1545 if (down)
1546 painter->setBrushOrigin(painter->brushOriginF() + QPoint(1, 1));
1547 if (hasFocus) {
1548 if (highContrastTheme)
1549 painter->setPen(QPen(newMbi.palette.highlight().color(), 2));
1550 else
1551 painter->setPen(Qt::NoPen);
1552 painter->setBrush(highContrastTheme ? newMbi.palette.window().color() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1553 QRect rect = mbi->rect.marginsRemoved(QMargins(5,0,5,0));
1554 painter->drawRoundedRect(rect,secondLevelRoundingRadius,secondLevelRoundingRadius, Qt::AbsoluteSize);
1555 }
1556 } else if (enabled && highContrastTheme) {
1557 painter->setPen(QPen(newMbi.palette.windowText().color(), 2));
1558 painter->setBrush(newMbi.palette.window().color());
1559 QRect rect = mbi->rect.marginsRemoved(QMargins(5,0,5,0));
1560 painter->drawRoundedRect(rect,secondLevelRoundingRadius,secondLevelRoundingRadius, Qt::AbsoluteSize);
1561 }
1562 newMbi.rect.adjust(hPadding,topPadding,-hPadding,-bottomPadding);
1563 painter->setFont(newMbi.font);
1564 QCommonStyle::drawControl(element, &newMbi, painter, widget);
1565 }
1566 break;
1567
1568#if QT_CONFIG(menu)
1569 case CE_MenuEmptyArea:
1570 break;
1571
1572 case CE_MenuItem:
1573 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1574 int x, y, w, h;
1575 menuitem->rect.getRect(&x, &y, &w, &h);
1576 int tab = menuitem->reservedShortcutWidth;
1577 bool dis = !(menuitem->state & State_Enabled);
1578 bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
1579 ? menuitem->checked : false;
1580 bool act = menuitem->state & State_Selected;
1581
1582 // windows always has a check column, regardless whether we have an icon or not
1583 int checkcol = qMax<int>(menuitem->maxIconWidth, 32);
1584
1585 QBrush fill = (act == true && dis == false) ? (highContrastTheme ? menuitem->palette.brush(QPalette::Highlight) : QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor])) : menuitem->palette.brush(QPalette::Button);
1586 painter->setBrush(fill);
1587 painter->setPen(Qt::NoPen);
1588 const QRect rect = menuitem->rect.marginsRemoved(QMargins(2,2,2,2));
1589 if (act && dis == false)
1590 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius, Qt::AbsoluteSize);
1591
1592 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator){
1593 int yoff = 4;
1594 painter->setPen(highContrastTheme == true ? menuitem->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]);
1595 painter->drawLine(x, y + yoff, x + w, y + yoff );
1596 break;
1597 }
1598
1599 QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height()));
1600 if (!menuitem->icon.isNull() && checked) {
1601 if (act) {
1602 qDrawShadePanel(painter, vCheckRect,
1603 menuitem->palette, true, 1,
1604 &menuitem->palette.brush(QPalette::Button));
1605 } else {
1606 QBrush fill(menuitem->palette.light().color(), Qt::Dense4Pattern);
1607 qDrawShadePanel(painter, vCheckRect, menuitem->palette, true, 1, &fill);
1608 }
1609 }
1610 // On Windows Style, if we have a checkable item and an icon we
1611 // draw the icon recessed to indicate an item is checked. If we
1612 // have no icon, we draw a checkmark instead.
1613 if (!menuitem->icon.isNull()) {
1614 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
1615 if (act && !dis)
1616 mode = QIcon::Active;
1617 const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
1618 QRect pmr(QPoint(0, 0), QSize(size, size));
1619 pmr.moveCenter(vCheckRect.center());
1620 menuitem->icon.paint(painter, pmr, Qt::AlignCenter, mode,
1621 checked ? QIcon::On : QIcon::Off);
1622 } else if (checked) {
1623 painter->save();
1624 if (dis)
1625 painter->setPen(menuitem->palette.text().color());
1626 painter->setFont(d->assetFont);
1627 const int text_flags = Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextDontClip | Qt::TextSingleLine;
1628 painter->setPen(option->palette.text().color());
1629 painter->drawText(vCheckRect, text_flags, CheckMark);
1630 painter->restore();
1631 }
1632 painter->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color());
1633
1634 QColor discol = menuitem->palette.text().color();
1635 if (dis)
1636 discol = menuitem->palette.color(QPalette::Disabled, QPalette::WindowText);
1637
1638 QStringView s(menuitem->text);
1639 if (!s.isEmpty()) { // draw text
1640 int xm = QWindowsStylePrivate::windowsItemFrame + checkcol + QWindowsStylePrivate::windowsItemHMargin;
1641 int xpos = menuitem->rect.x() + xm;
1642 QRect textRect(xpos, y + QWindowsStylePrivate::windowsItemVMargin,
1643 w - xm - QWindowsStylePrivate::windowsRightBorder - tab + 1, h - 2 * QWindowsStylePrivate::windowsItemVMargin);
1644 QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
1645
1646 painter->save();
1647 qsizetype t = s.indexOf(u'\t');
1648 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
1649 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
1650 text_flags |= Qt::TextHideMnemonic;
1651 text_flags |= Qt::AlignLeft;
1652 if (t >= 0) {
1653 QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
1654 QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
1655 const QString textToDraw = s.mid(t + 1).toString();
1656 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
1657 painter->setPen(menuitem->palette.light().color());
1658 painter->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, textToDraw);
1659 }
1660 if (highContrastTheme)
1661 painter->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color());
1662 else
1663 painter->setPen(menuitem->palette.color(QPalette::Disabled, QPalette::Text));
1664 painter->drawText(vShortcutRect, text_flags, textToDraw);
1665 s = s.left(t);
1666 }
1667 QFont font = menuitem->font;
1668 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
1669 font.setBold(true);
1670 painter->setFont(font);
1671 const QString textToDraw = s.left(t).toString();
1672 painter->setPen(highContrastTheme && act ? menuitem->palette.highlightedText().color() : discol);
1673 painter->drawText(vTextRect, text_flags, textToDraw);
1674 painter->restore();
1675 }
1676 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
1677 int dim = (h - 2 * QWindowsStylePrivate::windowsItemFrame) / 2;
1678 int xpos = x + w - QWindowsStylePrivate::windowsArrowHMargin - QWindowsStylePrivate::windowsItemFrame - dim;
1679 QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
1680 QStyleOptionMenuItem newMI = *menuitem;
1681 newMI.rect = vSubMenuRect;
1682 newMI.state = dis ? State_None : State_Enabled;
1683 if (act)
1684 newMI.palette.setColor(QPalette::ButtonText,
1685 newMI.palette.highlightedText().color());
1686 painter->save();
1687 painter->setFont(d->assetFont);
1688 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
1689 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
1690 text_flags |= Qt::TextHideMnemonic;
1691 text_flags |= Qt::AlignLeft;
1692 painter->setPen(option->palette.text().color());
1693 const bool isReverse = option->direction == Qt::RightToLeft;
1694 const auto str = isReverse ? ChevronLeftMed : ChevronRightMed;
1695 painter->drawText(vSubMenuRect, Qt::AlignCenter, str);
1696 painter->restore();
1697 }
1698 }
1699 break;
1700#endif // QT_CONFIG(menu)
1701 case CE_MenuBarEmptyArea: {
1702 break;
1703 }
1704 case CE_HeaderEmptyArea:
1705 break;
1706 case CE_HeaderSection: {
1707 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1708 painter->setPen(Qt::NoPen);
1709 painter->setBrush(header->palette.button());
1710 painter->drawRect(header->rect);
1711
1712 painter->setPen(highContrastTheme == true ? header->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]);
1713 painter->setBrush(Qt::NoBrush);
1714
1715 if (header->position == QStyleOptionHeader::OnlyOneSection) {
1716 break;
1717 }
1718 else if (header->position == QStyleOptionHeader::Beginning) {
1719 painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0),
1720 QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0));
1721 }
1722 else if (header->position == QStyleOptionHeader::End) {
1723 painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0),
1724 QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0));
1725 } else {
1726 painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0),
1727 QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0));
1728 painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0),
1729 QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0));
1730 }
1731 painter->drawLine(QPointF(option->rect.bottomLeft()) + QPointF(0.0,0.5),
1732 QPointF(option->rect.bottomRight()) + QPointF(0.0,0.5));
1733 }
1734 break;
1735 }
1736 case CE_ItemViewItem: {
1737 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1738 QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget);
1739 QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget);
1740 QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget);
1741
1742 // draw the background
1743 proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget);
1744
1745 const QRect &rect = vopt->rect;
1746 const bool isRtl = option->direction == Qt::RightToLeft;
1747 bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1748 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1749 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1750 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1751
1752 // the tree decoration already painted the left side of the rounded rect
1753 if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) &&
1754 vopt->showDecorationSelected) {
1755 isFirst = false;
1756 if (onlyOne) {
1757 onlyOne = false;
1758 isLast = true;
1759 }
1760 }
1761
1762 if (isRtl) {
1763 if (isFirst) {
1764 isFirst = false;
1765 isLast = true;
1766 } else if (isLast) {
1767 isFirst = true;
1768 isLast = false;
1769 }
1770 }
1771 const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver);
1772 if (highlightCurrent) {
1773 if (highContrastTheme) {
1774 painter->setBrush(vopt->palette.highlight());
1775 } else {
1776 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1777 painter->setBrush(view && view->alternatingRowColors()
1778 ? vopt->palette.highlight()
1779 : winUI3Color(subtleHighlightColor));
1780 }
1781 } else {
1782 painter->setBrush(vopt->backgroundBrush);
1783 }
1784 painter->setPen(Qt::NoPen);
1785
1786 if (onlyOne) {
1787 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)),
1788 secondLevelRoundingRadius, secondLevelRoundingRadius);
1789 } else if (isFirst) {
1790 painter->save();
1791 painter->setClipRect(rect);
1792 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1793 secondLevelRoundingRadius, secondLevelRoundingRadius);
1794 painter->restore();
1795 } else if (isLast) {
1796 painter->save();
1797 painter->setClipRect(rect);
1798 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1799 secondLevelRoundingRadius, secondLevelRoundingRadius);
1800 painter->restore();
1801 } else {
1802 painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1803 }
1804
1805 // draw the check mark
1806 if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) {
1807 QStyleOptionViewItem option(*vopt);
1808 option.rect = checkRect;
1809 option.state = option.state & ~QStyle::State_HasFocus;
1810
1811 switch (vopt->checkState) {
1812 case Qt::Unchecked:
1813 option.state |= QStyle::State_Off;
1814 break;
1815 case Qt::PartiallyChecked:
1816 option.state |= QStyle::State_NoChange;
1817 break;
1818 case Qt::Checked:
1819 option.state |= QStyle::State_On;
1820 break;
1821 }
1822 proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget);
1823 }
1824
1825 // draw the icon
1826 if (iconRect.isValid()) {
1827 QIcon::Mode mode = QIcon::Normal;
1828 if (!(vopt->state & QStyle::State_Enabled))
1829 mode = QIcon::Disabled;
1830 else if (vopt->state & QStyle::State_Selected)
1831 mode = QIcon::Selected;
1832 QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
1833 vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state);
1834 }
1835
1836 painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color()
1837 : vopt->palette.text().color());
1838 d->viewItemDrawText(painter, vopt, textRect);
1839
1840 // paint a vertical marker for QListView
1841 if (vopt->state & State_Selected) {
1842 if (const QListView *lv = qobject_cast<const QListView *>(widget);
1843 lv && lv->viewMode() != QListView::IconMode && !highContrastTheme) {
1844 painter->setPen(vopt->palette.accent().color());
1845 const auto xPos = isRtl ? rect.right() - 1 : rect.left();
1846 const QLineF lines[2] = {
1847 QLineF(xPos, rect.y() + 2, xPos, rect.y() + rect.height() - 2),
1848 QLineF(xPos + 1, rect.y() + 2, xPos + 1, rect.y() + rect.height() - 2),
1849 };
1850 painter->drawLines(lines, 2);
1851 }
1852 }
1853 }
1854 break;
1855 }
1856 default:
1857 QWindowsVistaStyle::drawControl(element, option, painter, widget);
1858 }
1859 painter->restore();
1860}
1861
1862int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt,
1863 const QWidget *widget, QStyleHintReturn *returnData) const {
1864 switch (hint) {
1865 case QStyle::SH_Menu_AllowActiveAndDisabled:
1866 return 0;
1867 case SH_GroupBox_TextLabelColor:
1868 if (opt!=nullptr && widget!=nullptr)
1869 return opt->palette.text().color().rgba();
1870 return 0;
1871 case QStyle::SH_ItemView_ShowDecorationSelected:
1872 return 1;
1873 case QStyle::SH_Slider_AbsoluteSetButtons:
1874 return Qt::LeftButton;
1875 case QStyle::SH_Slider_PageSetButtons:
1876 return 0;
1877 default:
1878 return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData);
1879 }
1880}
1881
1882QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOption *option,
1883 const QWidget *widget) const
1884{
1885 QRect ret;
1886 switch (element) {
1887 case QStyle::SE_RadioButtonIndicator:
1888 case QStyle::SE_CheckBoxIndicator:
1889 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
1890 ret = ret.marginsRemoved(QMargins(4,0,0,0));
1891 break;
1892 case QStyle::SE_ComboBoxFocusRect:
1893 case QStyle::SE_CheckBoxFocusRect:
1894 case QStyle::SE_RadioButtonFocusRect:
1895 case QStyle::SE_PushButtonFocusRect:
1896 ret = option->rect;
1897 break;
1898 case QStyle::SE_LineEditContents:
1899 ret = option->rect.adjusted(4,0,-4,0);
1900 break;
1901 case QStyle::SE_ItemViewItemText:
1902 if (const auto *item = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1903 const int decorationOffset = item->features.testFlag(QStyleOptionViewItem::HasDecoration) ? item->decorationSize.width() : 0;
1904 const int checkboxOffset = item->features.testFlag(QStyleOptionViewItem::HasCheckIndicator) ? 16 : 0;
1905 if (widget && qobject_cast<QComboBoxPrivateContainer *>(widget->parentWidget())) {
1906 if (option->direction == Qt::LeftToRight)
1907 ret = option->rect.adjusted(decorationOffset + checkboxOffset + 5, 0, -5, 0);
1908 else
1909 ret = option->rect.adjusted(5, 0, decorationOffset - checkboxOffset - 5, 0);
1910 } else {
1911 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
1912 }
1913 } else {
1914 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
1915 }
1916 break;
1917 case QStyle::SE_ProgressBarLabel:
1918 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1919 if (pb->textAlignment.testFlags(Qt::AlignVCenter)) {
1920 ret = option->rect.adjusted(0, 6, 0, 0);
1921 } else {
1922 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
1923 }
1924 }
1925 break;
1926 case QStyle::SE_HeaderLabel:
1927 case QStyle::SE_HeaderArrow:
1928 ret = QCommonStyle::subElementRect(element, option, widget);
1929 break;
1930 default:
1931 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
1932 }
1933 return ret;
1934}
1935
1936/*!
1937 \internal
1938 */
1939QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
1940 SubControl subControl, const QWidget *widget) const
1941{
1942 QRect ret;
1943
1944 switch (control) {
1945#if QT_CONFIG(spinbox)
1946 case CC_SpinBox:
1947 if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
1948 QSize bs;
1949 int fw = spinbox->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0;
1950 bs.setHeight(qMax(8, spinbox->rect.height() - fw));
1951 bs.setWidth(16);
1952 int y = fw + spinbox->rect.y();
1953 int x, lx, rx;
1954 x = spinbox->rect.x() + spinbox->rect.width() - fw - 2 * bs.width();
1955 lx = fw;
1956 rx = x - fw;
1957 switch (subControl) {
1958 case SC_SpinBoxUp:
1959 if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
1960 return QRect();
1961 ret = QRect(x, y, bs.width(), bs.height());
1962 break;
1963 case SC_SpinBoxDown:
1964 if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons)
1965 return QRect();
1966 ret = QRect(x + bs.width(), y, bs.width(), bs.height());
1967 break;
1968 case SC_SpinBoxEditField:
1969 if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) {
1970 ret = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw);
1971 } else {
1972 ret = QRect(lx, fw, rx, spinbox->rect.height() - 2*fw);
1973 }
1974 break;
1975 case SC_SpinBoxFrame:
1976 ret = spinbox->rect;
1977 default:
1978 break;
1979 }
1980 ret = visualRect(spinbox->direction, spinbox->rect, ret);
1981 }
1982 break;
1983 case CC_TitleBar:
1984 if (const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
1985 SubControl sc = subControl;
1986 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
1987 static constexpr int indent = 3;
1988 static constexpr int controlWidthMargin = 2;
1989 const int controlHeight = titlebar->rect.height();
1990 const int controlWidth = 46;
1991 const int iconSize = proxy()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, option, widget);
1992 int offset = -(controlWidthMargin + indent);
1993
1994 bool isMinimized = titlebar->titleBarState & Qt::WindowMinimized;
1995 bool isMaximized = titlebar->titleBarState & Qt::WindowMaximized;
1996
1997 switch (sc) {
1998 case SC_TitleBarLabel:
1999 if (titlebar->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
2000 ret = titlebar->rect;
2001 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2002 ret.adjust(iconSize + controlWidthMargin + indent, 0, -controlWidth, 0);
2003 if (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)
2004 ret.adjust(0, 0, -controlWidth, 0);
2005 if (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)
2006 ret.adjust(0, 0, -controlWidth, 0);
2007 if (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)
2008 ret.adjust(0, 0, -controlWidth, 0);
2009 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2010 ret.adjust(0, 0, -controlWidth, 0);
2011 }
2012 break;
2013 case SC_TitleBarContextHelpButton:
2014 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2015 offset += controlWidth;
2016 Q_FALLTHROUGH();
2017 case SC_TitleBarMinButton:
2018 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2019 offset += controlWidth;
2020 else if (sc == SC_TitleBarMinButton)
2021 break;
2022 Q_FALLTHROUGH();
2023 case SC_TitleBarNormalButton:
2024 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2025 offset += controlWidth;
2026 else if (isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2027 offset += controlWidth;
2028 else if (sc == SC_TitleBarNormalButton)
2029 break;
2030 Q_FALLTHROUGH();
2031 case SC_TitleBarMaxButton:
2032 if (!isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2033 offset += controlWidth;
2034 else if (sc == SC_TitleBarMaxButton)
2035 break;
2036 Q_FALLTHROUGH();
2037 case SC_TitleBarShadeButton:
2038 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2039 offset += controlWidth;
2040 else if (sc == SC_TitleBarShadeButton)
2041 break;
2042 Q_FALLTHROUGH();
2043 case SC_TitleBarUnshadeButton:
2044 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2045 offset += controlWidth;
2046 else if (sc == SC_TitleBarUnshadeButton)
2047 break;
2048 Q_FALLTHROUGH();
2049 case SC_TitleBarCloseButton:
2050 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2051 offset += controlWidth;
2052 else if (sc == SC_TitleBarCloseButton)
2053 break;
2054 ret.setRect(titlebar->rect.right() - offset, titlebar->rect.top(),
2055 controlWidth, controlHeight);
2056 break;
2057 case SC_TitleBarSysMenu:
2058 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) {
2059 const auto yOfs = titlebar->rect.top() + (titlebar->rect.height() - iconSize) / 2;
2060 ret.setRect(titlebar->rect.left() + controlWidthMargin + indent, yOfs, iconSize,
2061 iconSize);
2062 }
2063 break;
2064 default:
2065 break;
2066 }
2067 if (widget && isMinimized && titlebar->rect.width() < offset)
2068 const_cast<QWidget*>(widget)->resize(controlWidthMargin + indent + offset + iconSize + controlWidthMargin, controlWidth);
2069 ret = visualRect(titlebar->direction, titlebar->rect, ret);
2070 }
2071 break;
2072#endif // Qt_NO_SPINBOX
2073 case CC_ScrollBar:
2074 {
2075 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
2076
2077 if (subControl == SC_ScrollBarAddLine || subControl == SC_ScrollBarSubLine) {
2078 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2079 if (scrollbar->orientation == Qt::Vertical)
2080 ret = ret.adjusted(2,2,-2,-3);
2081 else
2082 ret = ret.adjusted(3,2,-2,-2);
2083 }
2084 }
2085 break;
2086 }
2087 default:
2088 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2089 }
2090 return ret;
2091}
2092
2093/*!
2094 \internal
2095 */
2096QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *option,
2097 const QSize &size, const QWidget *widget) const
2098{
2099 QSize contentSize(size);
2100
2101 switch (type) {
2102
2103#if QT_CONFIG(menubar)
2104 case CT_MenuBarItem:
2105 if (!contentSize.isEmpty()) {
2106 constexpr int hMargin = 2 * 6;
2107 constexpr int hPadding = 2 * 11;
2108 constexpr int itemHeight = 32;
2109 contentSize.setWidth(contentSize.width() + hMargin + hPadding);
2110#if QT_CONFIG(tabwidget)
2111 if (widget->parent() && !qobject_cast<const QTabWidget *>(widget->parent()))
2112#endif
2113 contentSize.setHeight(itemHeight);
2114 }
2115 break;
2116#endif
2117#if QT_CONFIG(menu)
2118 case CT_MenuItem:
2119 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2120 const int checkcol = qMax<int>(menuItem->maxIconWidth, 32);
2121 int width = size.width();
2122 int height;
2123 if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
2124 width = 10;
2125 height = 6;
2126 } else {
2127 height = menuItem->fontMetrics.height() + 8;
2128 if (!menuItem->icon.isNull()) {
2129 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2130 height = qMax(height,
2131 menuItem->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
2132 }
2133 }
2134 if (menuItem->text.contains(u'\t'))
2135 width += menuItem->reservedShortcutWidth;
2136 else if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu)
2137 width += 2 * QWindowsStylePrivate::windowsArrowHMargin;
2138 else if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) {
2139 const QFontMetrics fm(menuItem->font);
2140 QFont fontBold = menuItem->font;
2141 fontBold.setBold(true);
2142 const QFontMetrics fmBold(fontBold);
2143 width += fmBold.horizontalAdvance(menuItem->text) - fm.horizontalAdvance(menuItem->text);
2144 }
2145 width += checkcol;
2146 width += 2 * QWindowsStylePrivate::windowsItemFrame;
2147 if (!menuItem->text.isEmpty()) {
2148 width += QWindowsStylePrivate::windowsItemHMargin;
2149 width += QWindowsStylePrivate::windowsRightBorder;
2150 }
2151 contentSize = QSize(width, height);
2152 }
2153 break;
2154#endif // QT_CONFIG(menu)
2155#if QT_CONFIG(spinbox)
2156 case QStyle::CT_SpinBox: {
2157 if (const auto *spinBoxOpt = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
2158 // Add button + frame widths
2159 const qreal dpi = QStyleHelper::dpi(option);
2160 const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons);
2161 const int margins = 8;
2162 const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0;
2163 const int frameWidth = spinBoxOpt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth,
2164 spinBoxOpt, widget) : 0;
2165
2166 contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth);
2167 }
2168 break;
2169 }
2170#endif
2171 case CT_ComboBox:
2172 if (const auto *comboBoxOpt = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2173 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
2174 contentSize += QSize(4, 4); // default win11 style margins
2175 if (comboBoxOpt->subControls & SC_ComboBoxArrow)
2176 contentSize += QSize(8, 0); // arrow margins
2177 }
2178 break;
2179 case CT_HeaderSection:
2180 // windows vista does not honor the indicator (as it was drawn above the text, not on the
2181 // side) so call QWindowsStyle::styleHint directly to get the correct size hint
2182 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
2183 break;
2184 case CT_RadioButton:
2185 case CT_CheckBox:
2186 // the indicator needs 2px more in width when there is no text, not needed when
2187 // the style draws the text
2188 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2189 if (size.width() == 0)
2190 contentSize.rwidth() += 2;
2191 break;
2192 case CT_PushButton:
2193 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2194 contentSize.rwidth() += 2 * 2; // the CE_PushButtonBevel draws a rounded rect with
2195 // QMargins(2, 2, 2, 2) removed
2196 break;
2197 default:
2198 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2199 break;
2200 }
2201
2202 return contentSize;
2203}
2204
2205
2206/*!
2207 \internal
2208 */
2209int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
2210{
2211 int res = 0;
2212
2213 switch (metric) {
2214 case QStyle::PM_IndicatorWidth:
2215 case QStyle::PM_IndicatorHeight:
2216 case QStyle::PM_ExclusiveIndicatorWidth:
2217 case QStyle::PM_ExclusiveIndicatorHeight:
2218 res = 16;
2219 break;
2220 case PM_SliderThickness: // full height of a slider
2221 if (auto opt = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2222 // hard-coded in qslider.cpp, but we need a little bit more
2223 constexpr auto TickSpace = 5;
2224 if (opt->tickPosition & QSlider::TicksAbove)
2225 res += 6 - TickSpace;
2226 if (opt->tickPosition & QSlider::TicksBelow)
2227 res += 6 - TickSpace;
2228 }
2229 Q_FALLTHROUGH();
2230 case PM_SliderControlThickness: // size of the control handle
2231 case PM_SliderLength: // same because handle is a circle with r=8
2232 res += 2 * 8;
2233 break;
2234 case QStyle::PM_TitleBarButtonIconSize:
2235 res = 16;
2236 break;
2237 case QStyle::PM_TitleBarButtonSize:
2238 res = 32;
2239 break;
2240 case QStyle::PM_ScrollBarExtent:
2241 res = 12;
2242 break;
2243 case QStyle::PM_SubMenuOverlap:
2244 res = -1;
2245 break;
2246 default:
2247 res = QWindowsVistaStyle::pixelMetric(metric, option, widget);
2248 }
2249
2250 return res;
2251}
2252
2253void QWindows11Style::polish(QWidget* widget)
2254{
2255#if QT_CONFIG(commandlinkbutton)
2256 if (!qobject_cast<QCommandLinkButton *>(widget))
2257#endif // QT_CONFIG(commandlinkbutton)
2258 QWindowsVistaStyle::polish(widget);
2259
2260 const bool isScrollBar = qobject_cast<QScrollBar *>(widget);
2261 const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
2262#if QT_CONFIG(menubar)
2263 if (qobject_cast<QMenuBar *>(widget)) {
2264 constexpr int itemHeight = 32;
2265 if (widget->maximumHeight() < itemHeight) {
2266 widget->setProperty("_q_original_menubar_maxheight", widget->maximumHeight());
2267 widget->setMaximumHeight(itemHeight);
2268 }
2269 }
2270#endif
2271 if (isScrollBar || qobject_cast<QMenu *>(widget) || comboBoxContainer) {
2272 bool wasCreated = widget->testAttribute(Qt::WA_WState_Created);
2273 bool layoutDirection = widget->testAttribute(Qt::WA_RightToLeft);
2274 widget->setAttribute(Qt::WA_OpaquePaintEvent,false);
2275 widget->setAttribute(Qt::WA_TranslucentBackground);
2276 if (!isScrollBar)
2277 widget->setWindowFlag(Qt::FramelessWindowHint);
2278 widget->setWindowFlag(Qt::NoDropShadowWindowHint);
2279 widget->setAttribute(Qt::WA_RightToLeft, layoutDirection);
2280 widget->setAttribute(Qt::WA_WState_Created, wasCreated);
2281 if (!isScrollBar) {
2282 bool inGraphicsView = widget->graphicsProxyWidget() != nullptr;
2283 if (!inGraphicsView && comboBoxContainer && comboBoxContainer->parentWidget())
2284 inGraphicsView = comboBoxContainer->parentWidget()->graphicsProxyWidget() != nullptr;
2285 if (!inGraphicsView) { // for menus and combobox containers...
2286 QGraphicsDropShadowEffect *dropshadow = new QGraphicsDropShadowEffect(widget);
2287 dropshadow->setBlurRadius(3);
2288 dropshadow->setXOffset(3);
2289 dropshadow->setYOffset(3);
2290 widget->setGraphicsEffect(dropshadow);
2291 }
2292 }
2293 } else if (QComboBox* cb = qobject_cast<QComboBox*>(widget)) {
2294 if (cb->isEditable()) {
2295 QLineEdit *le = cb->lineEdit();
2296 le->setFrame(false);
2297 }
2298 } else if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2299 scrollarea
2300 && !qobject_cast<QGraphicsView *>(widget)
2301#if QT_CONFIG(mdiarea)
2302 && !qobject_cast<QMdiArea *>(widget)
2303#endif
2304 ) {
2305 if (scrollarea->frameShape() == QFrame::StyledPanel) {
2306 const auto vp = scrollarea->viewport();
2307 const bool isAutoFillBackground = vp->autoFillBackground();
2308 const bool isStyledBackground = vp->testAttribute(Qt::WA_StyledBackground);
2309 vp->setProperty("_q_original_autofill_background", isAutoFillBackground);
2310 vp->setProperty("_q_original_styled_background", isStyledBackground);
2311 vp->setAutoFillBackground(false);
2312 vp->setAttribute(Qt::WA_StyledBackground, true);
2313 }
2314 // QTreeView & QListView are already set in the base windowsvista style
2315 if (auto table = qobject_cast<QTableView *>(widget))
2316 table->viewport()->setAttribute(Qt::WA_Hover, true);
2317 }
2318}
2319
2320void QWindows11Style::unpolish(QWidget *widget)
2321{
2322#if QT_CONFIG(commandlinkbutton)
2323 if (!qobject_cast<QCommandLinkButton *>(widget))
2324#endif // QT_CONFIG(commandlinkbutton)
2325 QWindowsVistaStyle::unpolish(widget);
2326
2327#if QT_CONFIG(menubar)
2328 if (qobject_cast<QMenuBar *>(widget) && !widget->property("_q_original_menubar_maxheight").isNull()) {
2329 widget->setMaximumHeight(widget->property("_q_original_menubar_maxheight").toInt());
2330 widget->setProperty("_q_original_menubar_maxheight", QVariant());
2331 }
2332#endif
2333
2334 if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2335 scrollarea
2336#if QT_CONFIG(mdiarea)
2337 && !qobject_cast<QMdiArea *>(widget)
2338#endif
2339 ) {
2340 const auto vp = scrollarea->viewport();
2341 const auto wasAutoFillBackground = vp->property("_q_original_autofill_background").toBool();
2342 vp->setAutoFillBackground(wasAutoFillBackground);
2343 vp->setProperty("_q_original_autofill_background", QVariant());
2344 const auto origStyledBackground = vp->property("_q_original_styled_background").toBool();
2345 vp->setAttribute(Qt::WA_StyledBackground, origStyledBackground);
2346 vp->setProperty("_q_original_styled_background", QVariant());
2347 }
2348}
2349
2350/*
2351The colors for Windows 11 are taken from the official WinUI3 Figma style at
2352https://www.figma.com/community/file/1159947337437047524
2353*/
2354#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE)
2355 if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged)
2356 result.setColor(GROUP, ROLE, VALUE)
2357
2358static void populateLightSystemBasePalette(QPalette &result)
2359{
2360 static QString oldStyleSheet;
2361 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2362
2363 constexpr QColor textColor = QColor(0x00,0x00,0x00,0xE4);
2364 constexpr QColor textDisabled = QColor(0x00, 0x00, 0x00, 0x5C);
2365 constexpr QColor btnFace = QColor(0xFF, 0xFF, 0xFF, 0xB3);
2366 constexpr QColor base = QColor(0xFF, 0xFF, 0xFF, 0xFF);
2367 constexpr QColor alternateBase = QColor(0x00, 0x00, 0x00, 0x09);
2368 const QColor btnHighlight = result.accent().color();
2369 const QColor btnColor = result.button().color();
2370
2371 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Highlight, btnHighlight);
2372 SET_IF_UNRESOLVED(QPalette::Active, QPalette::WindowText, textColor);
2373 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Button, btnFace);
2374 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Light, btnColor.lighter(150));
2375 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Dark, btnColor.darker(200));
2376 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Mid, btnColor.darker(150));
2377 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Text, textColor);
2378 SET_IF_UNRESOLVED(QPalette::Active, QPalette::BrightText, btnHighlight);
2379 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Base, base);
2380 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2381 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ButtonText, textColor);
2382 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Midlight, btnColor.lighter(125));
2383 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Shadow, Qt::black);
2384 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipBase, result.window().color());
2385 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipText, result.windowText().color());
2386 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2387
2388 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, btnHighlight);
2389 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, textColor);
2390 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, btnFace);
2391 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, btnColor.lighter(150));
2392 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, btnColor.darker(200));
2393 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Mid, btnColor.darker(150));
2394 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, textColor);
2395 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::BrightText, btnHighlight);
2396 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Base, base);
2397 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2398 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ButtonText, textColor);
2399 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Midlight, btnColor.lighter(125));
2400 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Shadow, Qt::black);
2401 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipBase, result.window().color());
2402 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipText, result.windowText().color());
2403 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2404
2405 result.setColor(QPalette::Disabled, QPalette::WindowText, textDisabled);
2406
2407 if (result.midlight() == result.button())
2408 result.setColor(QPalette::Midlight, btnColor.lighter(110));
2409 oldStyleSheet = qApp->styleSheet();
2410}
2411
2412static void populateDarkSystemBasePalette(QPalette &result)
2413{
2414 static QString oldStyleSheet;
2415 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2416
2417 constexpr QColor alternateBase = QColor(0xFF, 0xFF, 0xFF, 0x0F);
2418
2419 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2420
2421 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2422
2423 oldStyleSheet = qApp->styleSheet();
2424}
2425
2426/*!
2427 \internal
2428 */
2429void QWindows11Style::polish(QPalette& result)
2430{
2431 highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown;
2432 colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
2433
2434 if (!highContrastTheme && colorSchemeIndex == 0)
2435 populateLightSystemBasePalette(result);
2436 else if (!highContrastTheme && colorSchemeIndex == 1)
2437 populateDarkSystemBasePalette(result);
2438
2439 const bool styleSheetChanged = false; // so the macro works
2440
2441 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color());
2442 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color());
2443 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color());
2444 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color());
2445 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color());
2446 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color());
2447 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color());
2448 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color());
2449 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color());
2450
2451 if (highContrastTheme)
2452 result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color());
2453
2454 auto *d = const_cast<QWindows11StylePrivate *>(d_func());
2455 d->m_titleBarMinIcon = QIcon();
2456 d->m_titleBarMaxIcon = QIcon();
2457 d->m_titleBarCloseIcon = QIcon();
2458 d->m_titleBarNormalIcon = QIcon();
2459}
2460
2461QColor QWindows11Style::calculateAccentColor(const QStyleOption *option) const
2462{
2463 using namespace StyleOptionHelper;
2464 if (isDisabled(option))
2465 return winUI3Color(fillAccentDisabled);
2466 const auto alphaColor = isPressed(option) ? fillAccentTertiary
2467 : isHover(option) ? fillAccentSecondary
2468 : fillAccentDefault;
2469 const auto alpha = winUI3Color(alphaColor);
2470 QColor col = option->palette.accent().color();
2471 col.setAlpha(alpha.alpha());
2472 return col;
2473}
2474
2475QPen QWindows11Style::borderPenControlAlt(const QStyleOption *option) const
2476{
2477 using namespace StyleOptionHelper;
2478 if (isChecked(option))
2479 return Qt::NoPen; // same color as fill color, so no pen needed
2480 if (highContrastTheme)
2481 return option->palette.buttonText().color();
2482 if (isDisabled(option) || isPressed(option))
2483 return winUI3Color(frameColorStrongDisabled);
2484 return winUI3Color(frameColorStrong);
2485}
2486
2487QBrush QWindows11Style::controlFillBrush(const QStyleOption *option, ControlType controlType) const
2488{
2489 using namespace StyleOptionHelper;
2490 static constexpr WINUI3Color colorEnums[2][4] = {
2491 // Light & Dark Control
2494 // Light & Dark Control Alt
2497 };
2498
2499 if (option->palette.isBrushSet(QPalette::Current, QPalette::Button))
2500 return option->palette.button();
2501
2502 if (!isChecked(option) && isAutoRaise(option))
2503 return Qt::NoBrush;
2504
2505 // checked is the same for Control (Buttons) and Control Alt (Radiobuttons/Checkboxes)
2506 if (isChecked(option))
2507 return calculateAccentColor(option);
2508
2509 const auto state = calcControlState(option);
2510 return winUI3Color(colorEnums[int(controlType)][int(state)]);
2511}
2512
2513QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::ColorRole role) const
2514{
2515 using namespace StyleOptionHelper;
2516 static constexpr WINUI3Color colorEnums[2][4] = {
2517 // Control, unchecked
2519 // Control, checked
2521 };
2522
2523 if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText))
2524 return option->palette.buttonText().color();
2525
2526 const int colorIndex = isChecked(option) ? 1 : 0;
2527 const auto state = calcControlState(option);
2528 const auto alpha = winUI3Color(colorEnums[colorIndex][int(state)]);
2529 QColor col = option->palette.color(role);
2530 col.setAlpha(alpha.alpha());
2531 return col;
2532}
2533
2534void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const
2535{
2536 const bool isHovered = o->state & State_MouseOver;
2537 const auto frameCol = highContrastTheme
2538 ? o->palette.color(isHovered ? QPalette::Accent
2539 : QPalette::ButtonText)
2540 : winUI3Color(frameColorLight);
2541 drawRoundedRect(p, rect, frameCol, Qt::NoBrush);
2542
2543 if (!isEditable)
2544 return;
2545
2546 QPainterStateGuard psg(p);
2547 p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 0.5, 0, -1)));
2548 const bool hasFocus = o->state & State_HasFocus;
2549 const auto underlineCol = hasFocus
2550 ? o->palette.color(QPalette::Accent)
2551 : colorSchemeIndex == 0 ? QColor(0x80, 0x80, 0x80)
2552 : QColor(0xa0, 0xa0, 0xa0);
2553 const auto penUnderline = QPen(underlineCol, hasFocus ? 2 : 1);
2554 drawRoundedRect(p, rect, penUnderline, Qt::NoBrush);
2555}
2556
2557QColor QWindows11Style::winUI3Color(enum WINUI3Color col) const
2558{
2559 return WINUI3Colors[colorSchemeIndex][col];
2560}
2561
2562#undef SET_IF_UNRESOLVED
2563
2564QT_END_NAMESPACE
friend class QPainter
QPainter * painter() const
Returns the paint engine's painter.
\inmodule QtCore\reentrant
Definition qpoint.h:231
\inmodule QtCore\reentrant
Definition qpoint.h:29
bool isDisabled(const QStyleOption *option)
ControlState calcControlState(const QStyleOption *option)
bool isChecked(const QStyleOption *option)
bool isPressed(const QStyleOption *option)
bool isHover(const QStyleOption *option)
bool isAutoRaise(const QStyleOption *option)
#define qApp
#define ChevronDownMed
#define ChevronRightMed
static constexpr std::array< QColor, 32 > WINUI3ColorsLight
#define CaretLeftSolid8
static void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush)
#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE)
#define CaretUpSolid8
#define ChromeMinimize
#define Help
static constexpr QColor shellCaptionCloseFillColorSecondary(0xC4, 0x2B, 0x1C, 0xE6)
static qreal sliderInnerRadius(QStyle::State state, bool insideHandle)
#define CaretRightSolid8
static qreal radioButtonInnerRadius(int state)
static constexpr QColor shellCaptionCloseFillColorPrimary(0xC4, 0x2B, 0x1C, 0xFF)
#define AcceptMedium
static constexpr std::array< std::array< QColor, 32 >, 2 > WINUI3Colors
#define ChevronLeftMed
static void populateLightSystemBasePalette(QPalette &result)
static constexpr int percentToAlpha(double percent)
static void populateDarkSystemBasePalette(QPalette &result)
#define ChevronUpSmall
static constexpr QColor shellCaptionCloseTextFillColorPrimary(0xFF, 0xFF, 0xFF, 0xFF)
#define Dash12
#define ChromeClose
static constexpr int topLevelRoundingRadius
static constexpr QColor shellCaptionCloseTextFillColorSecondary(0xFF, 0xFF, 0xFF, 0xB3)
#define ChromeRestore
#define CaretDownSolid8
static constexpr std::array< QColor, 32 > WINUI3ColorsDark
#define ChromeMaximize
#define ChevronDownSmall
static constexpr int secondLevelRoundingRadius
WINUI3Color
@ fillControlAltSecondary
@ fillControlTertiary
@ textOnAccentPrimary
@ fillControlDefault
@ fillControlDisabled
@ subtleHighlightColor
@ textPrimary
@ textDisabled
@ fillControlSecondary
@ textSecondary
@ textOnAccentSecondary
@ textOnAccentDisabled
@ fillControlAltQuarternary
@ fillControlAltTertiary
@ fillControlAltDisabled