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