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
826 if (toolbutton->subControls.testFlag(SC_ToolButtonMenu)) {
827 QPainterStateGuard psg(painter);
828 painter->setClipRect(menuareaRect);
829 tool.state = mflags;
830 prx->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
831
832 const int fontSize = painter->font().pointSize();
833 QFont f(d->assetFont);
834 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
835 painter->setFont(f);
836 painter->setPen(controlTextColor(option));
837 const QRect textRect(menuareaRect.topLeft(), menuareaRect.size() - QSize(fw, 0));
838 painter->drawText(textRect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
839
840 } else if (toolbutton->features.testFlag(QStyleOptionToolButton::HasMenu)) {
841 const int mbi = qRound(prx->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget) * 0.3);
842 const QRect &ir = toolbutton->rect;
843 QRect rect(ir.right() - mbi - 2 * fw, ir.bottom() - mbi - fw, mbi,
844 mbi);
845 rect = visualRect(toolbutton->direction, buttonRect, rect);
846 const QRect lblRect = label.rect - QMargins(0, 0, rect.width(), 0);
847 label.rect = visualRect(toolbutton->direction, buttonRect, lblRect);
848 painter->setFont(QFont(d->assetFont));
849 painter->setPen(controlTextColor(option));
850 painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
851 }
852 prx->drawControl(CE_ToolButtonLabel, &label, painter, widget);
853 }
854 break;
855#endif // QT_CONFIG(toolbutton)
856 default:
857 QWindowsVistaStyle::drawComplexControl(control, option, painter, widget);
858 }
859}
860
861void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
862 QPainter *painter,
863 const QWidget *widget) const {
864 QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func());
865
866 const State state = option->state;
867 QPainterStateGuard psg(painter);
868 painter->setRenderHint(QPainter::Antialiasing);
869 if (d->transitionsEnabled() && option->styleObject && (element == PE_IndicatorCheckBox || element == PE_IndicatorRadioButton)) {
870 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
871 int oldState = styleObject->property("_q_stylestate").toInt();
872 styleObject->setProperty("_q_stylestate", int(option->state));
873 styleObject->setProperty("_q_stylerect", option->rect);
874 bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken)
875 || ((state & State_MouseOver) != (oldState & State_MouseOver))
876 || (state & State_On) != (oldState & State_On))
877 && state & State_Enabled);
878 if (doTransition) {
879 if (element == PE_IndicatorRadioButton) {
880 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
881 t->setStartValue(styleObject->property("_q_inner_radius").toFloat());
882 t->setEndValue(radioButtonInnerRadius(state));
883 styleObject->setProperty("_q_end_radius", t->endValue());
884 t->setStartTime(d->animationTime());
885 t->setDuration(150);
886 d->startAnimation(t);
887 }
888 else if (element == PE_IndicatorCheckBox) {
889 if ((oldState & State_Off && state & State_On) || (oldState & State_NoChange && state & State_On)) {
890 QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject);
891 t->setStartValue(0.0f);
892 t->setEndValue(1.0f);
893 t->setStartTime(d->animationTime());
894 t->setDuration(150);
895 d->startAnimation(t);
896 }
897 }
898 }
899 }
900
901 switch (element) {
902 case PE_IndicatorArrowUp:
903 case PE_IndicatorArrowDown:
904 case PE_IndicatorArrowRight:
905 case PE_IndicatorArrowLeft: {
906 const QRect &r = option->rect;
907 if (r.width() <= 1 || r.height() <= 1)
908 break;
909 Icon ico = Icon::Help;
910 switch (element) {
911 case PE_IndicatorArrowUp:
912 ico = Icon::ChevronUpMed;
913 break;
914 case PE_IndicatorArrowDown:
915 ico = Icon::ChevronDownMed;
916 break;
917 case PE_IndicatorArrowLeft:
918 ico = Icon::ChevronLeftMed;
919 break;
920 case PE_IndicatorArrowRight:
921 ico = Icon::ChevronRightMed;
922 break;
923 default:
924 break;
925 }
926 QPainterStateGuard psg(painter);
927 if (option->state.testFlag(State_Sunken)) {
928 const auto bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, option, widget);
929 const auto bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget);
930 if (bsx != 0 || bsy != 0)
931 painter->translate(bsx, bsy);
932 }
933 painter->setFont(d->assetFont);
934 painter->setPen(option->palette.buttonText().color());
935 painter->setBrush(option->palette.buttonText());
936 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
937 break;
938 }
939 case PE_FrameFocusRect: {
940 if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
941 if (!(fropt->state & State_KeyboardFocusChange))
942 break;
943 QRectF focusRect = option->rect;
944 focusRect = focusRect.marginsRemoved(QMarginsF(1.5,1.5,1.5,1.5));
945 painter->setPen(winUI3Color(focusFrameInnerStroke));
946 painter->drawRoundedRect(focusRect,4,4);
947
948 focusRect = focusRect.marginsAdded(QMarginsF(1.0,1.0,1.0,1.0));
949 painter->setPen(QPen(winUI3Color(focusFrameOuterStroke),1));
950 painter->drawRoundedRect(focusRect,4,4);
951 }
952 break;
953 }
954 case PE_PanelTipLabel: {
955 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5));
956 const auto pen = highContrastTheme ? option->palette.buttonText().color()
957 : winUI3Color(frameColorLight);
958 drawRoundedRect(painter, rect, pen, option->palette.toolTipBase());
959 break;
960 }
961 case PE_FrameTabWidget:
962#if QT_CONFIG(tabwidget)
963 if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
964 QPainterStateGuard psg(painter);
965 const auto clipRegion = painter->clipRegion();
966
967 painter->setPen(highContrastTheme ? frame->palette.buttonText().color()
968 : winUI3Color(frameColorLight));
969 painter->setBrush(frame->palette.base());
970
971 const auto &rect = option->rect;
972 QRect upperRect = rect;
973 upperRect.setHeight(rect.height() / 2);
974 QRect lowerRect = rect;
975 lowerRect.setY(lowerRect.y() + rect.height() / 2);
976 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
977 painter->drawRect(rect);
978 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
979 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
980
981 }
982#endif // QT_CONFIG(tabwidget)
983 break;
984 case PE_FrameGroupBox:
985 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
986 const auto pen = highContrastTheme ? frame->palette.buttonText().color()
987 : winUI3Color(frameColorStrong);
988 if (frame->features & QStyleOptionFrame::Flat) {
989 painter->setBrush(Qt::NoBrush);
990 painter->setPen(pen);
991 const QRect &fr = frame->rect;
992 QPoint p1(fr.x(), fr.y() + 1);
993 QPoint p2(fr.x() + fr.width(), p1.y());
994 painter->drawLine(p1, p2);
995 } else {
996 const auto frameRect = QRectF(frame->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
997 drawRoundedRect(painter, frameRect, pen, Qt::NoBrush);
998 }
999 }
1000 break;
1001 case PE_IndicatorHeaderArrow:
1002 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1003 const auto indicator = header->sortIndicator;
1004 if (indicator != QStyleOptionHeader::None) {
1005 QPainterStateGuard psg(painter);
1006 QFont f(d->assetFont);
1007 f.setPointSize(6);
1008 painter->setFont(f);
1009 painter->setPen(header->palette.text().color());
1010 const auto ico = indicator == QStyleOptionHeader::SortUp ? Icon::ChevronDown
1011 : Icon::ChevronUp;
1012 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
1013 }
1014 }
1015 break;
1016 case PE_IndicatorCheckBox: {
1017 const bool isOn = option->state & State_On;
1018 const bool isPartial = option->state & State_NoChange;
1019
1020 const QRectF rect = option->rect;
1021 const QPointF center = rect.center();
1022
1023 drawRoundedRect(painter, option->rect, borderPenControlAlt(option),
1024 controlFillBrush(option, ControlType::ControlAlt));
1025
1026 if (isOn) {
1027 painter->setFont(d->assetFont);
1028 painter->setPen(controlTextColor(option));
1029 const auto *animation =
1030 qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
1031 const qreal clipWidth = animation ? animation->currentValue() : 1.0;
1032 const QString str = fluentIcon(Icon::AcceptMedium);
1033 QFontMetrics fm(d->assetFont);
1034 QRectF clipRect = fm.boundingRect(str);
1035 clipRect.moveCenter(center);
1036 clipRect.setLeft(rect.x() + (rect.width() - clipRect.width()) / 2.0 + 0.5);
1037 clipRect.setWidth(clipWidth * clipRect.width());
1038 painter->drawText(clipRect, Qt::AlignVCenter | Qt::AlignLeft, str);
1039 } else if (isPartial) {
1040 QFont f(d->assetFont);
1041 f.setPointSize(6);
1042 painter->setFont(f);
1043 painter->setPen(controlTextColor(option));
1044 painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::Dash12));
1045 }
1046 }
1047 break;
1048 case PE_IndicatorBranch: {
1049 if (option->state & State_Children) {
1050 const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option);
1051 const bool isReverse = option->direction == Qt::RightToLeft;
1052 const bool isOpen = option->state & QStyle::State_Open;
1053 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1054 QFont f(d->assetFont);
1055 f.setPointSize(8);
1056 painter->setFont(f);
1057 if (view && view->alternatingRowColors() && vopt && vopt->state & State_Selected)
1058 painter->setPen(winUI3Color(textOnAccentPrimary));
1059 else
1060 painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled,
1061 QPalette::WindowText));
1062 const auto ico = isOpen ? Icon::ChevronDownMed
1063 : (isReverse ? Icon::ChevronLeftMed
1064 : Icon::ChevronRightMed);
1065 painter->drawText(option->rect, Qt::AlignCenter, fluentIcon(ico));
1066 }
1067 }
1068 break;
1069 case PE_IndicatorRadioButton: {
1070 const auto *animation =
1071 qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject));
1072 const qreal innerRadius =
1073 animation ? animation->currentValue() : radioButtonInnerRadius(state);
1074 const QRectF rect = option->rect;
1075 const QPointF center = rect.center();
1076
1077 if (option->styleObject)
1078 option->styleObject->setProperty("_q_inner_radius", innerRadius);
1079 painter->setPen(borderPenControlAlt(option));
1080 painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
1081 if (option->state.testFlag(State_On)) {
1082 QPainterPath path;
1083 path.addEllipse(center, 7.5, 7.5);
1084 path.addEllipse(center, innerRadius, innerRadius);
1085 painter->drawPath(path);
1086 // Text On Accent/Primary
1087 painter->setBrush(option->palette.window().color());
1088 painter->drawEllipse(center, innerRadius, innerRadius);
1089 } else {
1090 painter->drawEllipse(center, 7.5, 7.5);
1091 }
1092 }
1093 break;
1094 case PE_PanelButtonTool:
1095 case PE_PanelButtonBevel:{
1096 const bool isEnabled = state & QStyle::State_Enabled;
1097 const bool isMouseOver = state & QStyle::State_MouseOver;
1098 const bool isRaised = state & QStyle::State_Raised;
1099 const int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
1100 const QRectF rect = option->rect.marginsRemoved(QMargins(fw, fw, fw, fw));
1101 if (element == PE_PanelButtonTool && ((!isMouseOver && !isRaised) || !isEnabled))
1102 painter->setPen(Qt::NoPen);
1103 else
1104 painter->setPen(winUI3Color(controlStrokePrimary));
1105 painter->setBrush(controlFillBrush(option, ControlType::Control));
1106 if (element == PE_PanelButtonTool && widget) {
1107 const auto name = widget->objectName();
1108 if (name == "ScrollLeftButton"_L1 || name == "ScrollRightButton"_L1) {
1109 painter->setPen(Qt::NoPen);
1110 if (isMouseOver)
1111 painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
1112 else
1113 painter->setBrush(Qt::NoBrush);
1114 }
1115 }
1116 painter->drawRoundedRect(rect,
1117 secondLevelRoundingRadius, secondLevelRoundingRadius);
1118 }
1119 break;
1120 case PE_FrameDefaultButton:
1121 painter->setPen(option->palette.accent().color());
1122 painter->setBrush(Qt::NoBrush);
1123 painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1124 break;
1125 case PE_FrameMenu:
1126 break;
1127 case PE_PanelMenu: {
1128 if (!d->nativeRoundedTopLevelWindows) {
1129 const QRect rect = option->rect.marginsRemoved(QMargins(2, 2, 2, 2));
1130 painter->setPen(highContrastTheme ? QPen(option->palette.windowText().color(), 2)
1131 : winUI3Color(frameColorLight));
1132 painter->setBrush(winUI3Color(menuPanelFill));
1133 painter->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius);
1134 }
1135 break;
1136 }
1137 case PE_PanelLineEdit:
1138 if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1139 const bool isInSpinBox =
1140 widget && qobject_cast<const QAbstractSpinBox *>(widget->parent()) != nullptr;
1141 const bool isInComboBox =
1142 widget && qobject_cast<const QComboBox *>(widget->parent()) != nullptr;
1143 if (!isInSpinBox && !isInComboBox) {
1144 const auto frameRect =
1145 QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1146 drawRoundedRect(painter, frameRect, Qt::NoPen, inputFillBrush(option, widget));
1147 if (panel->lineWidth > 0)
1148 proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
1149 }
1150 }
1151 break;
1152 case PE_FrameLineEdit: {
1153 const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1154 drawLineEditFrame(painter, frameRect, option);
1155 if (state & State_KeyboardFocusChange && state & State_HasFocus) {
1156 QStyleOptionFocusRect fropt;
1157 fropt.QStyleOption::operator=(*option);
1158 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
1159 }
1160 break;
1161 }
1162 case PE_Frame: {
1163 if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1164 const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5));
1165 if (qobject_cast<const QComboBoxPrivateContainer *>(widget)) {
1166 if (d->nativeRoundedTopLevelWindows)
1167 break;
1168 QPen pen;
1169 if (highContrastTheme)
1170 pen = QPen(option->palette.windowText().color(), 2);
1171 else
1172 pen = Qt::NoPen;
1173 drawRoundedRect(painter, rect, pen, WINUI3Colors[colorSchemeIndex][menuPanelFill]);
1174 } else
1175 drawRoundedRect(painter, rect, Qt::NoPen, option->palette.brush(QPalette::Base));
1176
1177 if (frame->frameShape == QFrame::NoFrame)
1178 break;
1179
1180 const bool isEditable = qobject_cast<const QTextEdit *>(widget) != nullptr
1181 || qobject_cast<const QPlainTextEdit *>(widget) != nullptr;
1182 drawLineEditFrame(painter, rect, option, isEditable);
1183 }
1184 break;
1185 }
1186 case PE_PanelItemViewItem:
1187 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1188 if (vopt->backgroundBrush.style() != Qt::NoBrush) {
1189 QPainterStateGuard psg(painter);
1190 painter->setBrushOrigin(vopt->rect.topLeft());
1191 painter->fillRect(vopt->rect, vopt->backgroundBrush);
1192 }
1193 }
1194 break;
1195 case PE_PanelItemViewRow:
1196 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1197 // this is only called from a QTreeView to paint
1198 // - the tree branch decoration (incl. selected/hovered or not)
1199 // - the (alternate) background of the item in always unselected state
1200 const QRect &rect = vopt->rect;
1201 const bool isRtl = option->direction == Qt::RightToLeft;
1202 if (rect.width() <= 0)
1203 break;
1204
1205 if (vopt->features & QStyleOptionViewItem::Alternate) {
1206 QPalette::ColorGroup cg =
1207 (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled))
1208 ? QPalette::Normal
1209 : QPalette::Disabled;
1210 if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
1211 cg = QPalette::Inactive;
1212 painter->fillRect(rect, option->palette.brush(cg, QPalette::AlternateBase));
1213 }
1214
1215 if (option->state & State_Selected && !highContrastTheme) {
1216 // keep in sync with CE_ItemViewItem QListView indicator painting
1217 if (!qobject_cast<const QTableView *>(widget)) {
1218 const auto col = option->palette.accent().color();
1219 painter->setBrush(col);
1220 painter->setPen(col);
1221 const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f;
1222 const auto yOfs = rect.height() / 4.;
1223 QRectF r(QPointF(xPos, rect.y() + yOfs),
1224 QPointF(xPos + 1, rect.y() + rect.height() - yOfs));
1225 painter->drawRoundedRect(r, 1, 1);
1226 }
1227 }
1228
1229 const bool isTreeDecoration = vopt->features.testFlag(
1230 QStyleOptionViewItem::IsDecorationForRootColumn);
1231 if (isTreeDecoration && vopt->state.testAnyFlags(State_Selected | State_MouseOver) &&
1232 vopt->showDecorationSelected) {
1233 const bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1234 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1235 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1236 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1237
1238 if (onlyOne)
1239 isFirst = true;
1240
1241 if (isRtl) {
1242 isFirst = !isFirst;
1243 isLast = !isLast;
1244 }
1245
1246 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1247 painter->setBrush(view->alternatingRowColors() && state & State_Selected ? calculateAccentColor(option) : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1248 painter->setPen(Qt::NoPen);
1249 if (isFirst) {
1250 QPainterStateGuard psg(painter);
1251 painter->setClipRect(rect);
1252 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1253 secondLevelRoundingRadius, secondLevelRoundingRadius);
1254 } else if (isLast) {
1255 QPainterStateGuard psg(painter);
1256 painter->setClipRect(rect);
1257 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1258 secondLevelRoundingRadius, secondLevelRoundingRadius);
1259 } else {
1260 painter->drawRect(vopt->rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1261 }
1262 }
1263 }
1264 break;
1265 case QStyle::PE_Widget: {
1266 if (widget && widget->palette().isBrushSet(QPalette::Active, widget->backgroundRole())) {
1267 const QBrush bg = widget->palette().brush(widget->backgroundRole());
1268 auto wp = QWidgetPrivate::get(widget);
1269 QPainterStateGuard psg(painter);
1270 wp->updateBrushOrigin(painter, bg);
1271 painter->fillRect(option->rect, bg);
1272 }
1273 break;
1274 }
1275 case QStyle::PE_FrameWindow:
1276 if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1277
1278 QRectF rect= option->rect;
1279 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1280
1281 QRectF bottomLeftCorner = QRectF(rect.left() + 1.0,
1282 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1283 secondLevelRoundingRadius,
1284 secondLevelRoundingRadius);
1285 QRectF bottomRightCorner = QRectF(rect.right() - 1.0 - secondLevelRoundingRadius,
1286 rect.bottom() - 1.0 - secondLevelRoundingRadius,
1287 secondLevelRoundingRadius,
1288 secondLevelRoundingRadius);
1289
1290 //Draw Mask
1291 if (widget != nullptr) {
1292 QBitmap mask(widget->width(), widget->height());
1293 mask.clear();
1294
1295 QPainter maskPainter(&mask);
1296 maskPainter.setRenderHint(QPainter::Antialiasing);
1297 maskPainter.setBrush(Qt::color1);
1298 maskPainter.setPen(Qt::NoPen);
1299 maskPainter.drawRoundedRect(option->rect,secondLevelRoundingRadius,secondLevelRoundingRadius);
1300 const_cast<QWidget*>(widget)->setMask(mask);
1301 }
1302
1303 //Draw Window
1304 painter->setPen(QPen(frm->palette.base(), fwidth));
1305 painter->drawLine(QPointF(rect.left(), rect.top()),
1306 QPointF(rect.left(), rect.bottom() - fwidth));
1307 painter->drawLine(QPointF(rect.left() + fwidth, rect.bottom()),
1308 QPointF(rect.right() - fwidth, rect.bottom()));
1309 painter->drawLine(QPointF(rect.right(), rect.top()),
1310 QPointF(rect.right(), rect.bottom() - fwidth));
1311
1312 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1313 painter->drawLine(QPointF(rect.left() + 0.5, rect.top() + 0.5),
1314 QPointF(rect.left() + 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1315 painter->drawLine(QPointF(rect.left() + 0.5 + secondLevelRoundingRadius, rect.bottom() - 0.5),
1316 QPointF(rect.right() - 0.5 - secondLevelRoundingRadius, rect.bottom() - 0.5));
1317 painter->drawLine(QPointF(rect.right() - 0.5, rect.top() + 1.5),
1318 QPointF(rect.right() - 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius));
1319
1320 painter->setPen(Qt::NoPen);
1321 painter->setBrush(frm->palette.base());
1322 painter->drawPie(bottomRightCorner.marginsAdded(QMarginsF(2.5,2.5,0.0,0.0)),
1323 270 * 16,90 * 16);
1324 painter->drawPie(bottomLeftCorner.marginsAdded(QMarginsF(0.0,2.5,2.5,0.0)),
1325 -90 * 16,-90 * 16);
1326
1327 painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]);
1328 painter->setBrush(Qt::NoBrush);
1329 painter->drawArc(bottomRightCorner,
1330 0 * 16,-90 * 16);
1331 painter->drawArc(bottomLeftCorner,
1332 -90 * 16,-90 * 16);
1333 }
1334 break;
1335#if QT_CONFIG(tabbar)
1336 case PE_FrameTabBarBase:
1337 if (const auto *tab = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
1338 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1339 const auto clipRegion = painter->clipRegion();
1340
1341 painter->setPen(highContrastTheme ? tab->palette.buttonText().color()
1342 : winUI3Color(frameColorLight));
1343 painter->setBrush(tab->palette.base());
1344 QRect upperRect = tab->rect;
1345 upperRect.setHeight(tab->rect.height() / 2);
1346 QRect lowerRect = tab->rect;
1347 lowerRect.setY(lowerRect.y() + tab->rect.height() / 2);
1348 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
1349 painter->drawRoundedRect(tab->rect, secondLevelRoundingRadius,
1350 secondLevelRoundingRadius);
1351 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
1352 painter->drawRect(tab->rect);
1353 }
1354 break;
1355#endif // QT_CONFIG(tabbar)
1356 case PE_IndicatorTabTearLeft:
1357 case PE_IndicatorTabTearRight:
1358 break;
1359 default:
1360 QWindowsVistaStyle::drawPrimitive(element, option, painter, widget);
1361 }
1362}
1363
1364/*!
1365 \internal
1366*/
1367void QWindows11Style::drawControl(ControlElement element, const QStyleOption *option,
1368 QPainter *painter, const QWidget *widget) const
1369{
1370 Q_D(const QWindows11Style);
1371 State flags = option->state;
1372
1373 QPainterStateGuard psg(painter);
1374 painter->setRenderHint(QPainter::Antialiasing);
1375 switch (element) {
1376 case QStyle::CE_ComboBoxLabel:
1377#if QT_CONFIG(combobox)
1378 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1379 painter->setPen(controlTextColor(option, true));
1380 QStyleOptionComboBox newOption = *cb;
1381 newOption.rect.adjust(4,0,-4,0);
1382 QCommonStyle::drawControl(element, &newOption, painter, widget);
1383 }
1384#endif // QT_CONFIG(combobox)
1385 break;
1386#if QT_CONFIG(tabbar)
1387 case CE_TabBarTabShape:
1388 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
1389 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1390 const bool isSelected = tab->state.testFlag(State_Selected);
1391 const auto clipRegion = painter->clipRegion();
1392 const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab
1393 || tab->position == QStyleOptionTab::Moving;
1394 auto leftMargin = (tab->position == QStyleOptionTab::Beginning || onlyOne) ? 1 : 0;
1395 auto rightMargin = (tab->position == QStyleOptionTab::End || onlyOne) ? 1 : 0;
1396 if (QCommonStylePrivate::rtl(option))
1397 std::swap(leftMargin, rightMargin);
1398
1399 QRectF tabRect = tab->rect.marginsRemoved(QMargins(leftMargin, 1, rightMargin, -3));
1400 painter->setPen(highContrastTheme ? tab->palette.buttonText().color() : winUI3Color(frameColorLight));
1401 painter->setBrush(tab->palette.base());
1402 if (isSelected) {
1403 painter->setBrush(winUI3Color(fillMicaAltDefault));
1404 } else {
1405 if (tab->state.testFlag(State_Sunken))
1406 painter->setBrush(winUI3Color(fillMicaAltDefault));
1407 else if (tab->state.testFlag(State_MouseOver))
1408 painter->setBrush(winUI3Color(fillMicaAltSecondary));
1409 else
1410 painter->setBrush(winUI3Color(fillMicaAltTransparent));
1411 }
1412 QRect upperRect = tab->rect;
1413 upperRect.setHeight(tab->rect.height() / 2.);
1414 QRect lowerRect = tab->rect;
1415 lowerRect.setY(lowerRect.y() + tab->rect.height() / 2.);
1416 painter->setClipRegion(clipRegion.isNull() ? upperRect : clipRegion & upperRect);
1417 painter->drawRoundedRect(tabRect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1418 painter->setClipRegion(clipRegion.isNull() ? lowerRect : clipRegion & lowerRect);
1419 painter->drawRect(tabRect);
1420 }
1421 break;
1422 case CE_TabBarTabLabel:
1423 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
1424 const bool isEnabled = tab->state.testFlags(State_Enabled);
1425 const bool isSelected = tab->state.testFlags(State_Selected);
1426
1427 QRect tr = tab->rect;
1428 bool verticalTabs = tab->shape == QTabBar::RoundedEast
1429 || tab->shape == QTabBar::RoundedWest || tab->shape == QTabBar::TriangularEast
1430 || tab->shape == QTabBar::TriangularWest;
1431
1432 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
1433 if (!proxy()->styleHint(SH_UnderlineShortcut, option, widget))
1434 alignment |= Qt::TextHideMnemonic;
1435
1436 QPainterStateGuard psg(painter, QPainterStateGuard::InitialState::NoSave);
1437 if (verticalTabs) {
1438 psg.save();
1439 int newX, newY, newRot;
1440 if (tab->shape == QTabBar::RoundedEast || tab->shape == QTabBar::TriangularEast) {
1441 newX = tr.width() + tr.x();
1442 newY = tr.y();
1443 newRot = 90;
1444 } else {
1445 newX = tr.x();
1446 newY = tr.y() + tr.height();
1447 newRot = -90;
1448 }
1449 QTransform m = QTransform::fromTranslate(newX, newY);
1450 m.rotate(newRot);
1451 painter->setTransform(m, true);
1452 }
1453 QRect iconRect;
1454 d->tabLayout(tab, widget, &tr, &iconRect);
1455
1456 // compute tr again, unless tab is moving, because the style may override subElementRect
1457 if (tab->position != QStyleOptionTab::TabPosition::Moving)
1458 tr = proxy()->subElementRect(SE_TabBarTabText, option, widget);
1459
1460 if (!tab->icon.isNull()) {
1461 const auto mode = isEnabled ? QIcon::Normal : QIcon::Disabled;
1462 const auto state = isSelected ? QIcon::On : QIcon::Off;
1463 tab->icon.paint(painter, iconRect, Qt::AlignCenter, mode, state);
1464 }
1465
1466 painter->setPen(winUI3Color(isSelected ? textPrimary : textSecondary));
1467 proxy()->drawItemText(painter, tr, alignment, tab->palette, isEnabled, tab->text);
1468 }
1469 break;
1470#endif // QT_CONFIG(tabbar)
1471 case CE_ToolButtonLabel:
1472#if QT_CONFIG(toolbutton)
1473 if (const QStyleOptionToolButton *toolbutton
1474 = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
1475 QRect rect = toolbutton->rect;
1476 int shiftX = 0;
1477 int shiftY = 0;
1478 if (toolbutton->state & (State_Sunken | State_On)) {
1479 shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget);
1480 shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton, widget);
1481 }
1482 // Arrow type always overrules and is always shown
1483 bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow;
1484 if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty())
1485 || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) {
1486 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
1487 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1488 alignment |= Qt::TextHideMnemonic;
1489 rect.translate(shiftX, shiftY);
1490 painter->setFont(toolbutton->font);
1491 const QString text = d->toolButtonElideText(toolbutton, rect, alignment);
1492 painter->setPen(controlTextColor(option));
1493 proxy()->drawItemText(painter, rect, alignment, toolbutton->palette,
1494 toolbutton->state & State_Enabled, text);
1495 } else {
1496 QPixmap pm;
1497 QSize pmSize = toolbutton->iconSize;
1498 if (!toolbutton->icon.isNull()) {
1499 QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off;
1500 QIcon::Mode mode;
1501 if (!(toolbutton->state & State_Enabled))
1502 mode = QIcon::Disabled;
1503 else if ((toolbutton->state & State_MouseOver) && (toolbutton->state & State_AutoRaise))
1504 mode = QIcon::Active;
1505 else
1506 mode = QIcon::Normal;
1507 pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), painter->device()->devicePixelRatio(),
1508 mode, state);
1509 pmSize = pm.size() / pm.devicePixelRatio();
1510 }
1511
1512 if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) {
1513 painter->setFont(toolbutton->font);
1514 QRect pr = rect,
1515 tr = rect;
1516 int alignment = Qt::TextShowMnemonic | Qt::AlignCenter;
1517 if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget))
1518 alignment |= Qt::TextHideMnemonic;
1519
1520 if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
1521 pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1522 tr.adjust(0, pr.height() - 1, 0, -1);
1523 } else {
1524 pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint()
1525 tr.adjust(pr.width(), 0, 0, 0);
1526 }
1527 pr.translate(shiftX, shiftY);
1528 if (hasArrow) {
1529 drawArrow(proxy(), toolbutton, pr, painter, widget);
1530 } else {
1531 const auto vr = QStyle::visualRect(toolbutton->direction, rect, pr);
1532 proxy()->drawItemPixmap(painter, vr, Qt::AlignCenter, pm);
1533 }
1534 tr.translate(shiftX, shiftY);
1535 painter->setPen(controlTextColor(option));
1536 const QString text = d->toolButtonElideText(toolbutton, tr, alignment);
1537 const auto vr = QStyle::visualRect(toolbutton->direction, rect, tr);
1538 proxy()->drawItemText(painter, vr, alignment, toolbutton->palette,
1539 toolbutton->state & State_Enabled, text);
1540 } else {
1541 rect.translate(shiftX, shiftY);
1542 if (hasArrow) {
1543 drawArrow(proxy(), toolbutton, rect, painter, widget);
1544 } else {
1545 proxy()->drawItemPixmap(painter, rect, Qt::AlignCenter, pm);
1546 }
1547 }
1548 }
1549 }
1550#endif // QT_CONFIG(toolbutton)
1551 break;
1552 case QStyle::CE_ShapedFrame:
1553 if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1554 int frameShape = f->frameShape;
1555 int frameShadow = QFrame::Plain;
1556 if (f->state & QStyle::State_Sunken)
1557 frameShadow = QFrame::Sunken;
1558 else if (f->state & QStyle::State_Raised)
1559 frameShadow = QFrame::Raised;
1560
1561 int lw = f->lineWidth;
1562 int mlw = f->midLineWidth;
1563
1564 switch (frameShape) {
1565 case QFrame::Box:
1566 if (frameShadow == QFrame::Plain)
1567 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1568 else
1569 qDrawShadeRect(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw);
1570 break;
1571 case QFrame::Panel:
1572 if (frameShadow == QFrame::Plain)
1573 qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw);
1574 else
1575 qDrawShadePanel(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw);
1576 break;
1577 default:
1578 QWindowsVistaStyle::drawControl(element, option, painter, widget);
1579 }
1580 }
1581 break;
1582#if QT_CONFIG(progressbar)
1583 case CE_ProgressBarGroove:
1584 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
1585 QRect rect = option->rect;
1586 QPointF center = rect.center();
1587 if (baropt->state & QStyle::State_Horizontal) {
1588 rect.setHeight(1);
1589 rect.moveTop(center.y());
1590 } else {
1591 rect.setWidth(1);
1592 rect.moveLeft(center.x());
1593 }
1594 painter->setPen(Qt::NoPen);
1595 painter->setBrush(Qt::gray);
1596 painter->drawRect(rect);
1597 }
1598 break;
1599 case CE_ProgressBarContents:
1600 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1601 QPainterStateGuard psg(painter);
1602 QRectF rect = option->rect;
1603 painter->translate(rect.topLeft());
1604 rect.translate(-rect.topLeft());
1605
1606 constexpr qreal progressBarThickness = 3;
1607 constexpr qreal progressBarHalfThickness = progressBarThickness / 2.0;
1608
1609 const auto isIndeterminate = baropt->maximum == 0 && baropt->minimum == 0;
1610 const auto orientation =
1611 (baropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical;
1612 const auto inverted = baropt->invertedAppearance;
1613 const auto reverse = (baropt->direction == Qt::RightToLeft) ^ inverted;
1614 // If the orientation is vertical, we use a transform to rotate
1615 // the progress bar 90 degrees (counter)clockwise. This way we can use the
1616 // same rendering code for both orientations.
1617 if (orientation == Qt::Vertical) {
1618 rect = QRectF(rect.left(), rect.top(), rect.height(),
1619 rect.width()); // flip width and height
1620 QTransform m;
1621 if (inverted) {
1622 m.rotate(90);
1623 m.translate(0, -rect.height() + 1);
1624 } else {
1625 m.rotate(-90);
1626 m.translate(-rect.width(), 0);
1627 }
1628 painter->setTransform(m, true);
1629 } else if (reverse) {
1630 QTransform m = QTransform::fromScale(-1, 1);
1631 m.translate(-rect.width(), 0);
1632 painter->setTransform(m, true);
1633 }
1634 const qreal offset = (int(rect.height()) % 2 == 0) ? 0.5f : 0.0f;
1635
1636 if (isIndeterminate) {
1637#if QT_CONFIG(animation)
1638 auto anim = d->animation(option->styleObject);
1639 if (!anim) {
1640 auto anim = new QStyleAnimation(option->styleObject);
1641 anim->setFrameRate(QStyleAnimation::SixtyFps);
1642 d->startAnimation(anim);
1643 }
1644 constexpr auto loopDurationMSec = 4000;
1645 const auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(
1646 std::chrono::system_clock::now());
1647 const auto elapsed = elapsedTime.time_since_epoch().count();
1648 const auto handleCenter = (elapsed % loopDurationMSec) / float(loopDurationMSec);
1649 const auto isLongHandle = (elapsed / loopDurationMSec) % 2 == 0;
1650 const auto lengthFactor = (isLongHandle ? 33.0f : 25.0f) / 100.0f;
1651#else
1652 constexpr auto handleCenter = 0.5f;
1653 constexpr auto lengthFactor = 1;
1654#endif
1655 const auto begin = qMax(handleCenter * (1 + lengthFactor) - lengthFactor, 0.0f);
1656 const auto end = qMin(handleCenter * (1 + lengthFactor), 1.0f);
1657 const auto barBegin = begin * rect.width();
1658 const auto barEnd = end * rect.width();
1659 rect = QRectF(QPointF(rect.left() + barBegin, rect.top()),
1660 QPointF(rect.left() + barEnd, rect.bottom()));
1661 } else {
1662#if QT_CONFIG(animation)
1663 d->stopAnimation(option->styleObject);
1664#endif
1665 const auto fillPercentage = (float(baropt->progress - baropt->minimum))
1666 / (float(baropt->maximum - baropt->minimum));
1667 rect.setWidth(rect.width() * fillPercentage);
1668 }
1669 const QPointF center = rect.center();
1670 rect.setHeight(progressBarThickness);
1671 rect.moveTop(center.y() - progressBarHalfThickness - offset);
1672 drawRoundedRect(painter, rect, Qt::NoPen, baropt->palette.accent());
1673 }
1674 break;
1675 case CE_ProgressBarLabel:
1676 if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1677 const bool vertical = !(baropt->state & QStyle::State_Horizontal);
1678 if (!vertical) {
1679 proxy()->drawItemText(painter, baropt->rect, Qt::AlignCenter | Qt::TextSingleLine,
1680 baropt->palette, baropt->state & State_Enabled, baropt->text,
1681 QPalette::Text);
1682 }
1683 }
1684 break;
1685#endif // QT_CONFIG(progressbar)
1686 case CE_PushButtonLabel:
1687 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1688 QStyleOptionButton btnCopy(*btn);
1689 btnCopy.rect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0));
1690 btnCopy.palette.setBrush(QPalette::ButtonText, controlTextColor(option));
1691 QCommonStyle::drawControl(element, &btnCopy, painter, widget);
1692 }
1693 break;
1694 case CE_PushButtonBevel:
1695 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1696 using namespace StyleOptionHelper;
1697
1698 QRectF rect = btn->rect.marginsRemoved(QMargins(2, 2, 2, 2));
1699 painter->setPen(Qt::NoPen);
1700 if (btn->features.testFlag(QStyleOptionButton::Flat)) {
1701 painter->setBrush(btn->palette.button());
1702 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1703 if (flags & (State_Sunken | State_On)) {
1704 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtlePressedColor]);
1705 }
1706 else if (flags & State_MouseOver) {
1707 painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
1708 }
1709 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1710 } else {
1711 painter->setBrush(controlFillBrush(option, ControlType::Control));
1712 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1713
1714 rect.adjust(0.5,0.5,-0.5,-0.5);
1715 const bool defaultButton = btn->features.testFlag(QStyleOptionButton::DefaultButton);
1716 painter->setBrush(Qt::NoBrush);
1717 painter->setPen(defaultButton ? option->palette.accent().color()
1718 : WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
1719 painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
1720
1721 painter->setPen(defaultButton ? WINUI3Colors[colorSchemeIndex][controlStrokeOnAccentSecondary]
1722 : WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]);
1723 }
1724 if (btn->features.testFlag(QStyleOptionButton::HasMenu)) {
1725 QPainterStateGuard psg(painter);
1726
1727 QRect textRect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0));
1728 const auto indSize = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget);
1729 const auto indRect =
1730 QRect(btn->rect.right() - indSize - contentItemHMargin, textRect.top(),
1731 indSize + contentItemHMargin, btn->rect.height());
1732 const auto vindRect = visualRect(btn->direction, btn->rect, indRect);
1733 textRect.setWidth(textRect.width() - indSize);
1734
1735 int fontSize = painter->font().pointSize();
1736 QFont f(d->assetFont);
1737 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
1738 painter->setFont(f);
1739 painter->setPen(controlTextColor(option));
1740 painter->drawText(vindRect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed));
1741 }
1742 }
1743 break;
1744 case CE_MenuBarItem:
1745 if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1746 using namespace StyleOptionHelper;
1747
1748 constexpr int hPadding = 11;
1749 constexpr int topPadding = 4;
1750 constexpr int bottomPadding = 6;
1751 QStyleOptionMenuItem newMbi = *mbi;
1752
1753 if (auto mbiV2 = qstyleoption_cast<const QStyleOptionMenuItemV2 *>(option))
1754 newMbi.state.setFlag(State_Sunken, mbiV2->mouseDown);
1755
1756 newMbi.font.setPointSize(10);
1757 newMbi.palette.setColor(QPalette::ButtonText, controlTextColor(&newMbi));
1758 if (!isDisabled(&newMbi)) {
1759 QPen pen(Qt::NoPen);
1760 QBrush brush(Qt::NoBrush);
1761 if (highContrastTheme) {
1762 pen = QPen(newMbi.palette.highlight().color(), 2);
1763 brush = newMbi.palette.window();
1764 } else if (isPressed(&newMbi)) {
1765 brush = winUI3Color(subtlePressedColor);
1766 } else if (isHover(&newMbi)) {
1767 brush = winUI3Color(subtleHighlightColor);
1768 }
1769 if (pen != Qt::NoPen || brush != Qt::NoBrush) {
1770 const QRect rect = mbi->rect.marginsRemoved(QMargins(5, 0, 5, 0));
1771 drawRoundedRect(painter, rect, pen, brush);
1772 }
1773 }
1774 newMbi.rect.adjust(hPadding,topPadding,-hPadding,-bottomPadding);
1775 painter->setFont(newMbi.font);
1776 QCommonStyle::drawControl(element, &newMbi, painter, widget);
1777 }
1778 break;
1779
1780#if QT_CONFIG(menu)
1781 case CE_MenuEmptyArea:
1782 break;
1783
1784 case CE_MenuItem:
1785 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
1786 const auto visualMenuRect = [&](const QRect &rect) {
1787 return visualRect(option->direction, menuitem->rect, rect);
1788 };
1789 bool dis = !(menuitem->state & State_Enabled);
1790 bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
1791 ? menuitem->checked : false;
1792 bool act = menuitem->state & State_Selected;
1793
1794 const QRect rect = menuitem->rect.marginsRemoved(QMargins(2,2,2,2));
1795 if (act && dis == false) {
1796 drawRoundedRect(painter, rect, Qt::NoPen, highContrastTheme ? menuitem->palette.brush(QPalette::Highlight)
1797 : QBrush(winUI3Color(subtleHighlightColor)));
1798 }
1799 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
1800 constexpr int yoff = 1;
1801 painter->setPen(highContrastTheme ? menuitem->palette.buttonText().color() : winUI3Color(dividerStrokeDefault));
1802 painter->drawLine(menuitem->rect.topLeft() + QPoint(0, yoff),
1803 menuitem->rect.topRight() + QPoint(0, yoff));
1804 break;
1805 }
1806
1807 int xOffset = contentHMargin;
1808 // WinUI3 draws, in contrast to former windows styles, the checkmark and icon separately
1809 const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget);
1810 if (checked) {
1811 QRect vRect(visualMenuRect(QRect(rect.x() + xOffset, rect.y(),
1812 checkMarkWidth, rect.height())));
1813 QPainterStateGuard psg(painter);
1814 painter->setFont(d->assetFont);
1815 painter->setPen(option->palette.text().color());
1816 painter->drawText(vRect, Qt::AlignCenter, fluentIcon(Icon::CheckMark));
1817 }
1818 if (menuitem->menuHasCheckableItems)
1819 xOffset += checkMarkWidth + contentItemHMargin;
1820 if (!menuitem->icon.isNull()) {
1821 // 4 is added to maxIconWidth in qmenu.cpp to PM_SmallIconSize
1822 QRect vRect(visualMenuRect(QRect(rect.x() + xOffset,
1823 rect.y(),
1824 menuitem->maxIconWidth - 4,
1825 rect.height())));
1826 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
1827 if (act && !dis)
1828 mode = QIcon::Active;
1829 const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
1830 QRect pmr(QPoint(0, 0), QSize(size, size));
1831 pmr.moveCenter(vRect.center());
1832 menuitem->icon.paint(painter, pmr, Qt::AlignCenter, mode,
1833 checked ? QIcon::On : QIcon::Off);
1834 }
1835 if (menuitem->maxIconWidth > 0)
1836 xOffset += menuitem->maxIconWidth - 4 + contentItemHMargin;
1837
1838 QStringView s(menuitem->text);
1839 if (!s.isEmpty()) { // draw text
1840 QPoint tl(rect.left() + xOffset, rect.top());
1841 QPoint br(rect.right() - menuitem->reservedShortcutWidth - contentHMargin,
1842 rect.bottom());
1843 QRect textRect(tl, br);
1844 QRect vRect(visualMenuRect(textRect));
1845
1846 QColor penColor;
1847 if (highContrastTheme) {
1848 penColor = menuitem->palette.color(act ? QPalette::HighlightedText
1849 : QPalette::Text);
1850 } else {
1851 penColor = controlTextColor(option);
1852 }
1853 painter->setPen(penColor);
1854
1855 qsizetype t = s.indexOf(u'\t');
1856 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
1857 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
1858 text_flags |= Qt::TextHideMnemonic;
1859 text_flags |= Qt::AlignLeft;
1860 // a submenu doesn't paint a possible shortcut in WinUI3
1861 if (t >= 0 && menuitem->menuItemType != QStyleOptionMenuItem::SubMenu) {
1862 QRect shortcutRect(QPoint(textRect.right(), textRect.top()),
1863 QPoint(rect.right(), textRect.bottom()));
1864 QRect vShortcutRect(visualMenuRect(shortcutRect));
1865 const QString textToDraw = s.mid(t + 1).toString();
1866 painter->drawText(vShortcutRect, text_flags, textToDraw);
1867 s = s.left(t);
1868 }
1869 QFont font = menuitem->font;
1870 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
1871 font.setBold(true);
1872 painter->setFont(font);
1873 const QString textToDraw = s.left(t).toString();
1874 painter->drawText(vRect, text_flags, textToDraw);
1875 }
1876 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
1877 int fontSize = menuitem->font.pointSize();
1878 QFont f(d->assetFont);
1879 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
1880 painter->setFont(f);
1881 int yOfs = qRound(fontSize / 3.0f); // an offset to align the '>' with the baseline of the text
1882 QPoint tl(rect.right() - 2 * QWindowsStylePrivate::windowsArrowHMargin - contentItemHMargin,
1883 rect.top() + yOfs);
1884 QRect submenuRect(tl, rect.bottomRight());
1885 QRect vSubMenuRect = visualMenuRect(submenuRect);
1886 painter->setPen(option->palette.text().color());
1887 const bool isReverse = option->direction == Qt::RightToLeft;
1888 const auto ico = isReverse ? Icon::ChevronLeftMed : Icon::ChevronRightMed;
1889 painter->drawText(vSubMenuRect, Qt::AlignCenter, fluentIcon(ico));
1890 }
1891 }
1892 break;
1893#endif // QT_CONFIG(menu)
1894 case CE_MenuBarEmptyArea: {
1895 break;
1896 }
1897 case CE_HeaderEmptyArea:
1898 break;
1899 case CE_HeaderSection: {
1900 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1901 painter->setPen(Qt::NoPen);
1902 painter->setBrush(header->palette.button());
1903 painter->drawRect(header->rect);
1904 const bool isRtl = option->direction == Qt::RightToLeft;
1905 const QPointF tr = isRtl ? option->rect.topLeft() : option->rect.topRight();
1906 const QPointF br = isRtl ? option->rect.bottomLeft() : option->rect.bottomRight();
1907 const QPointF bl = isRtl ? option->rect.bottomRight() : option->rect.bottomLeft();
1908 constexpr QPointF trOfs = QPointF(0.5, 0.0);
1909 constexpr QPointF brOfs = QPointF(0.5, 0.5);
1910 constexpr QPointF blOfs = QPointF(0.0, 0.5);
1911 const std::array<QPointF, 3> points = { tr + trOfs, br + brOfs, bl + blOfs };
1912 QPen pen(highContrastTheme ? header->palette.buttonText().color()
1913 : winUI3Color(frameColorLight));
1914 pen.setJoinStyle(Qt::MiterJoin);
1915 painter->setPen(pen);
1916 painter->drawPolyline(points.data(), int(points.size()));
1917 }
1918 break;
1919 }
1920 case CE_ItemViewItem: {
1921 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
1922 const auto p = proxy();
1923 QRect checkRect = p->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget);
1924 QRect iconRect = p->subElementRect(SE_ItemViewItemDecoration, vopt, widget);
1925 QRect textRect = p->subElementRect(SE_ItemViewItemText, vopt, widget);
1926
1927 // draw the background
1928 proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget);
1929
1930 const QRect &rect = vopt->rect;
1931 const bool isRtl = option->direction == Qt::RightToLeft;
1932 bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne ||
1933 vopt->viewItemPosition == QStyleOptionViewItem::Invalid;
1934 bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
1935 bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End;
1936
1937 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
1938 if (qobject_cast<const QTableView *>(view)) {
1939 onlyOne = true;
1940 } else {
1941 // the tree decoration already painted the left side of the rounded rect
1942 if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) &&
1943 vopt->showDecorationSelected) {
1944 isFirst = false;
1945 if (onlyOne) {
1946 onlyOne = false;
1947 isLast = true;
1948 }
1949 }
1950
1951 if (isRtl) {
1952 if (isFirst) {
1953 isFirst = false;
1954 isLast = true;
1955 } else if (isLast) {
1956 isFirst = true;
1957 isLast = false;
1958 }
1959 }
1960 }
1961
1962 const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver);
1963 if (highlightCurrent) {
1964 if (highContrastTheme) {
1965 painter->setBrush(vopt->palette.highlight());
1966 } else {
1967 painter->setBrush(view && view->alternatingRowColors() && vopt->state & State_Selected
1968 ? calculateAccentColor(option)
1969 : winUI3Color(subtleHighlightColor));
1970 }
1971 } else {
1972 painter->setBrush(vopt->backgroundBrush);
1973 }
1974 painter->setPen(Qt::NoPen);
1975
1976 if (onlyOne) {
1977 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)),
1978 secondLevelRoundingRadius, secondLevelRoundingRadius);
1979 } else if (isFirst) {
1980 QPainterStateGuard psg(painter);
1981 painter->setClipRect(rect);
1982 painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)),
1983 secondLevelRoundingRadius, secondLevelRoundingRadius);
1984 } else if (isLast) {
1985 QPainterStateGuard psg(painter);
1986 painter->setClipRect(rect);
1987 painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)),
1988 secondLevelRoundingRadius, secondLevelRoundingRadius);
1989 } else {
1990 painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2)));
1991 }
1992
1993 // draw the check mark
1994 if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) {
1995 QStyleOptionViewItem option(*vopt);
1996 option.rect = checkRect;
1997 option.state = option.state & ~QStyle::State_HasFocus;
1998
1999 switch (vopt->checkState) {
2000 case Qt::Unchecked:
2001 option.state |= QStyle::State_Off;
2002 break;
2003 case Qt::PartiallyChecked:
2004 option.state |= QStyle::State_NoChange;
2005 break;
2006 case Qt::Checked:
2007 option.state |= QStyle::State_On;
2008 break;
2009 }
2010 proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget);
2011 }
2012
2013 // draw the icon
2014 if (iconRect.isValid()) {
2015 QIcon::Mode mode = QIcon::Normal;
2016 if (!(vopt->state & QStyle::State_Enabled))
2017 mode = QIcon::Disabled;
2018 else if (vopt->state & QStyle::State_Selected)
2019 mode = QIcon::Selected;
2020 QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
2021 vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state);
2022 }
2023
2024 if (highlightCurrent && highContrastTheme) {
2025 painter->setPen(vopt->palette.base().color());
2026 } else if ((view && view->alternatingRowColors() && highlightCurrent && vopt->state & State_Selected)) {
2027 painter->setPen(winUI3Color(textOnAccentPrimary));
2028 } else {
2029 painter->setPen(vopt->palette.text().color());
2030 }
2031 d->viewItemDrawText(painter, vopt, textRect);
2032
2033 // paint a vertical marker for QListView
2034 if (vopt->state & State_Selected && !highContrastTheme) {
2035 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2036 lv && lv->viewMode() != QListView::IconMode) {
2037 const auto col = vopt->palette.accent().color();
2038 painter->setBrush(col);
2039 painter->setPen(col);
2040 const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f;
2041 const auto yOfs = rect.height() / 4.;
2042 QRectF r(QPointF(xPos, rect.y() + yOfs),
2043 QPointF(xPos + 1, rect.y() + rect.height() - yOfs));
2044 painter->drawRoundedRect(r, 1, 1);
2045 }
2046 }
2047 }
2048 break;
2049 }
2050 default:
2051 QWindowsVistaStyle::drawControl(element, option, painter, widget);
2052 }
2053}
2054
2055int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt,
2056 const QWidget *widget, QStyleHintReturn *returnData) const {
2057 switch (hint) {
2058 case QStyle::SH_Menu_AllowActiveAndDisabled:
2059 return 0;
2060 case SH_GroupBox_TextLabelColor:
2061 if (opt!=nullptr && widget!=nullptr)
2062 return opt->palette.text().color().rgba();
2063 return 0;
2064 case QStyle::SH_ItemView_ShowDecorationSelected:
2065 return 1;
2066 case QStyle::SH_Slider_AbsoluteSetButtons:
2067 return Qt::LeftButton;
2068 case QStyle::SH_Slider_PageSetButtons:
2069 return 0;
2070 case SH_SpinControls_DisableOnBounds:
2071 return 1;
2072 default:
2073 return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData);
2074 }
2075}
2076
2077QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOption *option,
2078 const QWidget *widget) const
2079{
2080 QRect ret;
2081 switch (element) {
2082 case QStyle::SE_RadioButtonIndicator:
2083 case QStyle::SE_CheckBoxIndicator: {
2084 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2085 const auto ofs =
2086 QCommonStylePrivate::rtl(option) ? -contentItemHMargin : +contentItemHMargin;
2087 ret.moveLeft(ret.left() + ofs);
2088 break;
2089 }
2090 case QStyle::SE_ComboBoxFocusRect:
2091 case QStyle::SE_CheckBoxFocusRect:
2092 case QStyle::SE_RadioButtonFocusRect:
2093 case QStyle::SE_PushButtonFocusRect:
2094 ret = option->rect;
2095 break;
2096 case QStyle::SE_LineEditContents:
2097 ret = option->rect.adjusted(4,0,-4,0);
2098 break;
2099 case SE_ItemViewItemCheckIndicator:
2100 case SE_ItemViewItemDecoration:
2101 case SE_ItemViewItemText: {
2102 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2103 if (!ret.isValid() || highContrastTheme)
2104 return ret;
2105
2106 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2107 lv && lv->viewMode() != QListView::IconMode) {
2108 const int xOfs = contentHMargin;
2109 const bool isRtl = option->direction == Qt::RightToLeft;
2110 if (isRtl) {
2111 ret.moveRight(ret.right() - xOfs);
2112 if (ret.left() < option->rect.left())
2113 ret.setLeft(option->rect.left());
2114 } else {
2115 ret.moveLeft(ret.left() + xOfs);
2116 if (ret.right() > option->rect.right())
2117 ret.setRight(option->rect.right());
2118 }
2119 }
2120 break;
2121 }
2122#if QT_CONFIG(progressbar)
2123 case SE_ProgressBarGroove:
2124 case SE_ProgressBarContents:
2125 case SE_ProgressBarLabel:
2126 if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
2127 QStyleOptionProgressBar optCopy(*pb);
2128 // we only support label right from content
2129 optCopy.textAlignment = Qt::AlignRight;
2130 return QWindowsVistaStyle::subElementRect(element, &optCopy, widget);
2131 }
2132 break;
2133#endif // QT_CONFIG(progressbar)
2134#if QT_CONFIG(toolbar)
2135 case SE_TabBarScrollLeftButton:
2136 case SE_TabBarScrollRightButton: {
2137 const bool isRightButton = element == SE_TabBarScrollRightButton;
2138 const bool vertical = option->rect.width() < option->rect.height();
2139 const int buttonWidth = proxy()->pixelMetric(PM_TabBarScrollButtonWidth, option, widget);
2140
2141 if (vertical) {
2142 const int yOfs =
2143 isRightButton ? option->rect.height() - buttonWidth : 0;
2144 const QSize sz(option->rect.width(), buttonWidth);
2145 const QPoint tl(option->rect.topLeft() + QPoint(0, yOfs));
2146 ret = QRect(tl, sz);
2147 } else {
2148 const int xOfs =
2149 isRightButton ? option->rect.width() - buttonWidth : 0;
2150 const QSize sz(buttonWidth, option->rect.height());
2151 const QPoint tl(option->rect.topLeft() + QPoint(xOfs, 0));
2152 ret = QRect(tl, sz);
2153 ret = visualRect(widget->layoutDirection(), option->rect, ret);
2154 }
2155 break;
2156 }
2157 case SE_TabBarTearIndicatorLeft:
2158 case SE_TabBarTearIndicatorRight:
2159 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
2160 const bool isRightButton = element == SE_TabBarTearIndicatorRight;
2161 const int buttonWidth =
2162 proxy()->pixelMetric(PM_TabBarScrollButtonWidth, nullptr, widget);
2163 switch (tab->shape) {
2164 case QTabBar::RoundedNorth:
2165 case QTabBar::TriangularNorth:
2166 case QTabBar::RoundedSouth:
2167 case QTabBar::TriangularSouth: {
2168 const auto ofs = isRightButton
2169 ? option->rect.width() - 2 - buttonWidth - contentItemHMargin
2170 : 2 + buttonWidth + contentItemHMargin;
2171 const QPoint tl(tab->rect.topLeft() + QPoint(ofs, 0));
2172 ret = QRect(tl, QSize(1, option->rect.height()));
2173 break;
2174 }
2175 case QTabBar::RoundedWest:
2176 case QTabBar::TriangularWest:
2177 case QTabBar::RoundedEast:
2178 case QTabBar::TriangularEast: {
2179 const auto ofs = isRightButton
2180 ? option->rect.height() - 2 - buttonWidth - contentItemHMargin
2181 : 2 + buttonWidth + contentItemHMargin;
2182 const QPoint tl(tab->rect.topLeft() + QPoint(0, ofs));
2183 ret = QRect(tl, QSize(option->rect.width(), 1));
2184 break;
2185 }
2186 default:
2187 break;
2188 }
2189 ret = visualRect(option->direction, option->rect, ret);
2190 }
2191 break;
2192#endif // QT_CONFIG(toolbar)
2193 case QStyle::SE_HeaderLabel:
2194 case QStyle::SE_HeaderArrow:
2195 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2196 break;
2197 case SE_PushButtonContents: {
2198 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2199 ret = option->rect.marginsRemoved(QMargins(border, border, border, border));
2200 break;
2201 }
2202 default:
2203 ret = QWindowsVistaStyle::subElementRect(element, option, widget);
2204 }
2205 return ret;
2206}
2207
2208/*!
2209 \internal
2210 */
2211QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
2212 SubControl subControl, const QWidget *widget) const
2213{
2214 QRect ret;
2215
2216 switch (control) {
2217#if QT_CONFIG(spinbox)
2218 case CC_SpinBox:
2219 if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
2220 const bool hasButtons = spinbox->buttonSymbols != QAbstractSpinBox::NoButtons;
2221 const int fw = spinbox->frame
2222 ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget)
2223 : 0;
2224 const int buttonHeight = hasButtons
2225 ? qMin(spinbox->rect.height() - 3 * fw, spinbox->fontMetrics.height() * 5 / 4)
2226 : 0;
2227 const QSize buttonSize(buttonHeight * 6 / 5, buttonHeight);
2228 const int textFieldLength = spinbox->rect.width() - 2 * fw - 2 * buttonSize.width();
2229 const QPoint topLeft(spinbox->rect.topLeft() + QPoint(fw, fw));
2230 switch (subControl) {
2231 case SC_SpinBoxUp:
2232 case SC_SpinBoxDown: {
2233 if (!hasButtons)
2234 return QRect();
2235 const int yOfs = ((spinbox->rect.height() - 2 * fw) - buttonSize.height()) / 2;
2236 ret = QRect(topLeft.x() + textFieldLength, topLeft.y() + yOfs, buttonSize.width(),
2237 buttonSize.height());
2238 if (subControl == SC_SpinBoxDown)
2239 ret.moveRight(ret.right() + buttonSize.width());
2240 break;
2241 }
2242 case SC_SpinBoxEditField:
2243 ret = QRect(topLeft,
2244 spinbox->rect.bottomRight() - QPoint(fw + 2 * buttonSize.width(), fw));
2245 break;
2246 case SC_SpinBoxFrame:
2247 ret = spinbox->rect;
2248 default:
2249 break;
2250 }
2251 ret = visualRect(spinbox->direction, spinbox->rect, ret);
2252 }
2253 break;
2254 case CC_TitleBar:
2255 if (const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
2256 SubControl sc = subControl;
2257 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
2258 static constexpr int indent = 3;
2259 static constexpr int controlWidthMargin = 2;
2260 const int controlHeight = titlebar->rect.height();
2261 const int controlWidth = 46;
2262 const int iconSize = proxy()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, option, widget);
2263 int offset = -(controlWidthMargin + indent);
2264
2265 bool isMinimized = titlebar->titleBarState & Qt::WindowMinimized;
2266 bool isMaximized = titlebar->titleBarState & Qt::WindowMaximized;
2267
2268 switch (sc) {
2269 case SC_TitleBarLabel:
2270 if (titlebar->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) {
2271 ret = titlebar->rect;
2272 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2273 ret.adjust(iconSize + controlWidthMargin + indent, 0, -controlWidth, 0);
2274 if (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)
2275 ret.adjust(0, 0, -controlWidth, 0);
2276 if (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)
2277 ret.adjust(0, 0, -controlWidth, 0);
2278 if (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)
2279 ret.adjust(0, 0, -controlWidth, 0);
2280 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2281 ret.adjust(0, 0, -controlWidth, 0);
2282 }
2283 break;
2284 case SC_TitleBarContextHelpButton:
2285 if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)
2286 offset += controlWidth;
2287 Q_FALLTHROUGH();
2288 case SC_TitleBarMinButton:
2289 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2290 offset += controlWidth;
2291 else if (sc == SC_TitleBarMinButton)
2292 break;
2293 Q_FALLTHROUGH();
2294 case SC_TitleBarNormalButton:
2295 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint))
2296 offset += controlWidth;
2297 else if (isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2298 offset += controlWidth;
2299 else if (sc == SC_TitleBarNormalButton)
2300 break;
2301 Q_FALLTHROUGH();
2302 case SC_TitleBarMaxButton:
2303 if (!isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint))
2304 offset += controlWidth;
2305 else if (sc == SC_TitleBarMaxButton)
2306 break;
2307 Q_FALLTHROUGH();
2308 case SC_TitleBarShadeButton:
2309 if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2310 offset += controlWidth;
2311 else if (sc == SC_TitleBarShadeButton)
2312 break;
2313 Q_FALLTHROUGH();
2314 case SC_TitleBarUnshadeButton:
2315 if (isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint))
2316 offset += controlWidth;
2317 else if (sc == SC_TitleBarUnshadeButton)
2318 break;
2319 Q_FALLTHROUGH();
2320 case SC_TitleBarCloseButton:
2321 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)
2322 offset += controlWidth;
2323 else if (sc == SC_TitleBarCloseButton)
2324 break;
2325 ret.setRect(titlebar->rect.right() - offset, titlebar->rect.top(),
2326 controlWidth, controlHeight);
2327 break;
2328 case SC_TitleBarSysMenu:
2329 if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) {
2330 const auto yOfs = titlebar->rect.top() + (titlebar->rect.height() - iconSize) / 2;
2331 ret.setRect(titlebar->rect.left() + controlWidthMargin + indent, yOfs, iconSize,
2332 iconSize);
2333 }
2334 break;
2335 default:
2336 break;
2337 }
2338 if (widget && isMinimized && titlebar->rect.width() < offset)
2339 const_cast<QWidget*>(widget)->resize(controlWidthMargin + indent + offset + iconSize + controlWidthMargin, controlWidth);
2340 ret = visualRect(titlebar->direction, titlebar->rect, ret);
2341 }
2342 break;
2343#endif // Qt_NO_SPINBOX
2344 case CC_ScrollBar:
2345 {
2346 ret = QCommonStyle::subControlRect(control, option, subControl, widget);
2347
2348 if (subControl == SC_ScrollBarAddLine || subControl == SC_ScrollBarSubLine) {
2349 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2350 if (scrollbar->orientation == Qt::Vertical)
2351 ret = ret.adjusted(2,2,-2,-3);
2352 else
2353 ret = ret.adjusted(3,2,-2,-2);
2354 }
2355 }
2356 break;
2357 }
2358 case CC_ComboBox: {
2359 if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2360 const auto indicatorWidth =
2361 proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
2362 switch (subControl) {
2363 case SC_ComboBoxArrow: {
2364 const int fw =
2365 cb->frame ? proxy()->pixelMetric(PM_ComboBoxFrameWidth, cb, widget) : 0;
2366 const int buttonHeight =
2367 qMin(cb->rect.height() - 3 * fw, cb->fontMetrics.height() * 5 / 4);
2368 const QSize buttonSize(buttonHeight * 6 / 5, buttonHeight);
2369 const int textFieldLength = cb->rect.width() - 2 * fw - buttonSize.width();
2370 const QPoint topLeft(cb->rect.topLeft() + QPoint(fw, fw));
2371 const int yOfs = ((cb->rect.height() - 2 * fw) - buttonSize.height()) / 2;
2372 ret = QRect(topLeft.x() + textFieldLength, topLeft.y() + yOfs, buttonSize.width(),
2373 buttonSize.height());
2374 ret = visualRect(option->direction, option->rect, ret);
2375 break;
2376 }
2377 case SC_ComboBoxEditField: {
2378 ret = option->rect;
2379 if (cb->frame) {
2380 const int fw = proxy()->pixelMetric(PM_ComboBoxFrameWidth, cb, widget);
2381 ret = ret.marginsRemoved(QMargins(fw, fw, fw, fw));
2382 }
2383 ret.setWidth(ret.width() - indicatorWidth - contentHMargin * 2);
2384 ret = visualRect(option->direction, option->rect, ret);
2385 break;
2386 }
2387 default:
2388 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2389 break;
2390 }
2391 }
2392 break;
2393 }
2394#if QT_CONFIG(groupbox)
2395 case CC_GroupBox: {
2396 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2397 switch (subControl) {
2398 case SC_GroupBoxCheckBox:
2399 ret.moveTop(1);
2400 break;
2401 default:
2402 break;
2403 }
2404 break;
2405 }
2406#endif // QT_CONFIG(groupbox)
2407 default:
2408 ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
2409 }
2410 return ret;
2411}
2412
2413/*!
2414 \internal
2415 */
2416QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *option,
2417 const QSize &size, const QWidget *widget) const
2418{
2419 QSize contentSize(size);
2420
2421 switch (type) {
2422
2423#if QT_CONFIG(menubar)
2424 case CT_MenuBarItem:
2425 if (!contentSize.isEmpty()) {
2426 constexpr int hMargin = 2 * 6;
2427 constexpr int hPadding = 2 * 11;
2428 constexpr int itemHeight = 32;
2429 contentSize.setWidth(contentSize.width() + hMargin + hPadding);
2430#if QT_CONFIG(tabwidget)
2431 if (widget->parent() && !qobject_cast<const QTabWidget *>(widget->parent()))
2432#endif
2433 contentSize.setHeight(itemHeight);
2434 }
2435 break;
2436#endif
2437#if QT_CONFIG(menu)
2438 case CT_MenuItem:
2439 if (const auto *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2440 int width = size.width();
2441 int height;
2442 if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
2443 width = 10;
2444 height = 3;
2445 } else {
2446 height = menuItem->fontMetrics.height() + 8;
2447 if (!menuItem->icon.isNull()) {
2448 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2449 height = qMax(height,
2450 menuItem->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
2451 }
2452 }
2453 if (menuItem->text.contains(u'\t'))
2454 width += contentItemHMargin; // the text width is already in
2455 if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu)
2456 width += 2 * QWindowsStylePrivate::windowsArrowHMargin + contentItemHMargin;
2457 if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) {
2458 const QFontMetrics fm(menuItem->font);
2459 QFont fontBold = menuItem->font;
2460 fontBold.setBold(true);
2461 const QFontMetrics fmBold(fontBold);
2462 width += fmBold.horizontalAdvance(menuItem->text) - fm.horizontalAdvance(menuItem->text);
2463 }
2464 // in contrast to windowsvista, the checkmark and icon are drawn separately
2465 if (menuItem->menuHasCheckableItems) {
2466 const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget);
2467 width += checkMarkWidth + contentItemHMargin * 2;
2468 }
2469 // we have an icon and it's already in the given size, only add margins
2470 // 4 is added in qmenu.cpp to PM_SmallIconSize
2471 if (menuItem->maxIconWidth > 0)
2472 width += contentItemHMargin * 2 + menuItem->maxIconWidth - 4;
2473 width += 2 * 2; // margins for rounded border
2474 width += 2 * contentHMargin;
2475 if (width < 100) // minimum size
2476 width = 100;
2477 contentSize = QSize(width, height);
2478 }
2479 break;
2480#endif // QT_CONFIG(menu)
2481#if QT_CONFIG(spinbox)
2482 case CT_SpinBox: {
2483 if (const auto *spinBoxOpt = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
2484 // Add button + frame widths
2485 const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons);
2486 const int margins = 8;
2487 const int buttonWidth = hasButtons ? 16 + contentItemHMargin : 0;
2488 const int frameWidth = spinBoxOpt->frame
2489 ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget)
2490 : 0;
2491
2492 contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth);
2493 }
2494 break;
2495 }
2496#endif
2497#if QT_CONFIG(combobox)
2498 case CT_ComboBox:
2499 if (const auto *comboBoxOpt = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2500 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
2501 contentSize += QSize(0, 4); // for the lineedit frame
2502 if (comboBoxOpt->subControls & SC_ComboBoxArrow) {
2503 const auto w = proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
2504 contentSize.rwidth() += w + contentItemHMargin;
2505 }
2506 }
2507 break;
2508#endif
2509 case CT_LineEdit: {
2510 if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
2511 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
2512 contentSize += QSize(0, 4); // for the lineedit frame
2513 }
2514 break;
2515 }
2516 case CT_HeaderSection:
2517 // windows vista does not honor the indicator (as it was drawn above the text, not on the
2518 // side) so call QWindowsStyle::styleHint directly to get the correct size hint
2519 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
2520 break;
2521 case CT_RadioButton:
2522 case CT_CheckBox:
2523 if (const auto *buttonOpt = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2524 const auto p = proxy();
2525 const bool isRadio = (type == CT_RadioButton);
2526
2527 const int width = p->pixelMetric(
2528 isRadio ? PM_ExclusiveIndicatorWidth : PM_IndicatorWidth, option, widget);
2529 const int height = p->pixelMetric(
2530 isRadio ? PM_ExclusiveIndicatorHeight : PM_IndicatorHeight, option, widget);
2531
2532 int margins = 2 * contentItemHMargin;
2533 if (!buttonOpt->icon.isNull() || !buttonOpt->text.isEmpty()) {
2534 margins += p->pixelMetric(isRadio ? PM_RadioButtonLabelSpacing
2535 : PM_CheckBoxLabelSpacing,
2536 option, widget);
2537 }
2538
2539 contentSize += QSize(width + margins, 4);
2540 contentSize.setHeight(qMax(size.height(), height + 2 * contentItemHMargin));
2541 }
2542 break;
2543
2544 // the indicator needs 2px more in width when there is no text, not needed when
2545 // the style draws the text
2546 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2547 if (size.width() == 0)
2548 contentSize.rwidth() += 2;
2549 break;
2550 case CT_PushButton: {
2551 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2552 // we want our own horizontal spacing
2553 const int oldMargin = proxy()->pixelMetric(PM_ButtonMargin, option, widget);
2554 contentSize.rwidth() += 2 * contentHMargin - oldMargin;
2555 break;
2556 }
2557 case CT_ToolButton: {
2558 contentSize = size;
2559 if (const auto tb = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
2560 // no separate menu, only dropdown icon
2561 if (!tb->subControls.testFlag(SC_ToolButtonMenu)) {
2562 if (tb->features.testFlag(QStyleOptionToolButton::HasMenu))
2563 contentSize.rwidth() += 2;
2564 }
2565 if (tb->toolButtonStyle == Qt::ToolButtonTextBesideIcon
2566 || tb->toolButtonStyle == Qt::ToolButtonIconOnly) {
2567 contentSize.rheight() = qMax(contentSize.height(), tb->iconSize.height() + 4);
2568 }
2569 }
2570 const auto fw = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2571 contentSize += QSize(contentHMargin + 2 * fw, 2 * fw);
2572 break;
2573 }
2574 case CT_ItemViewItem: {
2575 if (const auto *viewItemOpt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
2576 if (const QListView *lv = qobject_cast<const QListView *>(widget);
2577 lv && lv->viewMode() != QListView::IconMode) {
2578 QStyleOptionViewItem vOpt(*viewItemOpt);
2579 // viewItemSize only takes PM_FocusFrameHMargin into account but no additional
2580 // margin, therefore adjust it here for a correct width during layouting when
2581 // WrapText is enabled
2582 vOpt.rect.setRight(vOpt.rect.right() - contentHMargin);
2583 contentSize = QWindowsVistaStyle::sizeFromContents(type, &vOpt, size, widget);
2584 contentSize.rwidth() += contentHMargin;
2585 contentSize.rheight() += 2 * contentHMargin;
2586
2587 } else {
2588 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2589 }
2590 }
2591 break;
2592 }
2593 default:
2594 contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget);
2595 break;
2596 }
2597
2598 return contentSize;
2599}
2600
2601
2602/*!
2603 \internal
2604 */
2605int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
2606{
2607 Q_D(const QWindows11Style);
2608 int res = 0;
2609
2610 switch (metric) {
2611 case QStyle::PM_IndicatorWidth:
2612 case QStyle::PM_IndicatorHeight:
2613 case QStyle::PM_ExclusiveIndicatorWidth:
2614 case QStyle::PM_ExclusiveIndicatorHeight:
2615 res = 16;
2616 break;
2617 case PM_SliderThickness: // full height of a slider
2618 if (auto opt = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2619 // hard-coded in qslider.cpp, but we need a little bit more
2620 constexpr auto TickSpace = 5;
2621 if (opt->tickPosition & QSlider::TicksAbove)
2622 res += 6 - TickSpace;
2623 if (opt->tickPosition & QSlider::TicksBelow)
2624 res += 6 - TickSpace;
2625 }
2626 Q_FALLTHROUGH();
2627 case PM_SliderControlThickness: // size of the control handle
2628 case PM_SliderLength: // same because handle is a circle with r=8
2629 res += 2 * 8;
2630 break;
2631 case PM_RadioButtonLabelSpacing:
2632 case PM_CheckBoxLabelSpacing:
2633 res = 2 * contentItemHMargin;
2634 break;
2635 case QStyle::PM_TitleBarButtonIconSize:
2636 res = 16;
2637 break;
2638 case QStyle::PM_TitleBarButtonSize:
2639 res = 32;
2640 break;
2641#if QT_CONFIG(toolbar)
2642 case PM_TabBarScrollButtonWidth:
2643 res = 16 + contentItemHMargin;
2644 break;
2645 case PM_ToolBarExtensionExtent:
2646 res = int(QStyleHelper::dpiScaled(32., option));
2647 break;
2648 case PM_ToolBarHandleExtent:
2649 res = int(QStyleHelper::dpiScaled(8., option));
2650 break;
2651#endif // QT_CONFIG(toolbar)
2652 case QStyle::PM_ScrollBarExtent:
2653 res = 12;
2654 break;
2655 case QStyle::PM_SubMenuOverlap:
2656 res = -1;
2657 break;
2658 case PM_MenuButtonIndicator: {
2659 res = contentItemHMargin;
2660 if (widget) {
2661 const int fontSize = widget->font().pointSize();
2662 auto it = m_fontPoint2ChevronDownMedWidth.find(fontSize);
2663 if (it == m_fontPoint2ChevronDownMedWidth.end()) {
2664 QFont f(d->assetFont);
2665 f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller
2666 QFontMetrics fm(f);
2667 const auto width = fm.horizontalAdvance(fluentIcon(Icon::ChevronDownMed));
2668 m_fontPoint2ChevronDownMedWidth.insert(fontSize, width);
2669 res += width;
2670 } else {
2671 res += it.value();
2672 }
2673 } else {
2674 res += 12;
2675 }
2676 break;
2677 }
2678 case PM_ComboBoxFrameWidth:
2679 case PM_SpinBoxFrameWidth:
2680 case PM_DefaultFrameWidth:
2681 res = 2;
2682 break;
2683 case PM_ButtonShiftHorizontal:
2684 case PM_ButtonShiftVertical:
2685 case PM_TabBarTabShiftHorizontal:
2686 case PM_TabBarTabShiftVertical:
2687 case PM_TabBarBaseOverlap:
2688 case PM_TabBarBaseHeight:
2689 res = 0;
2690 break;
2691 case PM_TreeViewIndentation:
2692 res = 30;
2693 break;
2694 case PM_ProgressBarChunkWidth:
2695 res = 0; // no chunks on windows11
2696 break;
2697 default:
2698 res = QWindowsVistaStyle::pixelMetric(metric, option, widget);
2699 }
2700
2701 return res;
2702}
2703
2704void QWindows11Style::polish(QWidget* widget)
2705{
2706 Q_D(QWindows11Style);
2707
2708#if QT_CONFIG(commandlinkbutton)
2709 if (!qobject_cast<QCommandLinkButton *>(widget))
2710#endif // QT_CONFIG(commandlinkbutton)
2711 QWindowsVistaStyle::polish(widget);
2712
2713 const bool isScrollBar = qobject_cast<QScrollBar *>(widget);
2714 const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
2715 const auto isComboBoxContainer = !d->nativeRoundedTopLevelWindows && comboBoxContainer != nullptr;
2716 const auto isMenu = !d->nativeRoundedTopLevelWindows && qobject_cast<QMenu *>(widget) != nullptr;
2717
2718#if QT_CONFIG(menubar)
2719 if (qobject_cast<QMenuBar *>(widget)) {
2720 constexpr int itemHeight = 32;
2721 if (widget->maximumHeight() < itemHeight) {
2722 widget->setProperty("_q_original_menubar_maxheight", widget->maximumHeight());
2723 widget->setMaximumHeight(itemHeight);
2724 }
2725 }
2726#endif
2727 if (comboBoxContainer && d->nativeRoundedTopLevelWindows) {
2728 auto view = comboBoxContainer->itemView();
2729 if (view && view->viewport())
2730 view->viewport()->setAutoFillBackground(false);
2731 }
2732 if (isScrollBar || isMenu || isComboBoxContainer) {
2733 bool wasCreated = widget->testAttribute(Qt::WA_WState_Created);
2734 bool layoutDirection = widget->testAttribute(Qt::WA_RightToLeft);
2735 widget->setAttribute(Qt::WA_OpaquePaintEvent,false);
2736 widget->setAttribute(Qt::WA_TranslucentBackground);
2737 if (!isScrollBar)
2738 widget->setWindowFlag(Qt::FramelessWindowHint);
2739 widget->setWindowFlag(Qt::NoDropShadowWindowHint);
2740 widget->setAttribute(Qt::WA_RightToLeft, layoutDirection);
2741 widget->setAttribute(Qt::WA_WState_Created, wasCreated);
2742 if (!isScrollBar) {
2743 bool inGraphicsView = widget->graphicsProxyWidget() != nullptr;
2744 if (!inGraphicsView && comboBoxContainer && comboBoxContainer->parentWidget())
2745 inGraphicsView = comboBoxContainer->parentWidget()->graphicsProxyWidget() != nullptr;
2746 if (!inGraphicsView) { // for menus and combobox containers...
2747 QGraphicsDropShadowEffect *dropshadow = new QGraphicsDropShadowEffect(widget);
2748 dropshadow->setBlurRadius(3);
2749 dropshadow->setXOffset(3);
2750 dropshadow->setYOffset(3);
2751 widget->setGraphicsEffect(dropshadow);
2752 }
2753 }
2754 } else if (QComboBox* cb = qobject_cast<QComboBox*>(widget)) {
2755 if (cb->isEditable()) {
2756 QLineEdit *le = cb->lineEdit();
2757 le->setFrame(false);
2758 }
2759 } else if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2760 scrollarea
2761 && !qobject_cast<QGraphicsView *>(widget)
2762#if QT_CONFIG(mdiarea)
2763 && !qobject_cast<QMdiArea *>(widget)
2764#endif
2765 ) {
2766 if (scrollarea->frameShape() == QFrame::StyledPanel) {
2767 const auto vp = scrollarea->viewport();
2768 const bool isAutoFillBackground = vp->autoFillBackground();
2769 const bool isStyledBackground = vp->testAttribute(Qt::WA_StyledBackground);
2770 vp->setProperty("_q_original_autofill_background", isAutoFillBackground);
2771 vp->setProperty("_q_original_styled_background", isStyledBackground);
2772 vp->setAutoFillBackground(false);
2773 vp->setAttribute(Qt::WA_StyledBackground, true);
2774 }
2775 // QTreeView & QListView are already set in the base windowsvista style
2776 if (auto table = qobject_cast<QTableView *>(widget))
2777 table->viewport()->setAttribute(Qt::WA_Hover, true);
2778 }
2779}
2780
2781void QWindows11Style::unpolish(QWidget *widget)
2782{
2783 Q_D(QWindows11Style);
2784#if QT_CONFIG(commandlinkbutton)
2785 if (!qobject_cast<QCommandLinkButton *>(widget))
2786#endif // QT_CONFIG(commandlinkbutton)
2787 QWindowsVistaStyle::unpolish(widget);
2788
2789#if QT_CONFIG(menubar)
2790 if (qobject_cast<QMenuBar *>(widget) && !widget->property("_q_original_menubar_maxheight").isNull()) {
2791 widget->setMaximumHeight(widget->property("_q_original_menubar_maxheight").toInt());
2792 widget->setProperty("_q_original_menubar_maxheight", QVariant());
2793 }
2794#endif
2795 const bool isScrollBar = qobject_cast<QScrollBar *>(widget);
2796 const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
2797 const auto isComboBoxContainer = !d->nativeRoundedTopLevelWindows && comboBoxContainer != nullptr;
2798 const auto isMenu = !d->nativeRoundedTopLevelWindows && qobject_cast<QMenu *>(widget) != nullptr;
2799
2800 if (comboBoxContainer && d->nativeRoundedTopLevelWindows) {
2801 auto view = comboBoxContainer->itemView();
2802 // see QAbstractScrollAreaPrivate::init()
2803 if (view && view->viewport())
2804 view->viewport()->setAutoFillBackground(true);
2805 }
2806 if (isScrollBar || isMenu || isComboBoxContainer) {
2807 widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
2808 widget->setAttribute(Qt::WA_TranslucentBackground, false);
2809 widget->setWindowFlag(Qt::FramelessWindowHint, false);
2810 widget->setWindowFlag(Qt::NoDropShadowWindowHint, false);
2811 }
2812
2813 if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
2814 scrollarea
2815#if QT_CONFIG(mdiarea)
2816 && !qobject_cast<QMdiArea *>(widget)
2817#endif
2818 ) {
2819 const auto vp = scrollarea->viewport();
2820 const auto wasAutoFillBackground = vp->property("_q_original_autofill_background").toBool();
2821 vp->setAutoFillBackground(wasAutoFillBackground);
2822 vp->setProperty("_q_original_autofill_background", QVariant());
2823 const auto origStyledBackground = vp->property("_q_original_styled_background").toBool();
2824 vp->setAttribute(Qt::WA_StyledBackground, origStyledBackground);
2825 vp->setProperty("_q_original_styled_background", QVariant());
2826 }
2827 dwmSetWindowCornerPreference(widget, false);
2828}
2829
2830void QWindows11Style::polish(QApplication *app)
2831{
2832 Q_D(const QWindows11Style);
2833 if (d->nativeRoundedTopLevelWindows)
2834 app->installEventFilter(this);
2835 QWindowsVistaStyle::polish(app);
2836}
2837
2838void QWindows11Style::unpolish(QApplication *app)
2839{
2840 Q_D(const QWindows11Style);
2841 if (d->nativeRoundedTopLevelWindows)
2842 app->removeEventFilter(this);
2843 QWindowsVistaStyle::unpolish(app);
2844}
2845
2846bool QWindows11Style::eventFilter(QObject *obj, QEvent *event)
2847{
2848 // QEvent::WinIdChange is to early so we have to wait for QEvent::Show
2849 if (event->type() == QEvent::Show)
2850 dwmSetWindowCornerPreference(qobject_cast<QWidget *>(obj), true);
2851 return QWindowsVistaStyle::eventFilter(obj, event);
2852}
2853
2854void QWindows11Style::dwmSetWindowCornerPreference(const QWidget *widget, bool bSet) const
2855{
2856 Q_D(const QWindows11Style);
2857 if (!d->nativeRoundedTopLevelWindows)
2858 return;
2859#ifdef Q_CC_MSVC
2860 static constexpr auto dmwmaWindowCornerPreference = DWMWA_WINDOW_CORNER_PREFERENCE;
2861 static constexpr auto dwmcpRound = DWMWCP_ROUND;
2862 static constexpr auto dwmcpRoundSmall = DWMWCP_ROUNDSMALL;
2863 static constexpr auto dwmcpDefault = DWMWCP_DEFAULT;
2864#else
2865 // MinGW 13.1.0 does not provide this
2866 static constexpr auto dmwmaWindowCornerPreference = 33;
2867 static constexpr auto dwmcpRound = 2;
2868 static constexpr auto dwmcpRoundSmall = 3;
2869 static constexpr auto dwmcpDefault = 0;
2870#endif
2871 if (widget && widget->isWindow() && widget->testAttribute(Qt::WA_WState_Created)
2872 && !widget->windowFlags().testFlag(Qt::FramelessWindowHint)) {
2873 const auto window = widget->windowHandle();
2874 if (window && window->handle()) {
2875 const auto wId = reinterpret_cast<HWND>(widget->winId());
2876 if (wId != 0) {
2877 const bool isToolTip =
2878#if QT_CONFIG(tooltip)
2879 qobject_cast<const QTipLabel *>(widget) != nullptr;
2880#else
2881 false;
2882#endif
2883 uint32_t pref = bSet ? (isToolTip ? dwmcpRoundSmall : dwmcpRound) : dwmcpDefault;
2884 DwmSetWindowAttribute(wId, dmwmaWindowCornerPreference, &pref, sizeof(pref));
2885 }
2886 }
2887 }
2888}
2889
2890/*
2891The colors for Windows 11 are taken from the official WinUI3 Figma style at
2892https://www.figma.com/community/file/1159947337437047524
2893*/
2894#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE)
2895 if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged)
2896 result.setColor(GROUP, ROLE, VALUE)
2897
2898static void populateLightSystemBasePalette(QPalette &result)
2899{
2900 static QString oldStyleSheet;
2901 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2902
2903 constexpr QColor textColor = QColor(0x00,0x00,0x00,0xE4);
2904 constexpr QColor textDisabled = QColor(0x00, 0x00, 0x00, 0x5C);
2905 constexpr QColor btnFace = QColor(0xFF, 0xFF, 0xFF, 0xB3);
2906 constexpr QColor base = QColor(0xFF, 0xFF, 0xFF, 0xFF);
2907 constexpr QColor alternateBase = QColor(0x00, 0x00, 0x00, 0x09);
2908 const QColor btnHighlight = result.accent().color();
2909 const QColor btnColor = result.button().color();
2910
2911 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Highlight, btnHighlight);
2912 SET_IF_UNRESOLVED(QPalette::Active, QPalette::WindowText, textColor);
2913 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Button, btnFace);
2914 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Light, btnColor.lighter(150));
2915 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Dark, btnColor.darker(200));
2916 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Mid, btnColor.darker(150));
2917 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Text, textColor);
2918 SET_IF_UNRESOLVED(QPalette::Active, QPalette::BrightText, btnHighlight);
2919 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Base, base);
2920 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2921 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ButtonText, textColor);
2922 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Midlight, btnColor.lighter(125));
2923 SET_IF_UNRESOLVED(QPalette::Active, QPalette::Shadow, Qt::black);
2924 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipBase, result.window().color());
2925 SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipText, result.windowText().color());
2926 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2927
2928 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, btnHighlight);
2929 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, textColor);
2930 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, btnFace);
2931 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, btnColor.lighter(150));
2932 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, btnColor.darker(200));
2933 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Mid, btnColor.darker(150));
2934 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, textColor);
2935 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::BrightText, btnHighlight);
2936 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Base, base);
2937 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
2938 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ButtonText, textColor);
2939 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Midlight, btnColor.lighter(125));
2940 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Shadow, Qt::black);
2941 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipBase, result.window().color());
2942 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipText, result.windowText().color());
2943 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2944
2945 result.setColor(QPalette::Disabled, QPalette::WindowText, textDisabled);
2946
2947 if (result.midlight() == result.button())
2948 result.setColor(QPalette::Midlight, btnColor.lighter(110));
2949 oldStyleSheet = qApp->styleSheet();
2950}
2951
2952static void populateDarkSystemBasePalette(QPalette &result)
2953{
2954 static QString oldStyleSheet;
2955 const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
2956
2957 constexpr QColor alternateBase = QColor(0xFF, 0xFF, 0xFF, 0x0F);
2958
2959 SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase);
2960
2961 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase);
2962
2963 oldStyleSheet = qApp->styleSheet();
2964}
2965
2966/*!
2967 \internal
2968 */
2969void QWindows11Style::polish(QPalette& result)
2970{
2971 highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown;
2972 colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
2973
2974 if (!highContrastTheme && colorSchemeIndex == 0)
2975 populateLightSystemBasePalette(result);
2976 else if (!highContrastTheme && colorSchemeIndex == 1)
2977 populateDarkSystemBasePalette(result);
2978
2979 const bool styleSheetChanged = false; // so the macro works
2980
2981 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color());
2982 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color());
2983 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color());
2984 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color());
2985 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color());
2986 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color());
2987 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color());
2988 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color());
2989 SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color());
2990
2991 auto *d = const_cast<QWindows11StylePrivate *>(d_func());
2992 d->m_standardIcons.clear();
2993}
2994
2995QPixmap QWindows11Style::standardPixmap(StandardPixmap standardPixmap,
2996 const QStyleOption *option,
2997 const QWidget *widget) const
2998{
2999 switch (standardPixmap) {
3000 case SP_TabScrollLeftButton:
3001 case SP_TabScrollRightButton:
3002 case SP_TabScrollUpButton:
3003 case SP_TabScrollDownButton:
3004 case SP_ToolBarHorizontalExtensionButton:
3005 case SP_ToolBarVerticalExtensionButton: {
3006 const int size = proxy()->pixelMetric(PM_ToolBarExtensionExtent, option, widget);
3007 return standardIcon(standardPixmap, option, widget).pixmap(size);
3008 }
3009 default:
3010 break;
3011 }
3012 return QWindowsVistaStyle::standardPixmap(standardPixmap, option, widget);
3013}
3014
3015QIcon QWindows11Style::standardIcon(StandardPixmap standardIcon,
3016 const QStyleOption *option,
3017 const QWidget *widget) const
3018{
3019 const auto getIcon = [&](Icon ico, double scale) {
3020 auto *d = const_cast<QWindows11StylePrivate *>(d_func());
3021 QIcon &icon = d->m_standardIcons[ico];
3022 if (icon.isNull()) {
3023 auto engine = new WinFontIconEngine(fluentIcon(ico), d->assetFont);
3024 engine->setScale(scale);
3025 icon = QIcon(engine);
3026 }
3027 return icon;
3028 };
3029
3030 switch (standardIcon) {
3031 case SP_TitleBarMinButton:
3032 return getIcon(Icon::ChromeMinimize, 0.7);
3033 case SP_TitleBarMaxButton:
3034 return getIcon(Icon::ChromeMaximize, 0.7);
3035 case SP_TitleBarCloseButton:
3036 return getIcon(Icon::ChromeClose, 0.7);
3037 case SP_TitleBarNormalButton:
3038 return getIcon(Icon::ChromeRestore, 0.7);
3039 case SP_LineEditClearButton:
3040 return getIcon(Icon::Clear, 0.7);
3041 case SP_ToolBarHorizontalExtensionButton:
3042 case SP_ToolBarVerticalExtensionButton:
3043 return getIcon(Icon::More, 1.0);
3044 case SP_TabCloseButton:
3045 return getIcon(Icon::ChromeClose, 0.6);
3046 case SP_TabScrollLeftButton:
3047 return getIcon(Icon::CaretLeftSolid8, 0.5);
3048 case SP_TabScrollRightButton:
3049 return getIcon(Icon::CaretRightSolid8, 0.5);
3050 case SP_TabScrollUpButton:
3051 return getIcon(Icon::CaretUpSolid8, 0.5);
3052 case SP_TabScrollDownButton:
3053 return getIcon(Icon::CaretDownSolid8, 0.5);
3054 default:
3055 break;
3056 }
3057 return QWindowsVistaStyle::standardIcon(standardIcon, option, widget);
3058}
3059
3060QColor QWindows11Style::calculateAccentColor(const QStyleOption *option) const
3061{
3062 using namespace StyleOptionHelper;
3063 if (isDisabled(option))
3064 return winUI3Color(fillAccentDisabled);
3065 const auto alphaColor = isPressed(option) ? fillAccentTertiary
3066 : isHover(option) ? fillAccentSecondary
3067 : fillAccentDefault;
3068 const auto alpha = winUI3Color(alphaColor);
3069 QColor col = option->palette.accent().color();
3070 col.setAlpha(alpha.alpha());
3071 return col;
3072}
3073
3074QPen QWindows11Style::borderPenControlAlt(const QStyleOption *option) const
3075{
3076 using namespace StyleOptionHelper;
3077 if (isChecked(option))
3078 return Qt::NoPen; // same color as fill color, so no pen needed
3079 if (highContrastTheme)
3080 return option->palette.buttonText().color();
3081 if (isDisabled(option) || isPressed(option))
3082 return winUI3Color(frameColorStrongDisabled);
3083 return winUI3Color(frameColorStrong);
3084}
3085
3086QBrush QWindows11Style::controlFillBrush(const QStyleOption *option, ControlType controlType) const
3087{
3088 using namespace StyleOptionHelper;
3089 static constexpr WINUI3Color colorEnums[2][4] = {
3090 // Light & Dark Control
3093 // Light & Dark Control Alt
3096 };
3097
3098 if (option->palette.isBrushSet(QPalette::Current, QPalette::Button))
3099 return option->palette.button();
3100
3101 if (!isChecked(option) && isAutoRaise(option))
3102 return Qt::NoBrush;
3103
3104 // checked is the same for Control (Buttons) and Control Alt (Radiobuttons/Checkboxes)
3105 if (isChecked(option))
3106 return calculateAccentColor(option);
3107
3108 const auto state = calcControlState(option);
3109 return winUI3Color(colorEnums[int(controlType)][int(state)]);
3110}
3111
3112QBrush QWindows11Style::inputFillBrush(const QStyleOption *option, const QWidget *widget) const
3113{
3114 // slightly different states than in controlFillBrush
3115 using namespace StyleOptionHelper;
3116 const auto role = widget ? widget->backgroundRole() : QPalette::Window;
3117 if (option->palette.isBrushSet(QPalette::Current, role))
3118 return option->palette.button();
3119
3120 if (isDisabled(option))
3121 return winUI3Color(fillControlDisabled);
3122 if (hasFocus(option))
3123 return winUI3Color(fillControlInputActive);
3124 if (isHover(option))
3125 return winUI3Color(fillControlSecondary);
3126 return winUI3Color(fillControlDefault);
3127}
3128
3129QColor QWindows11Style::controlTextColor(const QStyleOption *option, bool ignoreIsChecked) const
3130{
3131 using namespace StyleOptionHelper;
3132 static constexpr WINUI3Color colorEnums[2][4] = {
3133 // Control, unchecked
3135 // Control, checked
3137 };
3138
3139 if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText))
3140 return option->palette.buttonText().color();
3141
3142 const int colorIndex = !ignoreIsChecked && isChecked(option) ? 1 : 0;
3143 const auto state = calcControlState(option);
3144 return winUI3Color(colorEnums[colorIndex][int(state)]);
3145}
3146
3147void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const
3148{
3149 const bool isHovered = o->state & State_MouseOver;
3150 const auto frameCol = highContrastTheme
3151 ? o->palette.color(isHovered ? QPalette::Accent
3152 : QPalette::ButtonText)
3153 : winUI3Color(frameColorLight);
3154 drawRoundedRect(p, rect, frameCol, Qt::NoBrush);
3155
3156 if (!isEditable || StyleOptionHelper::isDisabled(o))
3157 return;
3158
3159 const bool hasFocus = o->state & State_HasFocus;
3160 const auto underlineCol = hasFocus
3161 ? o->palette.color(QPalette::Accent)
3162 : colorSchemeIndex == 0 ? QColor(0x80, 0x80, 0x80)
3163 : QColor(0xa0, 0xa0, 0xa0);
3164 QPainterStateGuard psg(p);
3165 if (hasFocus) {
3166 p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 1.5, 0, -1)));
3167 const QRectF r(rect.top(), rect.left(), rect.width(), rect.height() - 1);
3168 drawRoundedRect(p, r, QPen(underlineCol, 2), Qt::NoBrush);
3169 } else {
3170 p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 0.5, 0, -1)));
3171 drawRoundedRect(p, rect, underlineCol, Qt::NoBrush);
3172 }
3173}
3174
3175QColor QWindows11Style::winUI3Color(enum WINUI3Color col) const
3176{
3177 return WINUI3Colors[colorSchemeIndex][col];
3178}
3179
3180#undef SET_IF_UNRESOLVED
3181
3182QT_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