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