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