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