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