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