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 <QtCore/qoperatingsystemversion.h>
18#include <QtWidgets/qcombobox.h>
19#if QT_CONFIG(commandlinkbutton)
20#include <QtWidgets/qcommandlinkbutton.h>
21#endif
22#include <QtWidgets/qgraphicsview.h>
23#include <QtWidgets/qlistview.h>
24#include <QtWidgets/qmenu.h>
25#if QT_CONFIG(mdiarea)
26#include <QtWidgets/qmdiarea.h>
27#endif
28#include <QtWidgets/qplaintextedit.h>
29#include <QtWidgets/qtextedit.h>
30#include <QtWidgets/qtreeview.h>
31#if QT_CONFIG(datetimeedit)
32# include <QtWidgets/qdatetimeedit.h>
33#endif
34#if QT_CONFIG(tabwidget)
35# include <QtWidgets/qtabwidget.h>
36#endif
37#if QT_CONFIG(menubar)
38# include <QtWidgets/qmenubar.h>
39#endif
40#if QT_CONFIG(tooltip)
41#include "private/qtooltip_p.h"
42#endif
43#include "qdrawutil.h"
44#include <chrono>
45#include <dwmapi.h>
46
47QT_BEGIN_NAMESPACE
48
49using namespace Qt::StringLiterals;
50
51static constexpr int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners
52static constexpr int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners
53static constexpr int contentItemHMargin = 4; // margin between content items (e.g. text and icon)
54static constexpr int contentHMargin = 2 * 3; // margin between rounded border and content (= rounded border margin * 3)
55
56
58{
59inline bool isChecked(const QStyleOption *option)
60{
61 return option->state.testAnyFlags(QStyle::State_On | QStyle::State_NoChange);
62}
63inline bool isDisabled(const QStyleOption *option)
64{
65 return !option->state.testFlag(QStyle::State_Enabled);
66}
67inline bool isPressed(const QStyleOption *option)
68{
69 return option->state.testFlag(QStyle::State_Sunken);
70}
71inline bool isHover(const QStyleOption *option)
72{
73 return option->state.testFlag(QStyle::State_MouseOver);
74}
75inline bool isAutoRaise(const QStyleOption *option)
76{
77 return option->state.testFlag(QStyle::State_AutoRaise);
78}
79inline bool hasFocus(const QStyleOption *option)
80{
81 return option->state.testFlag(QStyle::State_HasFocus);
82}
84inline ControlState calcControlState(const QStyleOption *option)
85{
86 if (isDisabled(option))
88 if (isPressed(option))
90 if (isHover(option))
93};
94
95} // namespace StyleOptionHelper
96
97template <typename R, typename P, typename B>
98static inline void drawRoundedRect(QPainter *p,
99 R &&rect,
100 P &&pen,
101 B &&brush,
102 int radius = secondLevelRoundingRadius)
103{
104 p->setPen(pen);
105 p->setBrush(brush);
106 p->drawRoundedRect(rect, radius, radius);
107}
108
109static constexpr int percentToAlpha(double percent)
110{
111 return qRound(percent * 255. / 100.);
112}
113
114static constexpr std::array<QColor, 37> WINUI3ColorsLight {
115 QColor(0x00,0x00,0x00,percentToAlpha(3.73)), // subtleHighlightColor (fillSubtleSecondary)
116 QColor(0x00,0x00,0x00,percentToAlpha(2.41)), // subtlePressedColor (fillSubtleTertiary)
117 QColor(0x00,0x00,0x00,0x0F), //frameColorLight
118 QColor(0x00,0x00,0x00,percentToAlpha(60.63)), //frameColorStrong
119 QColor(0x00,0x00,0x00,percentToAlpha(21.69)), //frameColorStrongDisabled
120 QColor(0x00,0x00,0x00,0x72), //controlStrongFill
121 QColor(0x00,0x00,0x00,0x29), //controlStrokeSecondary
122 QColor(0x00,0x00,0x00,0x14), //controlStrokePrimary
123 QColor(0xFF,0xFF,0xFF,0xFF), //menuPanelFill
124 QColor(0x00,0x00,0x00,0x66), //controlStrokeOnAccentSecondary
125 QColor(0xFF,0xFF,0xFF,0xFF), //controlFillSolid
126 QColor(0x75,0x75,0x75,0x66), //surfaceStroke
127 QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameInnerStroke
128 QColor(0x00,0x00,0x00,0xFF), //focusFrameOuterStroke
129 QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // fillControlDefault
130 QColor(0xF9,0xF9,0xF9,percentToAlpha(50)), // fillControlSecondary
131 QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlTertiary
132 QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlDisabled
133 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // fillControlInputActive
134 QColor(0x00,0x00,0x00,percentToAlpha(2.41)), // fillControlAltSecondary
135 QColor(0x00,0x00,0x00,percentToAlpha(5.78)), // fillControlAltTertiary
136 QColor(0x00,0x00,0x00,percentToAlpha(9.24)), // fillControlAltQuarternary
137 QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // fillControlAltDisabled
138 QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault
139 QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary
140 QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary
141 QColor(0x00,0x00,0x00,percentToAlpha(21.69)), // fillAccentDisabled
142 QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // fillMicaAltDefault
143 QColor(0xFF,0xFF,0xFF,percentToAlpha(0)), // fillMicaAltTransparent
144 QColor(0x00,0x00,0x00,percentToAlpha(3.73)), // fillMicaAltSecondary
145 QColor(0x00,0x00,0x00,percentToAlpha(89.56)), // textPrimary
146 QColor(0x00,0x00,0x00,percentToAlpha(60.63)), // textSecondary
147 QColor(0x00,0x00,0x00,percentToAlpha(36.14)), // textDisabled
148 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentPrimary
149 QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // textOnAccentSecondary
150 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentDisabled
151 QColor(0x00,0x00,0x00,percentToAlpha(8.03)), // dividerStrokeDefault
152};
153
154static constexpr std::array<QColor, 37> WINUI3ColorsDark {
155 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // subtleHighlightColor (fillSubtleSecondary)
156 QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // subtlePressedColor (fillSubtleTertiary)
157 QColor(0xFF,0xFF,0xFF,0x12), //frameColorLight
158 QColor(0xFF,0xFF,0xFF,percentToAlpha(60.47)), //frameColorStrong
159 QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), //frameColorStrongDisabled
160 QColor(0xFF,0xFF,0xFF,0x8B), //controlStrongFill
161 QColor(0xFF,0xFF,0xFF,0x18), //controlStrokeSecondary
162 QColor(0xFF,0xFF,0xFF,0x12), //controlStrokePrimary
163 QColor(0x0F,0x0F,0x0F,0xFF), //menuPanelFill
164 QColor(0xFF,0xFF,0xFF,0x14), //controlStrokeOnAccentSecondary
165 QColor(0x45,0x45,0x45,0xFF), //controlFillSolid
166 QColor(0x75,0x75,0x75,0x66), //surfaceStroke
167 QColor(0x00,0x00,0x00,0xFF), //focusFrameInnerStroke
168 QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameOuterStroke
169 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // fillControlDefault
170 QColor(0xFF,0xFF,0xFF,percentToAlpha(8.37)), // fillControlSecondary
171 QColor(0xFF,0xFF,0xFF,percentToAlpha(3.26)), // fillControlTertiary
172 QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlDisabled
173 QColor(0x1E,0x1E,0x1E,percentToAlpha(70)), // fillControlInputActive
174 QColor(0x00,0x00,0x00,percentToAlpha(10.0)), // fillControlAltDefault
175 QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlAltSecondary
176 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.98)), // fillControlAltTertiafillCy
177 QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // controlAltDisabled
178 QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault
179 QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary
180 QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary
181 QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), // fillAccentDisabled
182 QColor(0x3A,0x3A,0x3A,percentToAlpha(45)), // fillMicaAltDefault
183 QColor(0x00,0x00,0x00,percentToAlpha(0)), // fillMicaAltTransparent
184 QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // fillMicaAltSecondary
185 QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textPrimary
186 QColor(0xFF,0xFF,0xFF,percentToAlpha(78.6)), // textSecondary
187 QColor(0xFF,0xFF,0xFF,percentToAlpha(36.28)), // textDisabled
188 QColor(0x00,0x00,0x00,percentToAlpha(100)), // textOnAccentPrimary
189 QColor(0x00,0x00,0x00,percentToAlpha(70)), // textOnAccentSecondary
190 QColor(0xFF,0xFF,0xFF,percentToAlpha(53.02)), // textOnAccentDisabled
191 QColor(0xFF,0xFF,0xFF,percentToAlpha(8.37)), // dividerStrokeDefault
192};
193
194static constexpr std::array<std::array<QColor,37>, 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
234static qreal radioButtonInnerRadius(QStyle::State state)
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 const bool isEnabled = state & QStyle::State_Enabled;
251 if (isEnabled) {
252 if (state & QStyle::State_Sunken)
253 return 0.29;
254 else if (insideHandle)
255 return 0.71;
256 }
257 return 0.43;
258}
259/*!
260 \class QWindows11Style
261 \brief The QWindows11Style class provides a look and feel suitable for applications on Microsoft Windows 11.
262 \since 6.6
263 \ingroup appearance
264 \inmodule QtWidgets
265 \internal
266
267 \warning This style is only available on the Windows 11 platform and above.
268
269 \sa QWindows11Style QWindowsVistaStyle, QMacStyle, QFusionStyle
270*/
271QWindows11StylePrivate::QWindows11StylePrivate()
272{
273 nativeRoundedTopLevelWindows =
274 QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11_21H2;
275}
276
277/*!
278 Constructs a QWindows11Style object.
279*/
280QWindows11Style::QWindows11Style() : QWindows11Style(*new QWindows11StylePrivate)
281{
282}
283
284/*!
285 \internal
286 Constructs a QWindows11Style object.
287*/
288QWindows11Style::QWindows11Style(QWindows11StylePrivate &dd) : QWindowsVistaStyle(dd)
289{
290 Q_D(QWindows11Style);
291 const QStringList assetFontFamilies{ "Segoe Fluent Icons"_L1, "Segoe MDL2 Assets"_L1 };
292 d->assetFont = QFont(assetFontFamilies);
293 d->assetFont.setStyleStrategy(QFont::NoFontMerging);
294 highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown;
295 colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
296}
297
298/*!
299 Destructor.
300*/
301QWindows11Style::~QWindows11Style() = default;
302
303/*!
304 \internal
305 see drawPrimitive for comments on the animation support
306
307 */
308void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
309 QPainter *painter, const QWidget *widget) const
310{
311 QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func());
312
313 const auto drawTitleBarButton = [&](ComplexControl control, SubControl sc, Icon ico) {
314 using namespace StyleOptionHelper;
315 const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget);
316 if (buttonRect.isValid()) {
317 const bool hover = option->activeSubControls == sc && isHover(option);
318 if (hover)
319 painter->fillRect(buttonRect, winUI3Color(subtleHighlightColor));
320 painter->setPen(option->palette.color(QPalette::WindowText));
321 painter->drawText(buttonRect, Qt::AlignCenter, fluentIcon(ico));
322 }
323 };
324 const auto drawTitleBarCloseButton = [&](ComplexControl control, SubControl sc, Icon ico) {
325 using namespace StyleOptionHelper;
326 const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget);
327 if (buttonRect.isValid()) {
328 const auto state = (option->activeSubControls == sc) ? calcControlState(option)
329 : ControlState::Normal;
330 QPen pen;
331 switch (state) {
333 painter->fillRect(buttonRect, shellCaptionCloseFillColorPrimary);
334 pen = shellCaptionCloseTextFillColorPrimary;
335 break;
337 painter->fillRect(buttonRect, shellCaptionCloseFillColorSecondary);
338 pen = shellCaptionCloseTextFillColorSecondary;
339 break;
340 case ControlState::Disabled:
341 case ControlState::Normal:
342 pen = option->palette.color(QPalette::WindowText);
343 break;
344 }
345 painter->setPen(pen);
346 painter->drawText(buttonRect, Qt::AlignCenter, fluentIcon(ico));
347 }
348 };
349
350
351 State state = option->state;
352 SubControls sub = option->subControls;
353 State flags = option->state;
354 if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
355 flags |= State_MouseOver;
356
357 QPainterStateGuard psg(painter);
358 painter->setRenderHint(QPainter::Antialiasing);
359 if (d->transitionsEnabled() && option->styleObject) {
360 if (control == CC_Slider) {
361 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
362 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
363
364 QRectF thumbRect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
365 const qreal outerRadius = qMin(8.0, (slider->orientation == Qt::Horizontal ? thumbRect.height() / 2.0 : thumbRect.width() / 2.0) - 1);
366 bool isInsideHandle = option->activeSubControls == SC_SliderHandle;
367
368 bool oldIsInsideHandle = styleObject->property("_q_insidehandle").toBool();
369 State oldState = State(styleObject->property("_q_stylestate").toInt());
370 SubControls oldActiveControls = SubControls(styleObject->property("_q_stylecontrols").toInt());
371
372 QRectF oldRect = styleObject->property("_q_stylerect").toRect();
373 styleObject->setProperty("_q_insidehandle", isInsideHandle);
374 styleObject->setProperty("_q_stylestate", int(state));
375 styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls));
376 styleObject->setProperty("_q_stylerect", option->rect);
377 if (option->styleObject->property("_q_end_radius").isNull())
378 option->styleObject->setProperty("_q_end_radius", outerRadius * 0.43);
379
380 bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken)
381 || (oldIsInsideHandle != isInsideHandle)
382 || (oldActiveControls != option->activeSubControls))
383 && state & State_Enabled);
384
385 if (oldRect != option->rect) {
386 doTransition = false;
387 d->stopAnimation(styleObject);
388 styleObject->setProperty("_q_inner_radius", outerRadius * 0.43);
389 }
390
391 if (doTransition) {
392 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
393 t->setStartValue(styleObject->property("_q_inner_radius").toFloat());
394 t->setEndValue(outerRadius * sliderInnerRadius(state, isInsideHandle));
395 styleObject->setProperty("_q_end_radius", t->endValue());
396
397 t->setStartTime(d->animationTime());
398 t->setDuration(150);
399 d->startAnimation(t);
400 }
401 }
402 } else if (control == CC_ScrollBar) {
403 QObject *styleObject = option->styleObject;
404 State oldState = State(styleObject->property("_q_stylestate").toInt());
405 const bool wasMouseOver = oldState.testFlag(State_MouseOver);
406 const bool isMouseOver = state.testFlag(State_MouseOver);
407
408 if (wasMouseOver != isMouseOver) {
409 styleObject->setProperty("_q_stylestate", int(state));
410 auto anim = qobject_cast<QNumberStyleAnimation *>(d->animation(styleObject));
411 constexpr int durationMS = 100;
412 qreal startValue = isMouseOver ? 0 : 1;
413 int curDurationMS = durationMS;
414 if (anim) {
415 startValue = anim->currentValue();
416 curDurationMS = durationMS - (anim->duration() - anim->currentLoopTime());
417 d->stopAnimation(option->styleObject);
418 }
419 anim = new QNumberStyleAnimation(styleObject);
420 anim->setStartValue(startValue);
421 anim->setEndValue(isMouseOver ? 1 : 0);
422 anim->setDuration(curDurationMS);
423 d->startAnimation(anim);
424 }
425 }
426 }
427
428 switch (control) {
429#if QT_CONFIG(spinbox)
430 case CC_SpinBox:
431 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
432 QCachedPainter cp(painter, QLatin1StringView("win11_spinbox") % HexString<uint8_t>(colorSchemeIndex),
433 sb, sb->rect.size());
434 if (cp.needsPainting()) {
435 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
436 drawRoundedRect(cp.painter(), frameRect, Qt::NoPen, inputFillBrush(option, widget));
437
438 if (sb->frame && (sub & SC_SpinBoxFrame))
439 drawLineEditFrame(cp.painter(), frameRect, option);
440
441 const auto drawUpDown = [&](QStyle::SubControl sc,
442 QAbstractSpinBox::StepEnabledFlag flag) {
443 const bool isEnabled =
444 state.testFlag(QStyle::State_Enabled) && sb->stepEnabled.testFlag(flag);
445 const bool isUp = sc == SC_SpinBoxUp;
446 const QRect rect = proxy()->subControlRect(CC_SpinBox, option, sc, widget);
447 if (isEnabled && sb->activeSubControls & sc)
448 drawRoundedRect(cp.painter(), rect.adjusted(1, 1, -1, -2), Qt::NoPen,
449 winUI3Color(subtleHighlightColor));
450
451 cp->setFont(d->assetFont);
452 cp->setPen(sb->palette.color(isEnabled ? QPalette::Active : QPalette::Disabled,
453 QPalette::ButtonText));
454 cp->setBrush(Qt::NoBrush);
455 cp->drawText(rect, Qt::AlignCenter, fluentIcon(isUp ? Icon::ChevronUp : Icon::ChevronDown));
456 };
457 if (sub & SC_SpinBoxUp)
458 drawUpDown(SC_SpinBoxUp, QAbstractSpinBox::StepUpEnabled);
459 if (sub & SC_SpinBoxDown)
460 drawUpDown(SC_SpinBoxDown, QAbstractSpinBox::StepDownEnabled);
461 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
462 QStyleOptionFocusRect fropt;
463 fropt.QStyleOption::operator=(*option);
464 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, cp.painter(), widget);
465 }
466 }
467 }
468 break;
469#endif // QT_CONFIG(spinbox)
470#if QT_CONFIG(slider)
471 case CC_Slider:
472 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
473 const auto &slrect = slider->rect;
474 const bool isHorizontal = slider->orientation == Qt::Horizontal;
475 const QRectF handleRect(proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget));
476 const QPointF handleCenter(handleRect.center());
477
478 if (sub & SC_SliderGroove) {
479 QRectF rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
480 QRectF leftRect;
481 QRectF rightRect;
482
483 if (isHorizontal) {
484 rect = QRectF(rect.left() + 2, rect.center().y() - 2, rect.width() - 2, 4);
485 leftRect = QRectF(rect.left(), rect.top(), handleCenter.x() - rect.left(),
486 rect.height());
487 rightRect = QRectF(handleCenter.x(), rect.top(),
488 rect.width() - handleCenter.x(),
489 rect.height());
490 } else {
491 rect = QRect(rect.center().x() - 2, rect.top() + 2, 4, rect.height() - 2);
492 leftRect = QRectF(rect.left(), rect.top(), rect.width(),
493 handleCenter.y() - rect.top());
494 rightRect = QRectF(rect.left(), handleCenter.y(), rect.width(),
495 rect.height() - handleCenter.y());
496 }
497 if (slider->upsideDown)
498 qSwap(leftRect, rightRect);
499
500 painter->setPen(Qt::NoPen);
501 painter->setBrush(calculateAccentColor(option));
502 painter->drawRoundedRect(leftRect,1,1);
503 painter->setBrush(WINUI3Colors[colorSchemeIndex][controlStrongFill]);
504 painter->drawRoundedRect(rightRect,1,1);
505 }
506 if (sub & SC_SliderTickmarks) {
507 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
508 int ticks = slider->tickPosition;
509 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
510 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
511 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
512 int interval = slider->tickInterval;
513 if (interval <= 0) {
514 interval = slider->singleStep;
515 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
516 available)
517 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
518 0, available) < 3)
519 interval = slider->pageStep;
520 }
521 if (!interval)
522 interval = 1;
523 int fudge = len / 2;
524 painter->setPen(slider->palette.text().color());
525 QVarLengthArray<QLineF, 32> lines;
526 int v = slider->minimum;
527 while (v <= slider->maximum + 1) {
528 if (v == slider->maximum + 1 && interval == 1)
529 break;
530 const int v_ = qMin(v, slider->maximum);
531 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
532 int pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, v_,
533 available, slider->upsideDown);
534 pos += fudge;
535 if (isHorizontal) {
536 if (ticks & QSlider::TicksAbove) {
537 lines.append(QLineF(pos, tickOffset - 0.5,
538 pos, tickOffset - tickLength - 0.5));
539 }
540
541 if (ticks & QSlider::TicksBelow) {
542 lines.append(QLineF(pos, tickOffset + thickness + 0.5,
543 pos, tickOffset + thickness + tickLength + 0.5));
544 }
545 } else {
546 if (ticks & QSlider::TicksAbove) {
547 lines.append(QLineF(tickOffset - 0.5, pos,
548 tickOffset - tickLength - 0.5, pos));
549 }
550
551 if (ticks & QSlider::TicksBelow) {
552 lines.append(QLineF(tickOffset + thickness + 0.5, pos,
553 tickOffset + thickness + tickLength + 0.5, pos));
554 }
555 }
556 // in the case where maximum is max int
557 int nextInterval = v + interval;
558 if (nextInterval < v)
559 break;
560 v = nextInterval;
561 }
562 if (!lines.isEmpty()) {
563 QPainterStateGuard psg(painter);
564 painter->translate(slrect.topLeft());
565 painter->drawLines(lines.constData(), lines.size());
566 }
567 }
568 if (sub & SC_SliderHandle) {
569 const qreal outerRadius = qMin(8.0, (isHorizontal ? handleRect.height() / 2.0 : handleRect.width() / 2.0) - 1);
570 float innerRadius = outerRadius * 0.43;
571
572 if (option->styleObject) {
573 const QNumberStyleAnimation* animation = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
574 if (animation != nullptr) {
575 innerRadius = animation->currentValue();
576 option->styleObject->setProperty("_q_inner_radius", innerRadius);
577 } else {
578 bool isInsideHandle = option->activeSubControls == SC_SliderHandle;
579 innerRadius = outerRadius * sliderInnerRadius(state, isInsideHandle);
580 }
581 }
582
583 painter->setPen(Qt::NoPen);
584 painter->setBrush(winUI3Color(controlFillSolid));
585 painter->drawEllipse(handleCenter, outerRadius, outerRadius);
586 painter->setBrush(calculateAccentColor(option));
587 painter->drawEllipse(handleCenter, innerRadius, innerRadius);
588
589 painter->setPen(winUI3Color(controlStrokeSecondary));
590 painter->setBrush(Qt::NoBrush);
591 painter->drawEllipse(handleCenter, outerRadius + 0.5, outerRadius + 0.5);
592 }
593 if (slider->state & State_HasFocus) {
594 QStyleOptionFocusRect fropt;
595 fropt.QStyleOption::operator=(*slider);
596 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
597 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
598 }
599 }
600 break;
601#endif
602#if QT_CONFIG(combobox)
603 case CC_ComboBox:
604 if (const QStyleOptionComboBox *combobox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
605 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
606 QStyleOption opt(*option);
607 opt.state.setFlag(QStyle::State_On, false);
608 drawRoundedRect(painter, frameRect, Qt::NoPen,
609 combobox->editable ? inputFillBrush(option, widget)
610 : controlFillBrush(&opt, ControlType::Control));
611
612 if (combobox->frame)
613 drawLineEditFrame(painter, frameRect, combobox, combobox->editable);
614
615 const bool hasFocus = state & State_HasFocus;
616
617 if (sub & SC_ComboBoxArrow) {
618 QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
619 painter->setFont(d->assetFont);
620 painter->setPen(controlTextColor(option, true));
621 painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
622 }
623 if (state & State_KeyboardFocusChange && hasFocus) {
624 QStyleOptionFocusRect fropt;
625 fropt.QStyleOption::operator=(*option);
626 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
627 }
628 }
629 break;
630#endif // QT_CONFIG(combobox)
631 case CC_ScrollBar:
632 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
633 const bool vertical = scrollbar->orientation == Qt::Vertical;
634 const bool isMouseOver = state & State_MouseOver;
635 const auto prx = proxy();
636
637 QPainterStateGuard psg(painter);
638
639 const auto anim = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
640 qreal widthFactor = isMouseOver ? 1.0 : 0.0;
641 if (anim)
642 widthFactor = anim->currentValue();
643
644 if (widthFactor > 0.6) {
645 const bool isRtl = option->direction == Qt::RightToLeft;
646
647 QFont f = QFont(d->assetFont);
648 f.setPointSize(6);
649 painter->setFont(f);
650 painter->setPen(winUI3Color(controlStrongFill));
651
652 const QRectF rectAdd =
653 prx->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
654 const auto strAdd = vertical
655 ? Icon::CaretDownSolid8
656 : (isRtl ? Icon::CaretLeftSolid8 : Icon::CaretRightSolid8);
657 painter->drawText(rectAdd, Qt::AlignCenter, fluentIcon(strAdd));
658
659 const QRectF rectSub =
660 prx->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
661 const auto strSub = vertical
662 ? Icon::CaretUpSolid8
663 : (isRtl ? Icon::CaretRightSolid8 : Icon::CaretLeftSolid8);
664 painter->drawText(rectSub, Qt::AlignCenter, fluentIcon(strSub));
665 }
666 if (sub & SC_ScrollBarSlider) {
667 QRectF rect = prx->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
668 const QPointF center = rect.center();
669 if (vertical) {
670 auto w = rect.width() / 2. * widthFactor;
671 if (w < 2)
672 w = 2;
673 const auto ofs = rect.width() / 2 - w;
674 rect.setWidth(w);
675 rect.moveCenter(QPointF(center.x() + ofs, center.y()));
676 }
677 else {
678 auto h = rect.height() / 2. * widthFactor;
679 if (h < 2)
680 h = 2;
681 const auto ofs = rect.height() / 2 - h;
682 rect.setHeight(h);
683 rect.moveCenter(QPointF(center.x(), center.y() + ofs));
684 }
685 drawRoundedRect(painter, rect, Qt::NoPen, winUI3Color(controlStrongFill), 3);
686 }
687 }
688 break;
689 case CC_MdiControls:{
690 QFont buttonFont = QFont(d->assetFont);
691 buttonFont.setPointSize(8);
692 painter->setFont(buttonFont);
693 drawTitleBarCloseButton(CC_MdiControls, SC_MdiCloseButton, Icon::ChromeClose);
694 drawTitleBarButton(CC_MdiControls, SC_MdiNormalButton, Icon::ChromeRestore);
695 drawTitleBarButton(CC_MdiControls, SC_MdiMinButton, Icon::ChromeMinimize);
696 }
697 break;
698 case CC_TitleBar:
699 if (const auto* titlebar = qstyleoption_cast<const QStyleOptionTitleBar*>(option)) {
700 painter->setPen(Qt::NoPen);
701 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
702 painter->setBrush(titlebar->palette.button());
703 painter->drawRect(titlebar->rect);
704
705 // draw title
706 QRect textRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel, widget);
707 QColor textColor = titlebar->palette.color(titlebar->titleBarState & Qt::WindowActive ? QPalette::Active : QPalette::Disabled,QPalette::WindowText);
708 painter->setPen(textColor);
709 // Note workspace also does elliding but it does not use the correct font
710 QString title = painter->fontMetrics().elidedText(titlebar->text, Qt::ElideRight, textRect.width() - 14);
711 painter->drawText(textRect.adjusted(1, 1, -1, -1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter));
712
713 QFont buttonFont = QFont(d->assetFont);
714 buttonFont.setPointSize(8);
715 painter->setFont(buttonFont);
716 auto shouldDrawButton = [titlebar](SubControl sc, Qt::WindowType flag) {
717 return (titlebar->subControls & sc) && (titlebar->titleBarFlags & flag);
718 };
719
720 // min button
721 if (shouldDrawButton(SC_TitleBarMinButton, Qt::WindowMinimizeButtonHint) &&
722 !(titlebar->titleBarState & Qt::WindowMinimized)) {
723 drawTitleBarButton(CC_TitleBar, SC_TitleBarMinButton, Icon::ChromeMinimize);
724 }
725
726 // max button
727 if (shouldDrawButton(SC_TitleBarMaxButton, Qt::WindowMaximizeButtonHint) &&
728 !(titlebar->titleBarState & Qt::WindowMaximized)) {
729 drawTitleBarButton(CC_TitleBar, SC_TitleBarMaxButton, Icon::ChromeMaximize);
730 }
731
732 // close button
733 if (shouldDrawButton(SC_TitleBarCloseButton, Qt::WindowSystemMenuHint))
734 drawTitleBarCloseButton(CC_TitleBar, SC_TitleBarCloseButton, Icon::ChromeClose);
735
736 // normalize button
737 if ((titlebar->subControls & SC_TitleBarNormalButton) &&
738 (((titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) &&
739 (titlebar->titleBarState & Qt::WindowMinimized)) ||
740 ((titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) &&
741 (titlebar->titleBarState & Qt::WindowMaximized)))) {
742 drawTitleBarButton(CC_TitleBar, SC_TitleBarNormalButton, Icon::ChromeRestore);
743 }
744
745 // context help button
746 if (shouldDrawButton(SC_TitleBarContextHelpButton, Qt::WindowContextHelpButtonHint))
747 drawTitleBarButton(CC_TitleBar, SC_TitleBarContextHelpButton, Icon::Help);
748
749 // shade button
750 if (shouldDrawButton(SC_TitleBarShadeButton, Qt::WindowShadeButtonHint))
751 drawTitleBarButton(CC_TitleBar, SC_TitleBarShadeButton, Icon::ChevronUpSmall);
752
753 // unshade button
754 if (shouldDrawButton(SC_TitleBarUnshadeButton, Qt::WindowShadeButtonHint))
755 drawTitleBarButton(CC_TitleBar, SC_TitleBarUnshadeButton, Icon::ChevronDownSmall);
756
757 // window icon for system menu
758 if (shouldDrawButton(SC_TitleBarSysMenu, Qt::WindowSystemMenuHint)) {
759 const QRect iconRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarSysMenu, widget);
760 if (iconRect.isValid()) {
761 if (!titlebar->icon.isNull()) {
762 titlebar->icon.paint(painter, iconRect);
763 } else {
764 QStyleOption tool = *titlebar;
765 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, &tool, widget);
766 const auto dpr = QStyleHelper::getDpr(widget);
767 const auto icon = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget);
768 const auto pm = icon.pixmap(QSize(extent, extent), dpr);
769 proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm);
770 }
771 }
772 }
773 }
774 break;
775#if QT_CONFIG(toolbutton)
776 case CC_ToolButton:
777 if (const auto toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
778 auto prx = proxy();
779 const bool isSplitButton =
780 toolbutton->features.testFlag(QStyleOptionToolButton::MenuButtonPopup);
781 const auto fw = prx->pixelMetric(PM_DefaultFrameWidth, option, widget);
782 const auto buttonRect = prx->subControlRect(control, toolbutton, SC_ToolButton, widget);
783 const auto menuareaRect = isSplitButton
784 ? prx->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget)
785 : QRect();
786
787 State bflags = toolbutton->state;
788 State mflags = toolbutton->state;
789 if (toolbutton->activeSubControls.testFlag(SC_ToolButton)) {
790 mflags &= ~(State_Sunken | State_MouseOver);
791 if (bflags.testFlag(State_Sunken))
792 mflags |= State_Raised;
793 }
794 if (toolbutton->activeSubControls.testFlag(SC_ToolButtonMenu)) {
795 bflags &= ~(State_Sunken | State_MouseOver);
796 if (mflags.testFlag(State_Sunken))
797 bflags |= State_Raised;
798 }
799
800 QStyleOption tool = *toolbutton;
801 if (toolbutton->subControls.testFlag(SC_ToolButton)) {
802 QPainterStateGuard psg(painter);
803 if (isSplitButton)
804 painter->setClipRect(buttonRect);
805 tool.state = bflags;
806 prx->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
807 if (isSplitButton) {
808 if (state.testFlag(State_Enabled)) {
809 painter->setPen(winUI3Color(controlStrokePrimary));
810 const auto top = buttonRect.topRight() + QPoint(0, fw + 1);
811 const auto btm = buttonRect.bottomRight() - QPoint(0, fw);
812 painter->drawLine(top, btm);
813 }
814 }
815 }
816
817 if (toolbutton->state.testFlag(State_HasFocus)) {
818 QStyleOptionFocusRect fr;
819 fr.QStyleOption::operator=(*toolbutton);
820 prx->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
821 }
822 QStyleOptionToolButton label = *toolbutton;
823 label.state = bflags;
824 label.rect = buttonRect.marginsRemoved(QMargins(fw, fw, fw, fw));
825 prx->drawControl(CE_ToolButtonLabel, &label, painter, widget);
826
827 if (toolbutton->subControls.testFlag(SC_ToolButtonMenu)) {
828 QPainterStateGuard psg(painter);
829 painter->setClipRect(menuareaRect);
830 tool.state = mflags;
831 prx->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
832
833 const int fontSize = painter->font().pointSize();
834 QFont f(d->assetFont);
835 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
836 painter->setFont(f);
837 painter->setPen(controlTextColor(option));
838 const QRect textRect(menuareaRect.topLeft(), menuareaRect.size() - QSize(fw, 0));
839 painter->drawText(textRect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
840
841 } else if (toolbutton->features.testFlag(QStyleOptionToolButton::HasMenu)) {
842 const int mbi = prx->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
843 const QRect &ir = toolbutton->rect;
844 QRect rect(ir.right() + 5 - mbi, ir.y() + ir.height() - mbi + 4, mbi - 6 - fw,
845 mbi - 6);
846 rect = visualRect(toolbutton->direction, buttonRect, rect);
847 painter->setFont(QFont(d->assetFont));
848 painter->setPen(controlTextColor(option));
849 painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
850 }
851 }
852 break;
853#endif // QT_CONFIG(toolbutton)
854 default:
855 QWindowsVistaStyle::drawComplexControl(control, option, painter, widget);
856 }
857}
858
859void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
860 QPainter *painter,
861 const QWidget *widget) const {
862 QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func());
863
864 const State state = option->state;
865 QPainterStateGuard psg(painter);
866 painter->setRenderHint(QPainter::Antialiasing);
867 if (d->transitionsEnabled() && option->styleObject && (element == PE_IndicatorCheckBox || element == PE_IndicatorRadioButton)) {
868 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
869 int oldState = styleObject->property("_q_stylestate").toInt();
870 styleObject->setProperty("_q_stylestate", int(option->state));
871 styleObject->setProperty("_q_stylerect", option->rect);
872 bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken)
873 || ((state & State_MouseOver) != (oldState & State_MouseOver))
874 || (state & State_On) != (oldState & State_On))
875 && state & State_Enabled);
876 if (doTransition) {
877 if (element == PE_IndicatorRadioButton) {
878 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
879 t->setStartValue(styleObject->property("_q_inner_radius").toFloat());
880 t->setEndValue(radioButtonInnerRadius(state));
881 styleObject->setProperty("_q_end_radius", t->endValue());
882 t->setStartTime(d->animationTime());
883 t->setDuration(150);
884 d->startAnimation(t);
885 }
886 else if (element == PE_IndicatorCheckBox) {
887 if ((oldState & State_Off && state & State_On) || (oldState & State_NoChange && state & State_On)) {
888 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
889 t->setStartValue(0.0f);
890 t->setEndValue(1.0f);
891 t->setStartTime(d->animationTime());
892 t->setDuration(150);
893 d->startAnimation(t);
894 }
895 }
896 }
897 }
898
899 switch (element) {
900 case PE_IndicatorArrowUp:
901 case PE_IndicatorArrowDown:
902 case PE_IndicatorArrowRight:
903 case PE_IndicatorArrowLeft: {
904 const QRect &r = option->rect;
905 if (r.width() <= 1 || r.height() <= 1)
906 break;
907 Icon ico = Icon::Help;
908 switch (element) {
909 case PE_IndicatorArrowUp:
910 ico = Icon::ChevronUpMed;
911 break;
912 case PE_IndicatorArrowDown:
913 ico = Icon::ChevronDownMed;
914 break;
915 case PE_IndicatorArrowLeft:
916 ico = Icon::ChevronLeftMed;
917 break;
918 case PE_IndicatorArrowRight:
919 ico = Icon::ChevronRightMed;
920 break;
921 default:
922 break;
923 }
924 QPainterStateGuard psg(painter);
925 if (option->state.testFlag(State_Sunken)) {
926 const auto bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, option, widget);
927 const auto bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget);
928 if (bsx != 0 || bsy != 0)
929 painter->translate(bsx, bsy);
930 }
931 painter->setFont(d->assetFont);
932 painter->setPen(option->palette.buttonText().color());
933 painter->setBrush(option->palette.buttonText());
934 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
935 break;
936 }
937 case PE_FrameFocusRect: {
938 if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
939 if (!(fropt->state & State_KeyboardFocusChange))
940 break;
941 QRectF focusRect = option->rect;
942 focusRect = focusRect.marginsRemoved(QMarginsF(1.5,1.5,1.5,1.5));
943 painter->setPen(winUI3Color(focusFrameInnerStroke));
944 painter->drawRoundedRect(focusRect,4,4);
945
946 focusRect = focusRect.marginsAdded(QMarginsF(1.0,1.0,1.0,1.0));
947 painter->setPen(QPen(winUI3Color(focusFrameOuterStroke),1));
948 painter->drawRoundedRect(focusRect,4,4);
949 }
950 break;
951 }
952 case PE_PanelTipLabel: {
953 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5));
954 const auto pen = highContrastTheme ? option->palette.buttonText().color()
955 : winUI3Color(frameColorLight);
956 drawRoundedRect(painter, rect, pen, option->palette.toolTipBase());
957 break;
958 }
959 case PE_FrameTabWidget:
960#if QT_CONFIG(tabwidget)
961 if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
962 QPainterStateGuard psg(painter);
963 const auto clipRegion = painter->clipRegion();
964
965 painter->setPen(highContrastTheme ? frame->palette.buttonText().color()
966 : winUI3Color(frameColorLight));
967 painter->setBrush(frame->palette.base());
968
969 const auto &rect = option->rect;
970 QRect upperRect = rect;
971 upperRect.setHeight(rect.height() / 2);
972 QRect lowerRect = rect;
973 lowerRect.setY(lowerRect.y() + rect.height() / 2);
974 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
975 painter->drawRect(rect);
976 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
977 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
978
979 }
980#endif // QT_CONFIG(tabwidget)
981 break;
982 case PE_FrameGroupBox:
983 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
984 const auto pen = highContrastTheme ? frame->palette.buttonText().color()
985 : winUI3Color(frameColorStrong);
986 if (frame->features & QStyleOptionFrame::Flat) {
987 painter->setBrush(Qt::NoBrush);
988 painter->setPen(pen);
989 const QRect &fr = frame->rect;
990 QPoint p1(fr.x(), fr.y() + 1);
991 QPoint p2(fr.x() + fr.width(), p1.y());
992 painter->drawLine(p1, p2);
993 } else {
994 const auto frameRect = QRectF(frame->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
995 drawRoundedRect(painter, frameRect, pen, Qt::NoBrush);
996 }
997 }
998 break;
999 case PE_IndicatorHeaderArrow:
1000 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1001 const auto indicator = header->sortIndicator;
1002 if (indicator != QStyleOptionHeader::None) {
1003 QPainterStateGuard psg(painter);
1004 QFont f(d->assetFont);
1005 f.setPointSize(6);
1006 painter->setFont(f);
1007 painter->setPen(header->palette.text().color());
1008 const auto ico = indicator == QStyleOptionHeader::SortUp ? Icon::ChevronDown
1009 : Icon::ChevronUp;
1010 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
1011 }
1012 }
1013 break;
1014 case PE_IndicatorCheckBox: {
1015 const bool isOn = option->state & State_On;
1016 const bool isPartial = option->state & State_NoChange;
1017
1018 const QRectF rect = option->rect;
1019 const QPointF center = rect.center();
1020
1021 drawRoundedRect(painter, option->rect, borderPenControlAlt(option),
1022 controlFillBrush(option, ControlType::ControlAlt));
1023
1024 if (isOn) {
1025 painter->setFont(d->assetFont);
1026 painter->setPen(controlTextColor(option));
1027 const auto *animation =
1028 qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
1029 const qreal clipWidth = animation ? animation->currentValue() : 1.0;
1030 const QString str = fluentIcon(Icon::AcceptMedium);
1031 QFontMetrics fm(d->assetFont);
1032 QRectF clipRect = fm.boundingRect(str);
1033 clipRect.moveCenter(center);
1034 clipRect.setLeft(rect.x() + (rect.width() - clipRect.width()) / 2.0 + 0.5);
1035 clipRect.setWidth(clipWidth * clipRect.width());
1036 painter->drawText(clipRect, Qt::AlignVCenter | Qt::AlignLeft, str);
1037 } else if (isPartial) {
1038 QFont f(d->assetFont);
1039 f.setPointSize(6);
1040 painter->setFont(f);
1041 painter->setPen(controlTextColor(option));
1042 painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::Dash12));
1043 }
1044 }
1045 break;
1046 case PE_IndicatorBranch: {
1047 if (option->state & State_Children) {
1048 const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option);
1049 const bool isReverse = option->direction == Qt::RightToLeft;
1050 const bool isOpen = option->state & QStyle::State_Open;
1051 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1052 QFont f(d->assetFont);
1053 f.setPointSize(8);
1054 painter->setFont(f);
1055 if (view && view->alternatingRowColors() && vopt && vopt->state & State_Selected)
1056 painter->setPen(winUI3Color(textOnAccentPrimary));
1057 else
1058 painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled,
1059 QPalette::WindowText));
1060 const auto ico = isOpen ? Icon::ChevronDownMed
1061 : (isReverse ? Icon::ChevronLeftMed
1062 : Icon::ChevronRightMed);
1063 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
1064 }
1065 }
1066 break;
1067 case PE_IndicatorRadioButton: {
1068 const auto *animation =
1069 qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
1070 const qreal innerRadius =
1071 animation ? animation->currentValue() : radioButtonInnerRadius(state);
1072 const QRectF rect = option->rect;
1073 const QPointF center = rect.center();
1074
1075 if (option->styleObject)
1076 option->styleObject->setProperty("_q_inner_radius", innerRadius);
1077 painter->setPen(borderPenControlAlt(option));
1078 painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
1079 if (option->state.testFlag(State_On)) {
1080 QPainterPath path;
1081 path.addEllipse(center, 7.5, 7.5);
1082 path.addEllipse(center, innerRadius, innerRadius);
1083 painter->drawPath(path);
1084 // Text On Accent/Primary
1085 painter->setBrush(option->palette.window().color());
1086 painter->drawEllipse(center, innerRadius, innerRadius);
1087 } else {
1088 painter->drawEllipse(center, 7.5, 7.5);
1089 }
1090 }
1091 break;
1092 case PE_PanelButtonTool:
1093 case PE_PanelButtonBevel:{
1094 const bool isEnabled = state & QStyle::State_Enabled;
1095 const bool isMouseOver = state & QStyle::State_MouseOver;
1096 const bool isRaised = state & QStyle::State_Raised;
1097 const int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
1098 const QRectF rect = option->rect.marginsRemoved(QMargins(fw, fw, fw, fw));
1099 if (element == PE_PanelButtonTool && ((!isMouseOver && !isRaised) || !isEnabled))
1100 painter->setPen(Qt::NoPen);
1101 else
1102 painter->setPen(winUI3Color(controlStrokePrimary));
1103 painter->setBrush(controlFillBrush(option, ControlType::Control));
1104 if (element == PE_PanelButtonTool && widget) {
1105 const auto name = widget->objectName();
1106 if (name == "ScrollLeftButton"_L1 || name == "ScrollRightButton"_L1) {
1107 painter->setPen(Qt::NoPen);
1108 if (isMouseOver)
1109 painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
1110 else
1111 painter->setBrush(Qt::NoBrush);
1112 }
1113 }
1114 painter->drawRoundedRect(rect,
1115 secondLevelRoundingRadius, secondLevelRoundingRadius);
1116 }
1117 break;
1118 case PE_FrameDefaultButton:
1119 painter->setPen(option->palette.accent().color());
1120 painter->setBrush(Qt::NoBrush);
1121 painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1122 break;
1123 case PE_FrameMenu:
1124 break;
1125 case PE_PanelMenu: {
1126 if (!d->nativeRoundedTopLevelWindows) {
1127 const QRect rect = option->rect.marginsRemoved(QMargins(2, 2, 2, 2));
1128 painter->setPen(highContrastTheme ? QPen(option->palette.windowText().color(), 2)
1129 : winUI3Color(frameColorLight));
1130 painter->setBrush(winUI3Color(menuPanelFill));
1131 painter->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius);
1132 }
1133 break;
1134 }
1135 case PE_PanelLineEdit:
1136 if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1137 const bool isInSpinBox =
1138 widget && qobject_cast<const QAbstractSpinBox *>(widget->parent()) != nullptr;
1139 const bool isInComboBox =
1140 widget && qobject_cast<const QComboBox *>(widget->parent()) != nullptr;
1141 if (!isInSpinBox && !isInComboBox) {
1142 const auto frameRect =
1143 QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1144 drawRoundedRect(painter, frameRect, Qt::NoPen, inputFillBrush(option, widget));
1145 if (panel->lineWidth > 0)
1146 proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
1147 }
1148 }
1149 break;
1150 case PE_FrameLineEdit: {
1151 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1152 drawLineEditFrame(painter, frameRect, option);
1153 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
1154 QStyleOptionFocusRect fropt;
1155 fropt.QStyleOption::operator=(*option);
1156 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
1157 }
1158 break;
1159 }
1160 case PE_Frame: {
1161 if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1162 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1163 if (qobject_cast<const QComboBoxPrivateContainer *>(widget)) {
1164 if (d->nativeRoundedTopLevelWindows)
1165 break;
1166 QPen pen;
1167 if (highContrastTheme)
1168 pen = QPen(option->palette.windowText().color(), 2);
1169 else
1170 pen = Qt::NoPen;
1171 drawRoundedRect(painter, rect, pen, WINUI3Colors[colorSchemeIndex][menuPanelFill]);
1172 } else
1173 drawRoundedRect(painter, rect, Qt::NoPen, option->palette.brush(QPalette::Base));
1174
1175 if (frame->frameShape == QFrame::NoFrame)
1176 break;
1177
1178 const bool isEditable = qobject_cast<const QTextEdit *>(widget) != nullptr
1179 || qobject_cast<const QPlainTextEdit *>(widget) != nullptr;
1180 drawLineEditFrame(painter, rect, option, isEditable);
1181 }
1182 break;
1183 }
1184 case PE_PanelItemViewItem:
1185 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1186 if (vopt->backgroundBrush.style() != Qt::NoBrush) {
1187 QPainterStateGuard psg(painter);
1188 painter->setBrushOrigin(vopt->rect.topLeft());
1189 painter->fillRect(vopt->rect, vopt->backgroundBrush);
1190 }
1191 }
1192 break;
1193 case PE_PanelItemViewRow:
1194 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1195 // this is only called from a QTreeView to paint
1196 // - the tree branch decoration (incl. selected/hovered or not)
1197 // - the (alternate) background of the item in always unselected state
1198 const QRect &rect = vopt->rect;
1199 const bool isRtl = option->direction == Qt::RightToLeft;
1200 if (rect.width() <= 0)
1201 break;
1202
1203 if (vopt->features & QStyleOptionViewItem::Alternate) {
1204 QPalette::ColorGroup cg =
1205 (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled))
1206 ? QPalette::Normal
1207 : QPalette::Disabled;
1208 if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
1209 cg = QPalette::Inactive;
1210 painter->fillRect(rect, option->palette.brush(cg, QPalette::AlternateBase));
1211 }
1212
1213 if (option->state & State_Selected && !highContrastTheme) {
1214 // keep in sync with CE_ItemViewItem QListView indicator painting
1215 if (!qobject_cast<const QTableView *>(widget)) {
1216 const auto col = option->palette.accent().color();
1217 painter->setBrush(col);
1218 painter->setPen(col);
1219 const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f;
1220 const auto yOfs = rect.height() / 4.;
1221 QRectF r(QPointF(xPos, rect.y() + yOfs),
1222 QPointF(xPos + 1, rect.y() + rect.height() - yOfs));
1223 painter->drawRoundedRect(r, 1, 1);
1224 }
1225 }
1226
1227 const bool isTreeDecoration = vopt->features.testFlag(
1228 QStyleOptionViewItem::IsDecorationForRootColumn);
1229 if (isTreeDecoration && vopt->state.testAnyFlags(State_Selected | State_MouseOver) &&
1230 vopt->showDecorationSelected) {
1231 const bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1232 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1233 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1234 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1235
1236 if (onlyOne)
1237 isFirst = true;
1238
1239 if (isRtl) {
1240 isFirst = !isFirst;
1241 isLast = !isLast;
1242 }
1243
1244 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1245 painter->setBrush(view->alternatingRowColors() && state & State_Selected ? calculateAccentColor(option) : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1246 painter->setPen(Qt::NoPen);
1247 if (isFirst) {
1248 QPainterStateGuard psg(painter);
1249 painter->setClipRect(rect);
1250 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1251 secondLevelRoundingRadius, secondLevelRoundingRadius);
1252 } else if (isLast) {
1253 QPainterStateGuard psg(painter);
1254 painter->setClipRect(rect);
1255 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1256 secondLevelRoundingRadius, secondLevelRoundingRadius);
1257 } else {
1258 painter->drawRect(vopt->rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1259 }
1260 }
1261 }
1262 break;
1263 case QStyle::PE_Widget: {
1264 if (widget && widget->palette().isBrushSet(QPalette::Active, widget->backgroundRole())) {
1265 const QBrush bg = widget->palette().brush(widget->backgroundRole());
1266 auto wp = QWidgetPrivate::get(widget);
1267 QPainterStateGuard psg(painter);
1268 wp->updateBrushOrigin(painter, bg);
1269 painter->fillRect(option->rect, bg);
1270 }
1271 break;
1272 }
1273 case QStyle::PE_FrameWindow:
1274 if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1275
1276 QRectF rect= option->rect;
1277 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1278
1279 QRectF bottomLeftCorner = QRectF(rect.left() + 1.0,
1280 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1281 secondLevelRoundingRadius,
1282 secondLevelRoundingRadius);
1283 QRectF bottomRightCorner = QRectF(rect.right() - 1.0 - secondLevelRoundingRadius,
1284 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1285 secondLevelRoundingRadius,
1286 secondLevelRoundingRadius);
1287
1288 //Draw Mask
1289 if (widget != nullptr) {
1290 QBitmap mask(widget->width(), widget->height());
1291 mask.clear();
1292
1293 QPainter maskPainter(&mask);
1294 maskPainter.setRenderHint(QPainter::Antialiasing);
1295 maskPainter.setBrush(Qt::color1);
1296 maskPainter.setPen(Qt::NoPen);
1297 maskPainter.drawRoundedRect(option->rect,secondLevelRoundingRadius,secondLevelRoundingRadius);
1298 const_cast<QWidget*>(widget)->setMask(mask);
1299 }
1300
1301 //Draw Window
1302 painter->setPen(QPen(frm->palette.base(), fwidth));
1303 painter->drawLine(QPointF(rect.left(), rect.top()),
1304 QPointF(rect.left(), rect.bottom() - fwidth));
1305 painter->drawLine(QPointF(rect.left() + fwidth, rect.bottom()),
1306 QPointF(rect.right() - fwidth, rect.bottom()));
1307 painter->drawLine(QPointF(rect.right(), rect.top()),
1308 QPointF(rect.right(), rect.bottom() - fwidth));
1309
1310 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1311 painter->drawLine(QPointF(rect.left() + 0.5, rect.top() + 0.5),
1312 QPointF(rect.left() + 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1313 painter->drawLine(QPointF(rect.left() + 0.5 + secondLevelRoundingRadius, rect.bottom() - 0.5),
1314 QPointF(rect.right() - 0.5 - secondLevelRoundingRadius, rect.bottom() - 0.5));
1315 painter->drawLine(QPointF(rect.right() - 0.5, rect.top() + 1.5),
1316 QPointF(rect.right() - 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1317
1318 painter->setPen(Qt::NoPen);
1319 painter->setBrush(frm->palette.base());
1320 painter->drawPie(bottomRightCorner.marginsAdded(QMarginsF(2.5,2.5,0.0,0.0)),
1321 270 * 16,90 * 16);
1322 painter->drawPie(bottomLeftCorner.marginsAdded(QMarginsF(0.0,2.5,2.5,0.0)),
1323 -90 * 16,-90 * 16);
1324
1325 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1326 painter->setBrush(Qt::NoBrush);
1327 painter->drawArc(bottomRightCorner,
1328 0 * 16,-90 * 16);
1329 painter->drawArc(bottomLeftCorner,
1330 -90 * 16,-90 * 16);
1331 }
1332 break;
1333#if QT_CONFIG(tabbar)
1334 case PE_FrameTabBarBase:
1335 if (const auto *tab = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
1336 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1337 const auto clipRegion = painter->clipRegion();
1338
1339 painter->setPen(highContrastTheme ? tab->palette.buttonText().color()
1340 : winUI3Color(frameColorLight));
1341 painter->setBrush(tab->palette.base());
1342 QRect upperRect = tab->rect;
1343 upperRect.setHeight(tab->rect.height() / 2);
1344 QRect lowerRect = tab->rect;
1345 lowerRect.setY(lowerRect.y() + tab->rect.height() / 2);
1346 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
1347 painter->drawRoundedRect(tab->rect, secondLevelRoundingRadius,
1348 secondLevelRoundingRadius);
1349 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
1350 painter->drawRect(tab->rect);
1351 }
1352 break;
1353#endif // QT_CONFIG(tabbar)
1354 case PE_IndicatorTabTearLeft:
1355 case PE_IndicatorTabTearRight:
1356 break;
1357 default:
1358 QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
1359 }
1360}
1361
1362/*!
1363 \internal
1364*/
1365void QWindows11Style::drawControl(ControlElement element, const QStyleOption *option,
1366 QPainter *painter, const QWidget *widget) const
1367{
1368 Q_D(const QWindows11Style);
1369 State flags = option->state;
1370
1371 QPainterStateGuard psg(painter);
1372 painter->setRenderHint(QPainter::Antialiasing);
1373 switch (element) {
1374 case QStyle::CE_ComboBoxLabel:
1375#if QT_CONFIG(combobox)
1376 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1377 painter->setPen(controlTextColor(option, true));
1378 QStyleOptionComboBox newOption = *cb;
1379 newOption.rect.adjust(4,0,-4,0);
1380 QCommonStyle::drawControl(element, &newOption, painter, widget);
1381 }
1382#endif // QT_CONFIG(combobox)
1383 break;
1384#if QT_CONFIG(tabbar)
1385 case CE_TabBarTabShape:
1386 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
1387 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1388 const bool isSelected = tab->state.testFlag(State_Selected);
1389 const auto clipRegion = painter->clipRegion();
1390 const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab
1391 || tab->position == QStyleOptionTab::Moving;
1392 auto leftMargin = (tab->position == QStyleOptionTab::Beginning || onlyOne) ? 1 : 0;
1393 auto rightMargin = (tab->position == QStyleOptionTab::End || onlyOne) ? 1 : 0;
1394 if (QCommonStylePrivate::rtl(option))
1395 std::swap(leftMargin, rightMargin);
1396
1397 QRectF tabRect = tab->rect.marginsRemoved(QMargins(leftMargin, 1, rightMargin, -3));
1398 painter->setPen(highContrastTheme ? tab->palette.buttonText().color() : winUI3Color(frameColorLight));
1399 painter->setBrush(tab->palette.base());
1400 if (isSelected) {
1401 painter->setBrush(winUI3Color(fillMicaAltDefault));
1402 } else {
1403 if (tab->state.testFlag(State_Sunken))
1404 painter->setBrush(winUI3Color(fillMicaAltDefault));
1405 else if (tab->state.testFlag(State_MouseOver))
1406 painter->setBrush(winUI3Color(fillMicaAltSecondary));
1407 else
1408 painter->setBrush(winUI3Color(fillMicaAltTransparent));
1409 }
1410 QRect upperRect = tab->rect;
1411 upperRect.setHeight(tab->rect.height() / 2.);
1412 QRect lowerRect = tab->rect;
1413 lowerRect.setY(lowerRect.y() + tab->rect.height() / 2.);
1414 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
1415 painter->drawRoundedRect(tabRect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1416 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
1417 painter->drawRect(tabRect);
1418 }
1419 break;
1420 case CE_TabBarTabLabel:
1421 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
1422 const bool isEnabled = tab->state.testFlags(State_Enabled);
1423 const bool isSelected = tab->state.testFlags(State_Selected);
1424
1425 QRect tr = tab->rect;
1426 bool verticalTabs = tab->shape == QTabBar::RoundedEast
1427 || tab->shape == QTabBar::RoundedWest || tab->shape == QTabBar::TriangularEast
1428 || tab->shape == QTabBar::TriangularWest;
1429
1430 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
1431 if (!proxy()->styleHint(SH_UnderlineShortcut, option, widget))
1432 alignment |= Qt::TextHideMnemonic;
1433
1434 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1435 if (verticalTabs) {
1436 psg.save();
1437 int newX, newY, newRot;
1438 if (tab->shape == QTabBar::RoundedEast || tab->shape == QTabBar::TriangularEast) {
1439 newX = tr.width() + tr.x();
1440 newY = tr.y();
1441 newRot = 90;
1442 } else {
1443 newX = tr.x();
1444 newY = tr.y() + tr.height();
1445 newRot = -90;
1446 }
1447 QTransform m = QTransform::fromTranslate(newX, newY);
1448 m.rotate(newRot);
1449 painter->setTransform(m, true);
1450 }
1451 QRect iconRect;
1452 d->tabLayout(tab, widget, &tr, &iconRect);
1453
1454 // compute tr again, unless tab is moving, because the style may override subElementRect
1455 if (tab->position != QStyleOptionTab::TabPosition::Moving)
1456 tr = proxy()->subElementRect(SE_TabBarTabText, option, widget);
1457
1458 if (!tab->icon.isNull()) {
1459 const auto mode = isEnabled ? QIcon::Normal : QIcon::Disabled;
1460 const auto state = isSelected ? QIcon::On : QIcon::Off;
1461 tab->icon.paint(painter, iconRect, Qt::AlignCenter, mode, state);
1462 }
1463
1464 painter->setPen(winUI3Color(isSelected ? textPrimary : textSecondary));
1465 proxy()->drawItemText(painter, tr, alignment, tab->palette, isEnabled, tab->text);
1466 }
1467 break;
1468#endif // QT_CONFIG(tabbar)
1469 case CE_ToolButtonLabel:
1470#if QT_CONFIG(toolbutton)
1471 if (const QStyleOptionToolButton *toolbutton
1472 = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
1473 QRect rect = toolbutton->rect;
1474 int shiftX = 0;
1475 int shiftY = 0;
1476 if (toolbutton->state & (State_Sunken | State_On)) {
1477 shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget);
1478 shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton, widget);
1479 }
1480 // Arrow type always overrules and is always shown
1481 bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow;
1482 if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty())
1483 || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) {
1484 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
1485 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1486 alignment |= Qt::TextHideMnemonic;
1487 rect.translate(shiftX, shiftY);
1488 painter->setFont(toolbutton->font);
1489 const QString text = d->toolButtonElideText(toolbutton, rect, alignment);
1490 painter->setPen(controlTextColor(option));
1491 proxy()->drawItemText(painter, rect, alignment, toolbutton->palette,
1492 toolbutton->state & State_Enabled, text);
1493 } else {
1494 QPixmap pm;
1495 QSize pmSize = toolbutton->iconSize;
1496 if (!toolbutton->icon.isNull()) {
1497 QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off;
1498 QIcon::Mode mode;
1499 if (!(toolbutton->state & State_Enabled))
1500 mode = QIcon::Disabled;
1501 else if ((toolbutton->state & State_MouseOver) && (toolbutton->state & State_AutoRaise))
1502 mode = QIcon::Active;
1503 else
1504 mode = QIcon::Normal;
1505 pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), painter->device()->devicePixelRatio(),
1506 mode, state);
1507 pmSize = pm.size() / pm.devicePixelRatio();
1508 }
1509
1510 if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) {
1511 painter->setFont(toolbutton->font);
1512 QRect pr = rect,
1513 tr = rect;
1514 int alignment = Qt::TextShowMnemonic;
1515 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1516 alignment |= Qt::TextHideMnemonic;
1517
1518 if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
1519 pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1520 tr.adjust(0, pr.height() - 1, 0, -1);
1521 pr.translate(shiftX, shiftY);
1522 if (!hasArrow) {
1523 proxy()->drawItemPixmap(painter, pr, Qt::AlignCenter, pm);
1524 } else {
1525 drawArrow(proxy(), toolbutton, pr, painter, widget);
1526 }
1527 alignment |= Qt::AlignCenter;
1528 } else {
1529 pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1530 tr.adjust(pr.width(), 0, 0, 0);
1531 pr.translate(shiftX, shiftY);
1532 if (!hasArrow) {
1533 proxy()->drawItemPixmap(painter, QStyle::visualRect(toolbutton->direction, rect, pr), Qt::AlignCenter, pm);
1534 } else {
1535 drawArrow(proxy(), toolbutton, pr, painter, widget);
1536 }
1537 alignment |= Qt::AlignLeft | Qt::AlignVCenter;
1538 }
1539 tr.translate(shiftX, shiftY);
1540 const QString text = d->toolButtonElideText(toolbutton, tr, alignment);
1541 painter->setPen(controlTextColor(option));
1542 proxy()->drawItemText(painter, QStyle::visualRect(toolbutton->direction, rect, tr), alignment, toolbutton->palette,
1543 toolbutton->state & State_Enabled, text);
1544 } else {
1545 rect.translate(shiftX, shiftY);
1546 if (hasArrow) {
1547 drawArrow(proxy(), toolbutton, rect, painter, widget);
1548 } else {
1549 proxy()->drawItemPixmap(painter, rect, Qt::AlignCenter, pm);
1550 }
1551 }
1552 }
1553 }
1554#endif // QT_CONFIG(toolbutton)
1555 break;
1556 case QStyle::CE_ShapedFrame:
1557 if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1558 int frameShape = f->frameShape;
1559 int frameShadow = QFrame::Plain;
1560 if (f->state & QStyle::State_Sunken)
1561 frameShadow = QFrame::Sunken;
1562 else if (f->state & QStyle::State_Raised)
1563 frameShadow = QFrame::Raised;
1564
1565 int lw = f->lineWidth;
1566 int mlw = f->midLineWidth;
1567
1568 switch (frameShape) {
1569 case QFrame::Box:
1570 if (frameShadow == QFrame::Plain)
1571 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1572 else
1573 qDrawShadeRect(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw);
1574 break;
1575 case QFrame::Panel:
1576 if (frameShadow == QFrame::Plain)
1577 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1578 else
1579 qDrawShadePanel(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw);
1580 break;
1581 default:
1582 QWindowsVistaStyle::drawControl(element, option, painter, widget);
1583 }
1584 }
1585 break;
1586#if QT_CONFIG(progressbar)
1587 case CE_ProgressBarGroove:
1588 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
1589 QRect rect = option->rect;
1590 QPointF center = rect.center();
1591 if (baropt->state & QStyle::State_Horizontal) {
1592 rect.setHeight(1);
1593 rect.moveTop(center.y());
1594 } else {
1595 rect.setWidth(1);
1596 rect.moveLeft(center.x());
1597 }
1598 painter->setPen(Qt::NoPen);
1599 painter->setBrush(Qt::gray);
1600 painter->drawRect(rect);
1601 }
1602 break;
1603 case CE_ProgressBarContents:
1604 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1605 QPainterStateGuard psg(painter);
1606 QRectF rect = option->rect;
1607 painter->translate(rect.topLeft());
1608 rect.translate(-rect.topLeft());
1609
1610 constexpr qreal progressBarThickness = 3;
1611 constexpr qreal progressBarHalfThickness = progressBarThickness / 2.0;
1612
1613 const auto isIndeterminate = baropt->maximum == 0 && baropt->minimum == 0;
1614 const auto orientation =
1615 (baropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical;
1616 const auto inverted = baropt->invertedAppearance;
1617 const auto reverse = (baropt->direction == Qt::RightToLeft) ^ inverted;
1618 // If the orientation is vertical, we use a transform to rotate
1619 // the progress bar 90 degrees (counter)clockwise. This way we can use the
1620 // same rendering code for both orientations.
1621 if (orientation == Qt::Vertical) {
1622 rect = QRectF(rect.left(), rect.top(), rect.height(),
1623 rect.width()); // flip width and height
1624 QTransform m;
1625 if (inverted) {
1626 m.rotate(90);
1627 m.translate(0, -rect.height() + 1);
1628 } else {
1629 m.rotate(-90);
1630 m.translate(-rect.width(), 0);
1631 }
1632 painter->setTransform(m, true);
1633 } else if (reverse) {
1634 QTransform m = QTransform::fromScale(-1, 1);
1635 m.translate(-rect.width(), 0);
1636 painter->setTransform(m, true);
1637 }
1638 const qreal offset = (int(rect.height()) % 2 == 0) ? 0.5f : 0.0f;
1639
1640 if (isIndeterminate) {
1641#if QT_CONFIG(animation)
1642 auto anim = d->animation(option->styleObject);
1643 if (!anim) {
1644 auto anim = new QStyleAnimation(option->styleObject);
1645 anim->setFrameRate(QStyleAnimation::SixtyFps);
1646 d->startAnimation(anim);
1647 }
1648 constexpr auto loopDurationMSec = 4000;
1649 const auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(
1650 std::chrono::system_clock::now());
1651 const auto elapsed = elapsedTime.time_since_epoch().count();
1652 const auto handleCenter = (elapsed % loopDurationMSec) / float(loopDurationMSec);
1653 const auto isLongHandle = (elapsed / loopDurationMSec) % 2 == 0;
1654 const auto lengthFactor = (isLongHandle ? 33.0f : 25.0f) / 100.0f;
1655#else
1656 constexpr auto handleCenter = 0.5f;
1657 constexpr auto lengthFactor = 1;
1658#endif
1659 const auto begin = qMax(handleCenter * (1 + lengthFactor) - lengthFactor, 0.0f);
1660 const auto end = qMin(handleCenter * (1 + lengthFactor), 1.0f);
1661 const auto barBegin = begin * rect.width();
1662 const auto barEnd = end * rect.width();
1663 rect = QRectF(QPointF(rect.left() + barBegin, rect.top()),
1664 QPointF(rect.left() + barEnd, rect.bottom()));
1665 } else {
1666#if QT_CONFIG(animation)
1667 d->stopAnimation(option->styleObject);
1668#endif
1669 const auto fillPercentage = (float(baropt->progress - baropt->minimum))
1670 / (float(baropt->maximum - baropt->minimum));
1671 rect.setWidth(rect.width() * fillPercentage);
1672 }
1673 const QPointF center = rect.center();
1674 rect.setHeight(progressBarThickness);
1675 rect.moveTop(center.y() - progressBarHalfThickness - offset);
1676 drawRoundedRect(painter, rect, Qt::NoPen, baropt->palette.accent());
1677 }
1678 break;
1679 case CE_ProgressBarLabel:
1680 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1681 const bool vertical = !(baropt->state & QStyle::State_Horizontal);
1682 if (!vertical) {
1683 proxy()->drawItemText(painter, baropt->rect, Qt::AlignCenter | Qt::TextSingleLine,
1684 baropt->palette, baropt->state & State_Enabled, baropt->text,
1685 QPalette::Text);
1686 }
1687 }
1688 break;
1689#endif // QT_CONFIG(progressbar)
1690 case CE_PushButtonLabel:
1691 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1692 QStyleOptionButton btnCopy(*btn);
1693 btnCopy.rect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0));
1694 btnCopy.palette.setBrush(QPalette::ButtonText, controlTextColor(option));
1695 QCommonStyle::drawControl(element, &btnCopy, painter, widget);
1696 }
1697 break;
1698 case CE_PushButtonBevel:
1699 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1700 using namespace StyleOptionHelper;
1701
1702 QRectF rect = btn->rect.marginsRemoved(QMargins(2, 2, 2, 2));
1703 painter->setPen(Qt::NoPen);
1704 if (btn->features.testFlag(QStyleOptionButton::Flat)) {
1705 painter->setBrush(btn->palette.button());
1706 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1707 if (flags & (State_Sunken | State_On)) {
1708 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtlePressedColor]);
1709 }
1710 else if (flags & State_MouseOver) {
1711 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1712 }
1713 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1714 } else {
1715 painter->setBrush(controlFillBrush(option, ControlType::Control));
1716 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1717
1718 rect.adjust(0.5,0.5,-0.5,-0.5);
1719 const bool defaultButton = btn->features.testFlag(QStyleOptionButton::DefaultButton);
1720 painter->setBrush(Qt::NoBrush);
1721 painter->setPen(defaultButton ? option->palette.accent().color()
1722 : WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
1723 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1724
1725 painter->setPen(defaultButton ? WINUI3Colors[colorSchemeIndex][controlStrokeOnAccentSecondary]
1726 : WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]);
1727 }
1728 if (btn->features.testFlag(QStyleOptionButton::HasMenu)) {
1729 QPainterStateGuard psg(painter);
1730
1731 QRect textRect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0));
1732 const auto indSize = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget);
1733 const auto indRect =
1734 QRect(btn->rect.right() - indSize - contentItemHMargin, textRect.top(),
1735 indSize + contentItemHMargin, btn->rect.height());
1736 const auto vindRect = visualRect(btn->direction, btn->rect, indRect);
1737 textRect.setWidth(textRect.width() - indSize);
1738
1739 int fontSize = painter->font().pointSize();
1740 QFont f(d->assetFont);
1741 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
1742 painter->setFont(f);
1743 painter->setPen(controlTextColor(option));
1744 painter->drawText(vindRect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
1745 }
1746 }
1747 break;
1748 case CE_MenuBarItem:
1749 if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1750 using namespace StyleOptionHelper;
1751
1752 constexpr int hPadding = 11;
1753 constexpr int topPadding = 4;
1754 constexpr int bottomPadding = 6;
1755 QStyleOptionMenuItem newMbi = *mbi;
1756
1757 if (auto mbiV2 = qstyleoption_cast<const QStyleOptionMenuItemV2 *>(option))
1758 newMbi.state.setFlag(State_Sunken, mbiV2->mouseDown);
1759
1760 newMbi.font.setPointSize(10);
1761 newMbi.palette.setColor(QPalette::ButtonText, controlTextColor(&newMbi));
1762 if (!isDisabled(&newMbi)) {
1763 QPen pen(Qt::NoPen);
1764 QBrush brush(Qt::NoBrush);
1765 if (highContrastTheme) {
1766 pen = QPen(newMbi.palette.highlight().color(), 2);
1767 brush = newMbi.palette.window();
1768 } else if (isPressed(&newMbi)) {
1769 brush = winUI3Color(subtlePressedColor);
1770 } else if (isHover(&newMbi)) {
1771 brush = winUI3Color(subtleHighlightColor);
1772 }
1773 if (pen != Qt::NoPen || brush != Qt::NoBrush) {
1774 const QRect rect = mbi->rect.marginsRemoved(QMargins(5, 0, 5, 0));
1775 drawRoundedRect(painter, rect, pen, brush);
1776 }
1777 }
1778 newMbi.rect.adjust(hPadding,topPadding,-hPadding,-bottomPadding);
1779 painter->setFont(newMbi.font);
1780 QCommonStyle::drawControl(element, &newMbi, painter, widget);
1781 }
1782 break;
1783
1784#if QT_CONFIG(menu)
1785 case CE_MenuEmptyArea:
1786 break;
1787
1788 case CE_MenuItem:
1789 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1790 const auto visualMenuRect = [&](const QRect &rect) {
1791 return visualRect(option->direction, menuitem->rect, rect);
1792 };
1793 bool dis = !(menuitem->state & State_Enabled);
1794 bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
1795 ? menuitem->checked : false;
1796 bool act = menuitem->state & State_Selected;
1797
1798 const QRect rect = menuitem->rect.marginsRemoved(QMargins(2,2,2,2));
1799 if (act && dis == false) {
1800 drawRoundedRect(painter, rect, Qt::NoPen, highContrastTheme ? menuitem->palette.brush(QPalette::Highlight)
1801 : QBrush(winUI3Color(subtleHighlightColor)));
1802 }
1803 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
1804 constexpr int yoff = 1;
1805 painter->setPen(highContrastTheme ? menuitem->palette.buttonText().color() : winUI3Color(dividerStrokeDefault));
1806 painter->drawLine(menuitem->rect.topLeft() + QPoint(0, yoff),
1807 menuitem->rect.topRight() + QPoint(0, yoff));
1808 break;
1809 }
1810
1811 int xOffset = contentHMargin;
1812 // WinUI3 draws, in contrast to former windows styles, the checkmark and icon separately
1813 const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget);
1814 if (checked) {
1815 QRect vRect(visualMenuRect(QRect(rect.x() + xOffset, rect.y(),
1816 checkMarkWidth, rect.height())));
1817 QPainterStateGuard psg(painter);
1818 painter->setFont(d->assetFont);
1819 painter->setPen(option->palette.text().color());
1820 painter->drawText(vRect, Qt::AlignCenter, fluentIcon(Icon::CheckMark));
1821 }
1822 if (menuitem->menuHasCheckableItems)
1823 xOffset += checkMarkWidth + contentItemHMargin;
1824 if (!menuitem->icon.isNull()) {
1825 // 4 is added to maxIconWidth in qmenu.cpp to PM_SmallIconSize
1826 QRect vRect(visualMenuRect(QRect(rect.x() + xOffset,
1827 rect.y(),
1828 menuitem->maxIconWidth - 4,
1829 rect.height())));
1830 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
1831 if (act && !dis)
1832 mode = QIcon::Active;
1833 const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
1834 QRect pmr(QPoint(0, 0), QSize(size, size));
1835 pmr.moveCenter(vRect.center());
1836 menuitem->icon.paint(painter, pmr, Qt::AlignCenter, mode,
1837 checked ? QIcon::On : QIcon::Off);
1838 }
1839 if (menuitem->maxIconWidth > 0)
1840 xOffset += menuitem->maxIconWidth - 4 + contentItemHMargin;
1841
1842 QStringView s(menuitem->text);
1843 if (!s.isEmpty()) { // draw text
1844 QPoint tl(rect.left() + xOffset, rect.top());
1845 QPoint br(rect.right() - menuitem->reservedShortcutWidth - contentHMargin,
1846 rect.bottom());
1847 QRect textRect(tl, br);
1848 QRect vRect(visualMenuRect(textRect));
1849
1850 QColor penColor;
1851 if (highContrastTheme) {
1852 penColor = menuitem->palette.color(act ? QPalette::HighlightedText
1853 : QPalette::Text);
1854 } else {
1855 penColor = controlTextColor(option);
1856 }
1857 painter->setPen(penColor);
1858
1859 qsizetype t = s.indexOf(u'\t');
1860 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
1861 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
1862 text_flags |= Qt::TextHideMnemonic;
1863 text_flags |= Qt::AlignLeft;
1864 // a submenu doesn't paint a possible shortcut in WinUI3
1865 if (t >= 0 && menuitem->menuItemType != QStyleOptionMenuItem::SubMenu) {
1866 QRect shortcutRect(QPoint(textRect.right(), textRect.top()),
1867 QPoint(rect.right(), textRect.bottom()));
1868 QRect vShortcutRect(visualMenuRect(shortcutRect));
1869 const QString textToDraw = s.mid(t + 1).toString();
1870 painter->drawText(vShortcutRect, text_flags, textToDraw);
1871 s = s.left(t);
1872 }
1873 QFont font = menuitem->font;
1874 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
1875 font.setBold(true);
1876 painter->setFont(font);
1877 const QString textToDraw = s.left(t).toString();
1878 painter->drawText(vRect, text_flags, textToDraw);
1879 }
1880 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
1881 int fontSize = menuitem->font.pointSize();
1882 QFont f(d->assetFont);
1883 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
1884 painter->setFont(f);
1885 int yOfs = qRound(fontSize / 3.0f); // an offset to align the '>' with the baseline of the text
1886 QPoint tl(rect.right() - 2 * QWindowsStylePrivate::windowsArrowHMargin - contentItemHMargin,
1887 rect.top() + yOfs);
1888 QRect submenuRect(tl, rect.bottomRight());
1889 QRect vSubMenuRect = visualMenuRect(submenuRect);
1890 painter->setPen(option->palette.text().color());
1891 const bool isReverse = option->direction == Qt::RightToLeft;
1892 const auto ico = isReverse ? Icon::ChevronLeftMed : Icon::ChevronRightMed;
1893 painter->drawText(vSubMenuRect, Qt::AlignCenter, fluentIcon(ico));
1894 }
1895 }
1896 break;
1897#endif // QT_CONFIG(menu)
1898 case CE_MenuBarEmptyArea: {
1899 break;
1900 }
1901 case CE_HeaderEmptyArea:
1902 break;
1903 case CE_HeaderSection: {
1904 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1905 painter->setPen(Qt::NoPen);
1906 painter->setBrush(header->palette.button());
1907 painter->drawRect(header->rect);
1908 const bool isRtl = option->direction == Qt::RightToLeft;
1909 const QPointF tr = isRtl ? option->rect.topLeft() : option->rect.topRight();
1910 const QPointF br = isRtl ? option->rect.bottomLeft() : option->rect.bottomRight();
1911 const QPointF bl = isRtl ? option->rect.bottomRight() : option->rect.bottomLeft();
1912 constexpr QPointF trOfs = QPointF(0.5, 0.0);
1913 constexpr QPointF brOfs = QPointF(0.5, 0.5);
1914 constexpr QPointF blOfs = QPointF(0.0, 0.5);
1915 const std::array<QPointF, 3> points = { tr + trOfs, br + brOfs, bl + blOfs };
1916 QPen pen(highContrastTheme ? header->palette.buttonText().color()
1917 : winUI3Color(frameColorLight));
1918 pen.setJoinStyle(Qt::MiterJoin);
1919 painter->setPen(pen);
1920 painter->drawPolyline(points.data(), int(points.size()));
1921 }
1922 break;
1923 }
1924 case CE_ItemViewItem: {
1925 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1926 const auto p = proxy();
1927 QRect checkRect = p->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget);
1928 QRect iconRect = p->subElementRect(SE_ItemViewItemDecoration, vopt, widget);
1929 QRect textRect = p->subElementRect(SE_ItemViewItemText, vopt, widget);
1930
1931 // draw the background
1932 proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget);
1933
1934 const QRect &rect = vopt->rect;
1935 const bool isRtl = option->direction == Qt::RightToLeft;
1936 bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1937 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1938 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1939 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1940
1941 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1942 if (qobject_cast<const QTableView *>(view)) {
1943 onlyOne = true;
1944 } else {
1945 // the tree decoration already painted the left side of the rounded rect
1946 if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) &&
1947 vopt->showDecorationSelected) {
1948 isFirst = false;
1949 if (onlyOne) {
1950 onlyOne = false;
1951 isLast = true;
1952 }
1953 }
1954
1955 if (isRtl) {
1956 if (isFirst) {
1957 isFirst = false;
1958 isLast = true;
1959 } else if (isLast) {
1960 isFirst = true;
1961 isLast = false;
1962 }
1963 }
1964 }
1965
1966 const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver);
1967 if (highlightCurrent) {
1968 if (highContrastTheme) {
1969 painter->setBrush(vopt->palette.highlight());
1970 } else {
1971 painter->setBrush(view && view->alternatingRowColors() && vopt->state & State_Selected
1972 ? calculateAccentColor(option)
1973 : winUI3Color(subtleHighlightColor));
1974 }
1975 } else {
1976 painter->setBrush(vopt->backgroundBrush);
1977 }
1978 painter->setPen(Qt::NoPen);
1979
1980 if (onlyOne) {
1981 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)),
1982 secondLevelRoundingRadius, secondLevelRoundingRadius);
1983 } else if (isFirst) {
1984 QPainterStateGuard psg(painter);
1985 painter->setClipRect(rect);
1986 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1987 secondLevelRoundingRadius, secondLevelRoundingRadius);
1988 } else if (isLast) {
1989 QPainterStateGuard psg(painter);
1990 painter->setClipRect(rect);
1991 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1992 secondLevelRoundingRadius, secondLevelRoundingRadius);
1993 } else {
1994 painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1995 }
1996
1997 // draw the check mark
1998 if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) {
1999 QStyleOptionViewItem option(*vopt);
2000 option.rect = checkRect;
2001 option.state = option.state & ~QStyle::State_HasFocus;
2002
2003 switch (vopt->checkState) {
2004 case Qt::Unchecked:
2005 option.state |= QStyle::State_Off;
2006 break;
2007 case Qt::PartiallyChecked:
2008 option.state |= QStyle::State_NoChange;
2009 break;
2010 case Qt::Checked:
2011 option.state |= QStyle::State_On;
2012 break;
2013 }
2014 proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget);
2015 }
2016
2017 // draw the icon
2018 if (iconRect.isValid()) {
2019 QIcon::Mode mode = QIcon::Normal;
2020 if (!(vopt->state & QStyle::State_Enabled))
2021 mode = QIcon::Disabled;
2022 else if (vopt->state & QStyle::State_Selected)
2023 mode = QIcon::Selected;
2024 QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
2025 vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state);
2026 }
2027
2028 if (highlightCurrent && highContrastTheme) {
2029 painter->setPen(vopt->palette.base().color());
2030 } else if ((view && view->alternatingRowColors() && highlightCurrent && vopt->state & State_Selected)) {
2031 painter->setPen(winUI3Color(textOnAccentPrimary));
2032 } else {
2033 painter->setPen(vopt->palette.text().color());
2034 }
2035 d->viewItemDrawText(painter, vopt, textRect);
2036
2037 // paint a vertical marker for QListView
2038 if (vopt->state & State_Selected && !highContrastTheme) {
2039 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2040 lv && lv->viewMode() != QListView::IconMode) {
2041 const auto col = vopt->palette.accent().color();
2042 painter->setBrush(col);
2043 painter->setPen(col);
2044 const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f;
2045 const auto yOfs = rect.height() / 4.;
2046 QRectF r(QPointF(xPos, rect.y() + yOfs),
2047 QPointF(xPos + 1, rect.y() + rect.height() - yOfs));
2048 painter->drawRoundedRect(r, 1, 1);
2049 }
2050 }
2051 }
2052 break;
2053 }
2054 default:
2055 QWindowsVistaStyle::drawControl(element, option, painter, widget);
2056 }
2057}
2058
2059int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt,
2060 const QWidget *widget, QStyleHintReturn *returnData) const {
2061 switch (hint) {
2062 case QStyle::SH_Menu_AllowActiveAndDisabled:
2063 return 0;
2064 case SH_GroupBox_TextLabelColor:
2065 if (opt!=nullptr && widget!=nullptr)
2066 return opt->palette.text().color().rgba();
2067 return 0;
2068 case QStyle::SH_ItemView_ShowDecorationSelected:
2069 return 1;
2070 case QStyle::SH_Slider_AbsoluteSetButtons:
2071 return Qt::LeftButton;
2072 case QStyle::SH_Slider_PageSetButtons:
2073 return 0;
2074 case SH_SpinControls_DisableOnBounds:
2075 return 1;
2076 default:
2077 return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData);
2078 }
2079}
2080
2081QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOption *option,
2082 const QWidget *widget) const
2083{
2084 QRect ret;
2085 switch (element) {
2086 case QStyle::SE_RadioButtonIndicator:
2087 case QStyle::SE_CheckBoxIndicator: {
2088 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2089 const auto ofs =
2090 QCommonStylePrivate::rtl(option) ? -contentItemHMargin : +contentItemHMargin;
2091 ret.moveLeft(ret.left() + ofs);
2092 break;
2093 }
2094 case QStyle::SE_ComboBoxFocusRect:
2095 case QStyle::SE_CheckBoxFocusRect:
2096 case QStyle::SE_RadioButtonFocusRect:
2097 case QStyle::SE_PushButtonFocusRect:
2098 ret = option->rect;
2099 break;
2100 case QStyle::SE_LineEditContents:
2101 ret = option->rect.adjusted(4,0,-4,0);
2102 break;
2103 case SE_ItemViewItemCheckIndicator:
2104 case SE_ItemViewItemDecoration:
2105 case SE_ItemViewItemText: {
2106 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2107 if (!ret.isValid() || highContrastTheme)
2108 return ret;
2109
2110 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2111 lv && lv->viewMode() != QListView::IconMode) {
2112 const int xOfs = contentHMargin;
2113 const bool isRtl = option->direction == Qt::RightToLeft;
2114 if (isRtl) {
2115 ret.moveRight(ret.right() - xOfs);
2116 if (ret.left() < option->rect.left())
2117 ret.setLeft(option->rect.left());
2118 } else {
2119 ret.moveLeft(ret.left() + xOfs);
2120 if (ret.right() > option->rect.right())
2121 ret.setRight(option->rect.right());
2122 }
2123 }
2124 break;
2125 }
2126#if QT_CONFIG(progressbar)
2127 case SE_ProgressBarGroove:
2128 case SE_ProgressBarContents:
2129 case SE_ProgressBarLabel:
2130 if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
2131 QStyleOptionProgressBar optCopy(*pb);
2132 // we only support label right from content
2133 optCopy.textAlignment = Qt::AlignRight;
2134 return QWindowsVistaStyle::subElementRect(element, &optCopy, widget);
2135 }
2136 break;
2137#endif // QT_CONFIG(progressbar)
2138#if QT_CONFIG(toolbar)
2139 case SE_TabBarScrollLeftButton:
2140 case SE_TabBarScrollRightButton: {
2141 const bool isRightButton = element == SE_TabBarScrollRightButton;
2142 const bool vertical = option->rect.width() < option->rect.height();
2143 const int buttonWidth = proxy()->pixelMetric(PM_TabBarScrollButtonWidth, option, widget);
2144
2145 if (vertical) {
2146 const int yOfs =
2147 isRightButton ? option->rect.height() - buttonWidth : 0;
2148 const QSize sz(option->rect.width(), buttonWidth);
2149 const QPoint tl(option->rect.topLeft() + QPoint(0, yOfs));
2150 ret = QRect(tl, sz);
2151 } else {
2152 const int xOfs =
2153 isRightButton ? option->rect.width() - buttonWidth : 0;
2154 const QSize sz(buttonWidth, option->rect.height());
2155 const QPoint tl(option->rect.topLeft() + QPoint(xOfs, 0));
2156 ret = QRect(tl, sz);
2157 ret = visualRect(widget->layoutDirection(), option->rect, ret);
2158 }
2159 break;
2160 }
2161 case SE_TabBarTearIndicatorLeft:
2162 case SE_TabBarTearIndicatorRight:
2163 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
2164 const bool isRightButton = element == SE_TabBarTearIndicatorRight;
2165 const int buttonWidth =
2166 proxy()->pixelMetric(PM_TabBarScrollButtonWidth, nullptr, widget);
2167 switch (tab->shape) {
2168 case QTabBar::RoundedNorth:
2169 case QTabBar::TriangularNorth:
2170 case QTabBar::RoundedSouth:
2171 case QTabBar::TriangularSouth: {
2172 const auto ofs = isRightButton
2173 ? option->rect.width() - 2 - buttonWidth - contentItemHMargin
2174 : 2 + buttonWidth + contentItemHMargin;
2175 const QPoint tl(tab->rect.topLeft() + QPoint(ofs, 0));
2176 ret = QRect(tl, QSize(1, option->rect.height()));
2177 break;
2178 }
2179 case QTabBar::RoundedWest:
2180 case QTabBar::TriangularWest:
2181 case QTabBar::RoundedEast:
2182 case QTabBar::TriangularEast: {
2183 const auto ofs = isRightButton
2184 ? option->rect.height() - 2 - buttonWidth - contentItemHMargin
2185 : 2 + buttonWidth + contentItemHMargin;
2186 const QPoint tl(tab->rect.topLeft() + QPoint(0, ofs));
2187 ret = QRect(tl, QSize(option->rect.width(), 1));
2188 break;
2189 }
2190 default:
2191 break;
2192 }
2193 ret = visualRect(option->direction, option->rect, ret);
2194 }
2195 break;
2196#endif // QT_CONFIG(toolbar)
2197 case QStyle::SE_HeaderLabel:
2198 case QStyle::SE_HeaderArrow:
2199 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2200 break;
2201 case SE_PushButtonContents: {
2202 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2203 ret = option->rect.marginsRemoved(QMargins(border, border, border, border));
2204 break;
2205 }
2206 default:
2207 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2208 }
2209 return ret;
2210}
2211
2212/*!
2213 \internal
2214 */
2215QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
2216 SubControl subControl, const QWidget *widget) const
2217{
2218 QRect ret;
2219
2220 switch (control) {
2221#if QT_CONFIG(spinbox)
2222 case CC_SpinBox:
2223 if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
2224 const bool hasButtons = spinbox->buttonSymbols != QAbstractSpinBox::NoButtons;
2225 const int fw = spinbox->frame
2226 ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget)
2227 : 0;
2228 const int buttonHeight = hasButtons
2229 ? qMin(spinbox->rect.height() - 3 * fw, spinbox->fontMetrics.height() * 5 / 4)
2230 : 0;
2231 const QSize buttonSize(buttonHeight * 6 / 5, buttonHeight);
2232 const int textFieldLength = spinbox->rect.width() - 2 * fw - 2 * buttonSize.width();
2233 const QPoint topLeft(spinbox->rect.topLeft() + QPoint(fw, fw));
2234 switch (subControl) {
2235 case SC_SpinBoxUp:
2236 case SC_SpinBoxDown: {
2237 if (!hasButtons)
2238 return QRect();
2239 const int yOfs = ((spinbox->rect.height() - 2 * fw) - buttonSize.height()) / 2;
2240 ret = QRect(topLeft.x() + textFieldLength, topLeft.y() + yOfs, buttonSize.width(),
2241 buttonSize.height());
2242 if (subControl == SC_SpinBoxDown)
2243 ret.moveRight(ret.right() + buttonSize.width());
2244 break;
2245 }
2246 case SC_SpinBoxEditField:
2247 ret = QRect(topLeft,
2248 spinbox->rect.bottomRight() - QPoint(fw + 2 * buttonSize.width(), fw));
2249 break;
2250 case SC_SpinBoxFrame:
2251 ret = spinbox->rect;
2252 default:
2253 break;
2254 }
2255 ret = visualRect(spinbox->direction, spinbox->rect, ret);
2256 }
2257 break;
2258 case CC_TitleBar:
2259 if (const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
2260 SubControl sc = subControl;
2261 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
2262 static constexpr int indent = 3;
2263 static constexpr int controlWidthMargin = 2;
2264 const int controlHeight = titlebar->rect.height();
2265 const int controlWidth = 46;
2266 const int iconSize = proxy()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, option, widget);
2267 int offset = -(controlWidthMargin + indent);
2268
2269 bool isMinimized = titlebar->titleBarState & Qt::WindowMinimized;
2270 bool isMaximized = titlebar->titleBarState & Qt::WindowMaximized;
2271
2272 switch (sc) {
2273 case SC_TitleBarLabel:
2274 if (titlebar->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
2275 ret = titlebar->rect;
2276 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2277 ret.adjust(iconSize + controlWidthMargin + indent, 0, -controlWidth, 0);
2278 if (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)
2279 ret.adjust(0, 0, -controlWidth, 0);
2280 if (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)
2281 ret.adjust(0, 0, -controlWidth, 0);
2282 if (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)
2283 ret.adjust(0, 0, -controlWidth, 0);
2284 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2285 ret.adjust(0, 0, -controlWidth, 0);
2286 }
2287 break;
2288 case SC_TitleBarContextHelpButton:
2289 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2290 offset += controlWidth;
2291 Q_FALLTHROUGH();
2292 case SC_TitleBarMinButton:
2293 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2294 offset += controlWidth;
2295 else if (sc == SC_TitleBarMinButton)
2296 break;
2297 Q_FALLTHROUGH();
2298 case SC_TitleBarNormalButton:
2299 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2300 offset += controlWidth;
2301 else if (isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2302 offset += controlWidth;
2303 else if (sc == SC_TitleBarNormalButton)
2304 break;
2305 Q_FALLTHROUGH();
2306 case SC_TitleBarMaxButton:
2307 if (!isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2308 offset += controlWidth;
2309 else if (sc == SC_TitleBarMaxButton)
2310 break;
2311 Q_FALLTHROUGH();
2312 case SC_TitleBarShadeButton:
2313 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2314 offset += controlWidth;
2315 else if (sc == SC_TitleBarShadeButton)
2316 break;
2317 Q_FALLTHROUGH();
2318 case SC_TitleBarUnshadeButton:
2319 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2320 offset += controlWidth;
2321 else if (sc == SC_TitleBarUnshadeButton)
2322 break;
2323 Q_FALLTHROUGH();
2324 case SC_TitleBarCloseButton:
2325 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2326 offset += controlWidth;
2327 else if (sc == SC_TitleBarCloseButton)
2328 break;
2329 ret.setRect(titlebar->rect.right() - offset, titlebar->rect.top(),
2330 controlWidth, controlHeight);
2331 break;
2332 case SC_TitleBarSysMenu:
2333 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) {
2334 const auto yOfs = titlebar->rect.top() + (titlebar->rect.height() - iconSize) / 2;
2335 ret.setRect(titlebar->rect.left() + controlWidthMargin + indent, yOfs, iconSize,
2336 iconSize);
2337 }
2338 break;
2339 default:
2340 break;
2341 }
2342 if (widget && isMinimized && titlebar->rect.width() < offset)
2343 const_cast<QWidget*>(widget)->resize(controlWidthMargin + indent + offset + iconSize + controlWidthMargin, controlWidth);
2344 ret = visualRect(titlebar->direction, titlebar->rect, ret);
2345 }
2346 break;
2347#endif // Qt_NO_SPINBOX
2348 case CC_ScrollBar:
2349 {
2350 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
2351
2352 if (subControl == SC_ScrollBarAddLine || subControl == SC_ScrollBarSubLine) {
2353 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2354 if (scrollbar->orientation == Qt::Vertical)
2355 ret = ret.adjusted(2,2,-2,-3);
2356 else
2357 ret = ret.adjusted(3,2,-2,-2);
2358 }
2359 }
2360 break;
2361 }
2362 case CC_ComboBox: {
2363 if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2364 const auto indicatorWidth =
2365 proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
2366 switch (subControl) {
2367 case SC_ComboBoxArrow: {
2368 const int fw =
2369 cb->frame ? proxy()->pixelMetric(PM_ComboBoxFrameWidth, cb, widget) : 0;
2370 const int buttonHeight =
2371 qMin(cb->rect.height() - 3 * fw, cb->fontMetrics.height() * 5 / 4);
2372 const QSize buttonSize(buttonHeight * 6 / 5, buttonHeight);
2373 const int textFieldLength = cb->rect.width() - 2 * fw - buttonSize.width();
2374 const QPoint topLeft(cb->rect.topLeft() + QPoint(fw, fw));
2375 const int yOfs = ((cb->rect.height() - 2 * fw) - buttonSize.height()) / 2;
2376 ret = QRect(topLeft.x() + textFieldLength, topLeft.y() + yOfs, buttonSize.width(),
2377 buttonSize.height());
2378 ret = visualRect(option->direction, option->rect, ret);
2379 break;
2380 }
2381 case SC_ComboBoxEditField: {
2382 ret = option->rect;
2383 if (cb->frame) {
2384 const int fw = proxy()->pixelMetric(PM_ComboBoxFrameWidth, cb, widget);
2385 ret = ret.marginsRemoved(QMargins(fw, fw, fw, fw));
2386 }
2387 ret.setWidth(ret.width() - indicatorWidth - contentHMargin * 2);
2388 ret = visualRect(option->direction, option->rect, ret);
2389 break;
2390 }
2391 default:
2392 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2393 break;
2394 }
2395 }
2396 break;
2397 }
2398#if QT_CONFIG(groupbox)
2399 case CC_GroupBox: {
2400 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2401 switch (subControl) {
2402 case SC_GroupBoxCheckBox:
2403 ret.moveTop(1);
2404 break;
2405 default:
2406 break;
2407 }
2408 break;
2409 }
2410#endif // QT_CONFIG(groupbox)
2411 default:
2412 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2413 }
2414 return ret;
2415}
2416
2417/*!
2418 \internal
2419 */
2420QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *option,
2421 const QSize &size, const QWidget *widget) const
2422{
2423 QSize contentSize(size);
2424
2425 switch (type) {
2426
2427#if QT_CONFIG(menubar)
2428 case CT_MenuBarItem:
2429 if (!contentSize.isEmpty()) {
2430 constexpr int hMargin = 2 * 6;
2431 constexpr int hPadding = 2 * 11;
2432 constexpr int itemHeight = 32;
2433 contentSize.setWidth(contentSize.width() + hMargin + hPadding);
2434#if QT_CONFIG(tabwidget)
2435 if (widget->parent() && !qobject_cast<const QTabWidget *>(widget->parent()))
2436#endif
2437 contentSize.setHeight(itemHeight);
2438 }
2439 break;
2440#endif
2441#if QT_CONFIG(menu)
2442 case CT_MenuItem:
2443 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2444 int width = size.width();
2445 int height;
2446 if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
2447 width = 10;
2448 height = 3;
2449 } else {
2450 height = menuItem->fontMetrics.height() + 8;
2451 if (!menuItem->icon.isNull()) {
2452 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2453 height = qMax(height,
2454 menuItem->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
2455 }
2456 }
2457 if (menuItem->text.contains(u'\t'))
2458 width += contentItemHMargin; // the text width is already in
2459 if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu)
2460 width += 2 * QWindowsStylePrivate::windowsArrowHMargin + contentItemHMargin;
2461 if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) {
2462 const QFontMetrics fm(menuItem->font);
2463 QFont fontBold = menuItem->font;
2464 fontBold.setBold(true);
2465 const QFontMetrics fmBold(fontBold);
2466 width += fmBold.horizontalAdvance(menuItem->text) - fm.horizontalAdvance(menuItem->text);
2467 }
2468 // in contrast to windowsvista, the checkmark and icon are drawn separately
2469 if (menuItem->menuHasCheckableItems) {
2470 const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget);
2471 width += checkMarkWidth + contentItemHMargin * 2;
2472 }
2473 // we have an icon and it's already in the given size, only add margins
2474 // 4 is added in qmenu.cpp to PM_SmallIconSize
2475 if (menuItem->maxIconWidth > 0)
2476 width += contentItemHMargin * 2 + menuItem->maxIconWidth - 4;
2477 width += 2 * 2; // margins for rounded border
2478 width += 2 * contentHMargin;
2479 if (width < 100) // minimum size
2480 width = 100;
2481 contentSize = QSize(width, height);
2482 }
2483 break;
2484#endif // QT_CONFIG(menu)
2485#if QT_CONFIG(spinbox)
2486 case CT_SpinBox: {
2487 if (const auto *spinBoxOpt = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
2488 // Add button + frame widths
2489 const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons);
2490 const int margins = 8;
2491 const int buttonWidth = hasButtons ? 16 + contentItemHMargin : 0;
2492 const int frameWidth = spinBoxOpt->frame
2493 ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget)
2494 : 0;
2495
2496 contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth);
2497 }
2498 break;
2499 }
2500#endif
2501#if QT_CONFIG(combobox)
2502 case CT_ComboBox:
2503 if (const auto *comboBoxOpt = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2504 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
2505 contentSize += QSize(0, 4); // for the lineedit frame
2506 if (comboBoxOpt->subControls & SC_ComboBoxArrow) {
2507 const auto w = proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
2508 contentSize.rwidth() += w + contentItemHMargin;
2509 }
2510 }
2511 break;
2512#endif
2513 case CT_LineEdit: {
2514 if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
2515 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
2516 contentSize += QSize(0, 4); // for the lineedit frame
2517 }
2518 break;
2519 }
2520 case CT_HeaderSection:
2521 // windows vista does not honor the indicator (as it was drawn above the text, not on the
2522 // side) so call QWindowsStyle::styleHint directly to get the correct size hint
2523 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
2524 break;
2525 case CT_RadioButton:
2526 case CT_CheckBox:
2527 if (const auto *buttonOpt = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2528 const auto p = proxy();
2529 const bool isRadio = (type == CT_RadioButton);
2530
2531 const int width = p->pixelMetric(
2532 isRadio ? PM_ExclusiveIndicatorWidth : PM_IndicatorWidth, option, widget);
2533 const int height = p->pixelMetric(
2534 isRadio ? PM_ExclusiveIndicatorHeight : PM_IndicatorHeight, option, widget);
2535
2536 int margins = 2 * contentItemHMargin;
2537 if (!buttonOpt->icon.isNull() || !buttonOpt->text.isEmpty()) {
2538 margins += p->pixelMetric(isRadio ? PM_RadioButtonLabelSpacing
2539 : PM_CheckBoxLabelSpacing,
2540 option, widget);
2541 }
2542
2543 contentSize += QSize(width + margins, 4);
2544 contentSize.setHeight(qMax(size.height(), height + 2 * contentItemHMargin));
2545 }
2546 break;
2547
2548 // the indicator needs 2px more in width when there is no text, not needed when
2549 // the style draws the text
2550 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2551 if (size.width() == 0)
2552 contentSize.rwidth() += 2;
2553 break;
2554 case CT_PushButton: {
2555 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2556 // we want our own horizontal spacing
2557 const int oldMargin = proxy()->pixelMetric(PM_ButtonMargin, option, widget);
2558 contentSize.rwidth() += 2 * contentHMargin - oldMargin;
2559 break;
2560 }
2561 case CT_ToolButton: {
2562 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2563 // we want our own horizontal spacing
2564 const int oldMargin = proxy()->pixelMetric(PM_ButtonMargin, option, widget);
2565 contentSize.rwidth() += 2 * contentHMargin - oldMargin;
2566 if (const auto toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
2567 if (toolbutton->features.testFlag(QStyleOptionToolButton::HasMenu))
2568 contentSize.rwidth() += size.height();
2569 }
2570 break;
2571 }
2572 case CT_ItemViewItem: {
2573 if (const auto *viewItemOpt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
2574 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2575 lv && lv->viewMode() != QListView::IconMode) {
2576 QStyleOptionViewItem vOpt(*viewItemOpt);
2577 // viewItemSize only takes PM_FocusFrameHMargin into account but no additional
2578 // margin, therefore adjust it here for a correct width during layouting when
2579 // WrapText is enabled
2580 vOpt.rect.setRight(vOpt.rect.right() - contentHMargin);
2581 contentSize = QWindowsVistaStyle::sizeFromContents(type, &vOpt, size, widget);
2582 contentSize.rwidth() += contentHMargin;
2583 contentSize.rheight() += 2 * contentHMargin;
2584
2585 } else {
2586 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2587 }
2588 }
2589 break;
2590 }
2591 default:
2592 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2593 break;
2594 }
2595
2596 return contentSize;
2597}
2598
2599
2600/*!
2601 \internal
2602 */
2603int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
2604{
2605 Q_D(const QWindows11Style);
2606 int res = 0;
2607
2608 switch (metric) {
2609 case QStyle::PM_IndicatorWidth:
2610 case QStyle::PM_IndicatorHeight:
2611 case QStyle::PM_ExclusiveIndicatorWidth:
2612 case QStyle::PM_ExclusiveIndicatorHeight:
2613 res = 16;
2614 break;
2615 case PM_SliderThickness: // full height of a slider
2616 if (auto opt = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2617 // hard-coded in qslider.cpp, but we need a little bit more
2618 constexpr auto TickSpace = 5;
2619 if (opt->tickPosition & QSlider::TicksAbove)
2620 res += 6 - TickSpace;
2621 if (opt->tickPosition & QSlider::TicksBelow)
2622 res += 6 - TickSpace;
2623 }
2624 Q_FALLTHROUGH();
2625 case PM_SliderControlThickness: // size of the control handle
2626 case PM_SliderLength: // same because handle is a circle with r=8
2627 res += 2 * 8;
2628 break;
2629 case PM_RadioButtonLabelSpacing:
2630 case PM_CheckBoxLabelSpacing:
2631 res = 2 * contentItemHMargin;
2632 break;
2633 case QStyle::PM_TitleBarButtonIconSize:
2634 res = 16;
2635 break;
2636 case QStyle::PM_TitleBarButtonSize:
2637 res = 32;
2638 break;
2639#if QT_CONFIG(toolbar)
2640 case PM_TabBarScrollButtonWidth:
2641 res = 16 + contentItemHMargin;
2642 break;
2643 case PM_ToolBarExtensionExtent:
2644 res = int(QStyleHelper::dpiScaled(32., option));
2645 break;
2646 case PM_ToolBarHandleExtent:
2647 res = int(QStyleHelper::dpiScaled(8., option));
2648 break;
2649#endif // QT_CONFIG(toolbar)
2650 case QStyle::PM_ScrollBarExtent:
2651 res = 12;
2652 break;
2653 case QStyle::PM_SubMenuOverlap:
2654 res = -1;
2655 break;
2656 case PM_MenuButtonIndicator: {
2657 res = contentItemHMargin;
2658 if (widget) {
2659 const int fontSize = widget->font().pointSize();
2660 auto it = m_fontPoint2ChevronDownMedWidth.find(fontSize);
2661 if (it == m_fontPoint2ChevronDownMedWidth.end()) {
2662 QFont f(d->assetFont);
2663 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
2664 QFontMetrics fm(f);
2665 const auto width = fm.horizontalAdvance(fluentIcon(Icon::ChevronDownMed));
2666 m_fontPoint2ChevronDownMedWidth.insert(fontSize, width);
2667 res += width;
2668 } else {
2669 res += it.value();
2670 }
2671 } else {
2672 res += 12;
2673 }
2674 break;
2675 }
2676 case PM_ComboBoxFrameWidth:
2677 case PM_SpinBoxFrameWidth:
2678 case PM_DefaultFrameWidth:
2679 res = 2;
2680 break;
2681 case PM_ButtonShiftHorizontal:
2682 case PM_ButtonShiftVertical:
2683 case PM_TabBarTabShiftHorizontal:
2684 case PM_TabBarTabShiftVertical:
2685 case PM_TabBarBaseOverlap:
2686 case PM_TabBarBaseHeight:
2687 res = 0;
2688 break;
2689 case PM_TreeViewIndentation:
2690 res = 30;
2691 break;
2692 case PM_ProgressBarChunkWidth:
2693 res = 0; // no chunks on windows11
2694 break;
2695 default:
2696 res = QWindowsVistaStyle::pixelMetric(metric, option, widget);
2697 }
2698
2699 return res;
2700}
2701
2702void QWindows11Style::polish(QWidget* widget)
2703{
2704 Q_D(QWindows11Style);
2705
2706#if QT_CONFIG(commandlinkbutton)
2707 if (!qobject_cast<QCommandLinkButton *>(widget))
2708#endif // QT_CONFIG(commandlinkbutton)
2709 QWindowsVistaStyle::polish(widget);
2710
2711 const bool isScrollBar = qobject_cast<QScrollBar *>(widget);
2712 const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
2713 const auto isComboBoxContainer = !d->nativeRoundedTopLevelWindows && comboBoxContainer != nullptr;
2714 const auto isMenu = !d->nativeRoundedTopLevelWindows && qobject_cast<QMenu *>(widget) != nullptr;
2715
2716#if QT_CONFIG(menubar)
2717 if (qobject_cast<QMenuBar *>(widget)) {
2718 constexpr int itemHeight = 32;
2719 if (widget->maximumHeight() < itemHeight) {
2720 widget->setProperty("_q_original_menubar_maxheight", widget->maximumHeight());
2721 widget->setMaximumHeight(itemHeight);
2722 }
2723 }
2724#endif
2725 if (comboBoxContainer && d->nativeRoundedTopLevelWindows) {
2726 auto view = comboBoxContainer->itemView();
2727 if (view && view->viewport())
2728 view->viewport()->setAutoFillBackground(false);
2729 }
2730 if (isScrollBar || isMenu || isComboBoxContainer) {
2731 bool wasCreated = widget->testAttribute(Qt::WA_WState_Created);
2732 bool layoutDirection = widget->testAttribute(Qt::WA_RightToLeft);
2733 widget->setAttribute(Qt::WA_OpaquePaintEvent,false);
2734 widget->setAttribute(Qt::WA_TranslucentBackground);
2735 if (!isScrollBar)
2736 widget->setWindowFlag(Qt::FramelessWindowHint);
2737 widget->setWindowFlag(Qt::NoDropShadowWindowHint);
2738 widget->setAttribute(Qt::WA_RightToLeft, layoutDirection);
2739 widget->setAttribute(Qt::WA_WState_Created, wasCreated);
2740 if (!isScrollBar) {
2741 bool inGraphicsView = widget->graphicsProxyWidget() != nullptr;
2742 if (!inGraphicsView && comboBoxContainer && comboBoxContainer->parentWidget())
2743 inGraphicsView = comboBoxContainer->parentWidget()->graphicsProxyWidget() != nullptr;
2744 if (!inGraphicsView) { // for menus and combobox containers...
2745 QGraphicsDropShadowEffect *dropshadow = new QGraphicsDropShadowEffect(widget);
2746 dropshadow->setBlurRadius(3);
2747 dropshadow->setXOffset(3);
2748 dropshadow->setYOffset(3);
2749 widget->setGraphicsEffect(dropshadow);
2750 }
2751 }
2752 } else if (QComboBox* cb = qobject_cast<QComboBox*>(widget)) {
2753 if (cb->isEditable()) {
2754 QLineEdit *le = cb->lineEdit();
2755 le->setFrame(false);
2756 }
2757 } else if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2758 scrollarea
2759 && !qobject_cast<QGraphicsView *>(widget)
2760#if QT_CONFIG(mdiarea)
2761 && !qobject_cast<QMdiArea *>(widget)
2762#endif
2763 ) {
2764 if (scrollarea->frameShape() == QFrame::StyledPanel) {
2765 const auto vp = scrollarea->viewport();
2766 const bool isAutoFillBackground = vp->autoFillBackground();
2767 const bool isStyledBackground = vp->testAttribute(Qt::WA_StyledBackground);
2768 vp->setProperty("_q_original_autofill_background", isAutoFillBackground);
2769 vp->setProperty("_q_original_styled_background", isStyledBackground);
2770 vp->setAutoFillBackground(false);
2771 vp->setAttribute(Qt::WA_StyledBackground, true);
2772 }
2773 // QTreeView & QListView are already set in the base windowsvista style
2774 if (auto table = qobject_cast<QTableView *>(widget))
2775 table->viewport()->setAttribute(Qt::WA_Hover, true);
2776 }
2777}
2778
2779void QWindows11Style::unpolish(QWidget *widget)
2780{
2781 Q_D(QWindows11Style);
2782#if QT_CONFIG(commandlinkbutton)
2783 if (!qobject_cast<QCommandLinkButton *>(widget))
2784#endif // QT_CONFIG(commandlinkbutton)
2785 QWindowsVistaStyle::unpolish(widget);
2786
2787#if QT_CONFIG(menubar)
2788 if (qobject_cast<QMenuBar *>(widget) && !widget->property("_q_original_menubar_maxheight").isNull()) {
2789 widget->setMaximumHeight(widget->property("_q_original_menubar_maxheight").toInt());
2790 widget->setProperty("_q_original_menubar_maxheight", QVariant());
2791 }
2792#endif
2793 const bool isScrollBar = qobject_cast<QScrollBar *>(widget);
2794 const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
2795 const auto isComboBoxContainer = !d->nativeRoundedTopLevelWindows && comboBoxContainer != nullptr;
2796 const auto isMenu = !d->nativeRoundedTopLevelWindows && qobject_cast<QMenu *>(widget) != nullptr;
2797
2798 if (comboBoxContainer && d->nativeRoundedTopLevelWindows) {
2799 auto view = comboBoxContainer->itemView();
2800 // see QAbstractScrollAreaPrivate::init()
2801 if (view && view->viewport())
2802 view->viewport()->setAutoFillBackground(true);
2803 }
2804 if (isScrollBar || isMenu || isComboBoxContainer) {
2805 widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
2806 widget->setAttribute(Qt::WA_TranslucentBackground, false);
2807 widget->setWindowFlag(Qt::FramelessWindowHint, false);
2808 widget->setWindowFlag(Qt::NoDropShadowWindowHint, false);
2809 }
2810
2811 if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2812 scrollarea
2813#if QT_CONFIG(mdiarea)
2814 && !qobject_cast<QMdiArea *>(widget)
2815#endif
2816 ) {
2817 const auto vp = scrollarea->viewport();
2818 const auto wasAutoFillBackground = vp->property("_q_original_autofill_background").toBool();
2819 vp->setAutoFillBackground(wasAutoFillBackground);
2820 vp->setProperty("_q_original_autofill_background", QVariant());
2821 const auto origStyledBackground = vp->property("_q_original_styled_background").toBool();
2822 vp->setAttribute(Qt::WA_StyledBackground, origStyledBackground);
2823 vp->setProperty("_q_original_styled_background", QVariant());
2824 }
2825 dwmSetWindowCornerPreference(widget, false);
2826}
2827
2828void QWindows11Style::polish(QApplication *app)
2829{
2830 Q_D(const QWindows11Style);
2831 if (d->nativeRoundedTopLevelWindows)
2832 app->installEventFilter(this);
2833 QWindowsVistaStyle::polish(app);
2834}
2835
2836void QWindows11Style::unpolish(QApplication *app)
2837{
2838 Q_D(const QWindows11Style);
2839 if (d->nativeRoundedTopLevelWindows)
2840 app->removeEventFilter(this);
2841 QWindowsVistaStyle::unpolish(app);
2842}
2843
2844bool QWindows11Style::eventFilter(QObject *obj, QEvent *event)
2845{
2846 // QEvent::WinIdChange is to early so we have to wait for QEvent::Show
2847 if (event->type() == QEvent::Show)
2848 dwmSetWindowCornerPreference(qobject_cast<QWidget *>(obj), true);
2849 return QWindowsVistaStyle::eventFilter(obj, event);
2850}
2851
2852void QWindows11Style::dwmSetWindowCornerPreference(const QWidget *widget, bool bSet) const
2853{
2854 Q_D(const QWindows11Style);
2855 if (!d->nativeRoundedTopLevelWindows)
2856 return;
2857#ifdef Q_CC_MSVC
2858 static constexpr auto dmwmaWindowCornerPreference = DWMWA_WINDOW_CORNER_PREFERENCE;
2859 static constexpr auto dwmcpRound = DWMWCP_ROUND;
2860 static constexpr auto dwmcpRoundSmall = DWMWCP_ROUNDSMALL;
2861 static constexpr auto dwmcpDefault = DWMWCP_DEFAULT;
2862#else
2863 // MinGW 13.1.0 does not provide this
2864 static constexpr auto dmwmaWindowCornerPreference = 33;
2865 static constexpr auto dwmcpRound = 2;
2866 static constexpr auto dwmcpRoundSmall = 3;
2867 static constexpr auto dwmcpDefault = 0;
2868#endif
2869 if (widget && widget->isWindow() && widget->testAttribute(Qt::WA_WState_Created)) {
2870 const auto window = widget->windowHandle();
2871 if (window && window->handle()) {
2872 const auto wId = reinterpret_cast<HWND>(widget->winId());
2873 if (wId != 0) {
2874 const bool isToolTip =
2875#if QT_CONFIG(tooltip)
2876 qobject_cast<const QTipLabel *>(widget) != nullptr;
2877#else
2878 false;
2879#endif
2880 uint32_t pref = bSet ? (isToolTip ? dwmcpRoundSmall : dwmcpRound) : dwmcpDefault;
2881 DwmSetWindowAttribute(wId, dmwmaWindowCornerPreference, &pref, sizeof(pref));
2882 }
2883 }
2884 }
2885}
2886
2887/*
2888The colors for Windows 11 are taken from the official WinUI3 Figma style at
2889https://www.figma.com/community/file/1159947337437047524
2890*/
2891#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE)
2892 if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged)
2893 result.setColor(GROUP, ROLE, VALUE)
2894
2895static void populateLightSystemBasePalette(QPalette &result)
2896{
2897 static QString oldStyleSheet;
2898 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2899
2900 constexpr QColor textColor = QColor(0x00,0x00,0x00,0xE4);
2901 constexpr QColor textDisabled = QColor(0x00, 0x00, 0x00, 0x5C);
2902 constexpr QColor btnFace = QColor(0xFF, 0xFF, 0xFF, 0xB3);
2903 constexpr QColor base = QColor(0xFF, 0xFF, 0xFF, 0xFF);
2904 constexpr QColor alternateBase = QColor(0x00, 0x00, 0x00, 0x09);
2905 const QColor btnHighlight = result.accent().color();
2906 const QColor btnColor = result.button().color();
2907
2908 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Highlight, btnHighlight);
2909 SET_IF_UNRESOLVED(QPalette::Active, QPalette::WindowText, textColor);
2910 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Button, btnFace);
2911 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Light, btnColor.lighter(150));
2912 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Dark, btnColor.darker(200));
2913 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Mid, btnColor.darker(150));
2914 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Text, textColor);
2915 SET_IF_UNRESOLVED(QPalette::Active, QPalette::BrightText, btnHighlight);
2916 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Base, base);
2917 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2918 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ButtonText, textColor);
2919 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Midlight, btnColor.lighter(125));
2920 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Shadow, Qt::black);
2921 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipBase, result.window().color());
2922 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipText, result.windowText().color());
2923 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2924
2925 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, btnHighlight);
2926 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, textColor);
2927 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, btnFace);
2928 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, btnColor.lighter(150));
2929 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, btnColor.darker(200));
2930 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Mid, btnColor.darker(150));
2931 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, textColor);
2932 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::BrightText, btnHighlight);
2933 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Base, base);
2934 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2935 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ButtonText, textColor);
2936 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Midlight, btnColor.lighter(125));
2937 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Shadow, Qt::black);
2938 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipBase, result.window().color());
2939 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipText, result.windowText().color());
2940 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2941
2942 result.setColor(QPalette::Disabled, QPalette::WindowText, textDisabled);
2943
2944 if (result.midlight() == result.button())
2945 result.setColor(QPalette::Midlight, btnColor.lighter(110));
2946 oldStyleSheet = qApp->styleSheet();
2947}
2948
2949static void populateDarkSystemBasePalette(QPalette &result)
2950{
2951 static QString oldStyleSheet;
2952 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2953
2954 constexpr QColor alternateBase = QColor(0xFF, 0xFF, 0xFF, 0x0F);
2955
2956 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2957
2958 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2959
2960 oldStyleSheet = qApp->styleSheet();
2961}
2962
2963/*!
2964 \internal
2965 */
2966void QWindows11Style::polish(QPalette& result)
2967{
2968 highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown;
2969 colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
2970
2971 if (!highContrastTheme && colorSchemeIndex == 0)
2972 populateLightSystemBasePalette(result);
2973 else if (!highContrastTheme && colorSchemeIndex == 1)
2974 populateDarkSystemBasePalette(result);
2975
2976 const bool styleSheetChanged = false; // so the macro works
2977
2978 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color());
2979 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color());
2980 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color());
2981 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color());
2982 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color());
2983 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color());
2984 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color());
2985 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color());
2986 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color());
2987
2988 auto *d = const_cast<QWindows11StylePrivate *>(d_func());
2989 d->m_standardIcons.clear();
2990}
2991
2992QPixmap QWindows11Style::standardPixmap(StandardPixmap standardPixmap,
2993 const QStyleOption *option,
2994 const QWidget *widget) const
2995{
2996 switch (standardPixmap) {
2997 case SP_TabScrollLeftButton:
2998 case SP_TabScrollRightButton:
2999 case SP_TabScrollUpButton:
3000 case SP_TabScrollDownButton:
3001 case SP_ToolBarHorizontalExtensionButton:
3002 case SP_ToolBarVerticalExtensionButton: {
3003 const int size = proxy()->pixelMetric(PM_ToolBarExtensionExtent, option, widget);
3004 return standardIcon(standardPixmap, option, widget).pixmap(size);
3005 }
3006 default:
3007 break;
3008 }
3009 return QWindowsVistaStyle::standardPixmap(standardPixmap, option, widget);
3010}
3011
3012QIcon QWindows11Style::standardIcon(StandardPixmap standardIcon,
3013 const QStyleOption *option,
3014 const QWidget *widget) const
3015{
3016 const auto getIcon = [&](Icon ico, double scale) {
3017 auto *d = const_cast<QWindows11StylePrivate *>(d_func());
3018 QIcon &icon = d->m_standardIcons[ico];
3019 if (icon.isNull()) {
3020 auto engine = new WinFontIconEngine(fluentIcon(ico), d->assetFont);
3021 engine->setScale(scale);
3022 icon = QIcon(engine);
3023 }
3024 return icon;
3025 };
3026
3027 switch (standardIcon) {
3028 case SP_TitleBarMinButton:
3029 return getIcon(Icon::ChromeMinimize, 0.7);
3030 case SP_TitleBarMaxButton:
3031 return getIcon(Icon::ChromeMaximize, 0.7);
3032 case SP_TitleBarCloseButton:
3033 return getIcon(Icon::ChromeClose, 0.7);
3034 case SP_TitleBarNormalButton:
3035 return getIcon(Icon::ChromeRestore, 0.7);
3036 case SP_LineEditClearButton:
3037 return getIcon(Icon::Clear, 0.7);
3038 case SP_ToolBarHorizontalExtensionButton:
3039 case SP_ToolBarVerticalExtensionButton:
3040 return getIcon(Icon::More, 1.0);
3041 case SP_TabCloseButton:
3042 return getIcon(Icon::ChromeClose, 0.6);
3043 case SP_TabScrollLeftButton:
3044 return getIcon(Icon::CaretLeftSolid8, 0.5);
3045 case SP_TabScrollRightButton:
3046 return getIcon(Icon::CaretRightSolid8, 0.5);
3047 case SP_TabScrollUpButton:
3048 return getIcon(Icon::CaretUpSolid8, 0.5);
3049 case SP_TabScrollDownButton:
3050 return getIcon(Icon::CaretDownSolid8, 0.5);
3051 default:
3052 break;
3053 }
3054 return QWindowsVistaStyle::standardIcon(standardIcon, option, widget);
3055}
3056
3057QColor QWindows11Style::calculateAccentColor(const QStyleOption *option) const
3058{
3059 using namespace StyleOptionHelper;
3060 if (isDisabled(option))
3061 return winUI3Color(fillAccentDisabled);
3062 const auto alphaColor = isPressed(option) ? fillAccentTertiary
3063 : isHover(option) ? fillAccentSecondary
3064 : fillAccentDefault;
3065 const auto alpha = winUI3Color(alphaColor);
3066 QColor col = option->palette.accent().color();
3067 col.setAlpha(alpha.alpha());
3068 return col;
3069}
3070
3071QPen QWindows11Style::borderPenControlAlt(const QStyleOption *option) const
3072{
3073 using namespace StyleOptionHelper;
3074 if (isChecked(option))
3075 return Qt::NoPen; // same color as fill color, so no pen needed
3076 if (highContrastTheme)
3077 return option->palette.buttonText().color();
3078 if (isDisabled(option) || isPressed(option))
3079 return winUI3Color(frameColorStrongDisabled);
3080 return winUI3Color(frameColorStrong);
3081}
3082
3083QBrush QWindows11Style::controlFillBrush(const QStyleOption *option, ControlType controlType) const
3084{
3085 using namespace StyleOptionHelper;
3086 static constexpr WINUI3Color colorEnums[2][4] = {
3087 // Light & Dark Control
3090 // Light & Dark Control Alt
3093 };
3094
3095 if (option->palette.isBrushSet(QPalette::Current, QPalette::Button))
3096 return option->palette.button();
3097
3098 if (!isChecked(option) && isAutoRaise(option))
3099 return Qt::NoBrush;
3100
3101 // checked is the same for Control (Buttons) and Control Alt (Radiobuttons/Checkboxes)
3102 if (isChecked(option))
3103 return calculateAccentColor(option);
3104
3105 const auto state = calcControlState(option);
3106 return winUI3Color(colorEnums[int(controlType)][int(state)]);
3107}
3108
3109QBrush QWindows11Style::inputFillBrush(const QStyleOption *option, const QWidget *widget) const
3110{
3111 // slightly different states than in controlFillBrush
3112 using namespace StyleOptionHelper;
3113 const auto role = widget ? widget->backgroundRole() : QPalette::Window;
3114 if (option->palette.isBrushSet(QPalette::Current, role))
3115 return option->palette.button();
3116
3117 if (isDisabled(option))
3118 return winUI3Color(fillControlDisabled);
3119 if (hasFocus(option))
3120 return winUI3Color(fillControlInputActive);
3121 if (isHover(option))
3122 return winUI3Color(fillControlSecondary);
3123 return winUI3Color(fillControlDefault);
3124}
3125
3126QColor QWindows11Style::controlTextColor(const QStyleOption *option, bool ignoreIsChecked) const
3127{
3128 using namespace StyleOptionHelper;
3129 static constexpr WINUI3Color colorEnums[2][4] = {
3130 // Control, unchecked
3132 // Control, checked
3134 };
3135
3136 if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText))
3137 return option->palette.buttonText().color();
3138
3139 const int colorIndex = !ignoreIsChecked && isChecked(option) ? 1 : 0;
3140 const auto state = calcControlState(option);
3141 return winUI3Color(colorEnums[colorIndex][int(state)]);
3142}
3143
3144void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const
3145{
3146 const bool isHovered = o->state & State_MouseOver;
3147 const auto frameCol = highContrastTheme
3148 ? o->palette.color(isHovered ? QPalette::Accent
3149 : QPalette::ButtonText)
3150 : winUI3Color(frameColorLight);
3151 drawRoundedRect(p, rect, frameCol, Qt::NoBrush);
3152
3153 if (!isEditable || StyleOptionHelper::isDisabled(o))
3154 return;
3155
3156 const bool hasFocus = o->state & State_HasFocus;
3157 const auto underlineCol = hasFocus
3158 ? o->palette.color(QPalette::Accent)
3159 : colorSchemeIndex == 0 ? QColor(0x80, 0x80, 0x80)
3160 : QColor(0xa0, 0xa0, 0xa0);
3161 QPainterStateGuard psg(p);
3162 if (hasFocus) {
3163 p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 1.5, 0, -1)));
3164 const QRectF r(rect.top(), rect.left(), rect.width(), rect.height() - 1);
3165 drawRoundedRect(p, r, QPen(underlineCol, 2), Qt::NoBrush);
3166 } else {
3167 p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 0.5, 0, -1)));
3168 drawRoundedRect(p, rect, underlineCol, Qt::NoBrush);
3169 }
3170}
3171
3172QColor QWindows11Style::winUI3Color(enum WINUI3Color col) const
3173{
3174 return WINUI3Colors[colorSchemeIndex][col];
3175}
3176
3177#undef SET_IF_UNRESOLVED
3178
3179QT_END_NAMESPACE
friend class QPainter
QPainter * painter() const
Returns the paint engine's painter.
\inmodule QtCore\reentrant
Definition qpoint.h:232
\inmodule QtCore\reentrant
Definition qpoint.h:30
bool isDisabled(const QStyleOption *option)
ControlState calcControlState(const QStyleOption *option)
bool hasFocus(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
static constexpr std::array< std::array< QColor, 37 >, 2 > WINUI3Colors
#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE)
static constexpr std::array< QColor, 37 > WINUI3ColorsDark
static constexpr QColor shellCaptionCloseFillColorSecondary(0xC4, 0x2B, 0x1C, 0xE6)
static qreal sliderInnerRadius(QStyle::State state, bool insideHandle)
static qreal radioButtonInnerRadius(QStyle::State state)
static constexpr QColor shellCaptionCloseFillColorPrimary(0xC4, 0x2B, 0x1C, 0xFF)
static constexpr int contentHMargin
static void populateLightSystemBasePalette(QPalette &result)
static constexpr std::array< QColor, 37 > WINUI3ColorsLight
static constexpr int percentToAlpha(double percent)
static void populateDarkSystemBasePalette(QPalette &result)
static constexpr int contentItemHMargin
static constexpr QColor shellCaptionCloseTextFillColorPrimary(0xFF, 0xFF, 0xFF, 0xFF)
static void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush, int radius=secondLevelRoundingRadius)
static constexpr int topLevelRoundingRadius
static constexpr QColor shellCaptionCloseTextFillColorSecondary(0xFF, 0xFF, 0xFF, 0xB3)
static constexpr int secondLevelRoundingRadius
WINUI3Color
@ fillControlAltSecondary
@ fillControlTertiary
@ textOnAccentPrimary
@ fillControlDefault
@ fillControlDisabled
@ subtleHighlightColor
@ textPrimary
@ textDisabled
@ fillControlSecondary
@ textSecondary
@ textOnAccentSecondary
@ textOnAccentDisabled
@ fillControlAltQuarternary
@ fillControlAltTertiary
@ fillControlAltDisabled