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