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