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