// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default #include "qwindows11style_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #if QT_CONFIG(commandlinkbutton) #include #endif #include #include #include #if QT_CONFIG(mdiarea) #include #endif #include #include #if QT_CONFIG(datetimeedit) # include #endif #if QT_CONFIG(tabwidget) # include #endif #if QT_CONFIG(menubar) # include #endif #include "qdrawutil.h" #include QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; static constexpr int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners static constexpr int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners static constexpr int contentItemHMargin = 4; // margin between content items (e.g. text and icon) static constexpr int contentHMargin = 2 * 3; // margin between rounded border and content (= rounded border margin * 3) namespace StyleOptionHelper { inline bool isChecked(const QStyleOption *option) { return option->state.testAnyFlags(QStyle::State_On | QStyle::State_NoChange); } inline bool isDisabled(const QStyleOption *option) { return !option->state.testFlag(QStyle::State_Enabled); } inline bool isPressed(const QStyleOption *option) { return option->state.testFlag(QStyle::State_Sunken); } inline bool isHover(const QStyleOption *option) { return option->state.testFlag(QStyle::State_MouseOver); } inline bool isAutoRaise(const QStyleOption *option) { return option->state.testFlag(QStyle::State_AutoRaise); } enum class ControlState { Normal, Hover, Pressed, Disabled }; inline ControlState calcControlState(const QStyleOption *option) { if (isDisabled(option)) return ControlState::Disabled; if (isPressed(option)) return ControlState::Pressed; if (isHover(option)) return ControlState::Hover; return ControlState::Normal; }; } // namespace StyleOptionHelper #define AcceptMedium u"\uF78C"_s // QStringLiteral(u"\uE73C") #define Dash12 u"\uE629"_s #define CheckMark u"\uE73E"_s #define CaretLeftSolid8 u"\uEDD9"_s #define CaretRightSolid8 u"\uEDDA"_s #define CaretUpSolid8 u"\uEDDB"_s #define CaretDownSolid8 u"\uEDDC"_s #define ChevronDown u"\uE70D"_s #define ChevronUp u"\uE70E"_s #define ChevronDownMed u"\uE972"_s #define ChevronLeftMed u"\uE973"_s #define ChevronRightMed u"\uE974"_s #define ChevronUpSmall u"\uE96D"_s #define ChevronDownSmall u"\uE96E"_s #define ChromeMinimize u"\uE921"_s #define ChromeMaximize u"\uE922"_s #define ChromeRestore u"\uE923"_s #define ChromeClose u"\uE8BB"_s #define More u"\uE712"_s #define Help u"\uE897"_s template static inline void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush) { p->setPen(pen); p->setBrush(brush); p->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); } static constexpr int percentToAlpha(double percent) { return qRound(percent * 255. / 100.); } static constexpr std::array WINUI3ColorsLight { QColor(0x00,0x00,0x00,percentToAlpha(3.73)), // subtleHighlightColor (fillSubtleSecondary) QColor(0x00,0x00,0x00,percentToAlpha(2.41)), // subtlePressedColor (fillSubtleTertiary) QColor(0x00,0x00,0x00,0x0F), //frameColorLight QColor(0x00,0x00,0x00,percentToAlpha(60.63)), //frameColorStrong QColor(0x00,0x00,0x00,percentToAlpha(21.69)), //frameColorStrongDisabled QColor(0x00,0x00,0x00,0x72), //controlStrongFill QColor(0x00,0x00,0x00,0x29), //controlStrokeSecondary QColor(0x00,0x00,0x00,0x14), //controlStrokePrimary QColor(0xFF,0xFF,0xFF,0xFF), //menuPanelFill QColor(0x00,0x00,0x00,0x66), //controlStrokeOnAccentSecondary QColor(0xFF,0xFF,0xFF,0xFF), //controlFillSolid QColor(0x75,0x75,0x75,0x66), //surfaceStroke QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameInnerStroke QColor(0x00,0x00,0x00,0xFF), //focusFrameOuterStroke QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // fillControlDefault QColor(0xF9,0xF9,0xF9,percentToAlpha(50)), // fillControlSecondary QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlTertiary QColor(0xF9,0xF9,0xF9,percentToAlpha(30)), // fillControlDisabled QColor(0x00,0x00,0x00,percentToAlpha(2.41)), // fillControlAltSecondary QColor(0x00,0x00,0x00,percentToAlpha(5.78)), // fillControlAltTertiary QColor(0x00,0x00,0x00,percentToAlpha(9.24)), // fillControlAltQuarternary QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // fillControlAltDisabled QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary QColor(0x00,0x00,0x00,percentToAlpha(21.69)), // fillAccentDisabled QColor(0x00,0x00,0x00,percentToAlpha(89.56)), // textPrimary QColor(0x00,0x00,0x00,percentToAlpha(60.63)), // textSecondary QColor(0x00,0x00,0x00,percentToAlpha(36.14)), // textDisabled QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentPrimary QColor(0xFF,0xFF,0xFF,percentToAlpha(70)), // textOnAccentSecondary QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textOnAccentDisabled QColor(0x00,0x00,0x00,percentToAlpha(8.03)), // dividerStrokeDefault }; static constexpr std::array WINUI3ColorsDark { QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // subtleHighlightColor (fillSubtleSecondary) QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // subtlePressedColor (fillSubtleTertiary) QColor(0xFF,0xFF,0xFF,0x12), //frameColorLight QColor(0xFF,0xFF,0xFF,percentToAlpha(60.47)), //frameColorStrong QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), //frameColorStrongDisabled QColor(0xFF,0xFF,0xFF,0x8B), //controlStrongFill QColor(0xFF,0xFF,0xFF,0x18), //controlStrokeSecondary QColor(0xFF,0xFF,0xFF,0x12), //controlStrokePrimary QColor(0x0F,0x0F,0x0F,0xFF), //menuPanelFill QColor(0xFF,0xFF,0xFF,0x14), //controlStrokeOnAccentSecondary QColor(0x45,0x45,0x45,0xFF), //controlFillSolid QColor(0x75,0x75,0x75,0x66), //surfaceStroke QColor(0x00,0x00,0x00,0xFF), //focusFrameInnerStroke QColor(0xFF,0xFF,0xFF,0xFF), //focusFrameOuterStroke QColor(0xFF,0xFF,0xFF,percentToAlpha(6.05)), // fillControlDefault QColor(0xFF,0xFF,0xFF,percentToAlpha(8.37)), // fillControlSecondary QColor(0xFF,0xFF,0xFF,percentToAlpha(3.26)), // fillControlTertiary QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlDisabled QColor(0x00,0x00,0x00,percentToAlpha(10.0)), // fillControlAltDefault QColor(0xFF,0xFF,0xFF,percentToAlpha(4.19)), // fillControlAltSecondary QColor(0xFF,0xFF,0xFF,percentToAlpha(6.98)), // fillControlAltTertiafillCy QColor(0xFF,0xFF,0xFF,percentToAlpha(0.00)), // controlAltDisabled QColor(0x00,0x00,0x00,percentToAlpha(100)), // fillAccentDefault QColor(0x00,0x00,0x00,percentToAlpha(90)), // fillAccentSecondary QColor(0x00,0x00,0x00,percentToAlpha(80)), // fillAccentTertiary QColor(0xFF,0xFF,0xFF,percentToAlpha(15.81)), // fillAccentDisabled QColor(0xFF,0xFF,0xFF,percentToAlpha(100)), // textPrimary QColor(0xFF,0xFF,0xFF,percentToAlpha(78.6)), // textSecondary QColor(0xFF,0xFF,0xFF,percentToAlpha(36.28)), // textDisabled QColor(0x00,0x00,0x00,percentToAlpha(100)), // textOnAccentPrimary QColor(0x00,0x00,0x00,percentToAlpha(70)), // textOnAccentSecondary QColor(0xFF,0xFF,0xFF,percentToAlpha(53.02)), // textOnAccentDisabled QColor(0xFF,0xFF,0xFF,percentToAlpha(8.37)), // dividerStrokeDefault }; static constexpr std::array, 2> WINUI3Colors { WINUI3ColorsLight, WINUI3ColorsDark }; // Color of close Button in Titlebar (default + hover) static constexpr QColor shellCaptionCloseFillColorPrimary(0xC4,0x2B,0x1C,0xFF); static constexpr QColor shellCaptionCloseTextFillColorPrimary(0xFF,0xFF,0xFF,0xFF); // Color of close Button in Titlebar (pressed + disabled) static constexpr QColor shellCaptionCloseFillColorSecondary(0xC4,0x2B,0x1C,0xE6); static constexpr QColor shellCaptionCloseTextFillColorSecondary(0xFF,0xFF,0xFF,0xB3); #if QT_CONFIG(toolbutton) static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton, const QRect &rect, QPainter *painter, const QWidget *widget = nullptr) { QStyle::PrimitiveElement pe; switch (toolbutton->arrowType) { case Qt::LeftArrow: pe = QStyle::PE_IndicatorArrowLeft; break; case Qt::RightArrow: pe = QStyle::PE_IndicatorArrowRight; break; case Qt::UpArrow: pe = QStyle::PE_IndicatorArrowUp; break; case Qt::DownArrow: pe = QStyle::PE_IndicatorArrowDown; break; default: return; } QStyleOption arrowOpt = *toolbutton; arrowOpt.rect = rect; style->drawPrimitive(pe, &arrowOpt, painter, widget); } #endif // QT_CONFIG(toolbutton) static qreal radioButtonInnerRadius(int state) { qreal radius = 7.0; if (state & QStyle::State_Sunken) radius = 4.0f; else if (state & QStyle::State_MouseOver && !(state & QStyle::State_On)) radius = 7.0f; else if (state & QStyle::State_MouseOver && (state & QStyle::State_On)) radius = 5.0f; else if (state & QStyle::State_On) radius = 4.0f; return radius; } static qreal sliderInnerRadius(QStyle::State state, bool insideHandle) { if (state & QStyle::State_Sunken) return 0.29; else if (insideHandle) return 0.71; return 0.43; } /*! \class QWindows11Style \brief The QWindows11Style class provides a look and feel suitable for applications on Microsoft Windows 11. \since 6.6 \ingroup appearance \inmodule QtWidgets \internal \warning This style is only available on the Windows 11 platform and above. \sa QWindows11Style QWindowsVistaStyle, QMacStyle, QFusionStyle */ /*! Constructs a QWindows11Style object. */ QWindows11Style::QWindows11Style() : QWindows11Style(*new QWindows11StylePrivate) { } /*! \internal Constructs a QWindows11Style object. */ QWindows11Style::QWindows11Style(QWindows11StylePrivate &dd) : QWindowsVistaStyle(dd) { Q_D(QWindows11Style); d->assetFont = QFont("Segoe Fluent Icons"); d->assetFont.setStyleStrategy(QFont::NoFontMerging); highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown; colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1; } /*! Destructor. */ QWindows11Style::~QWindows11Style() = default; /*! \internal see drawPrimitive for comments on the animation support */ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const { QWindows11StylePrivate *d = const_cast(d_func()); const auto drawTitleBarButton = [&](ComplexControl control, SubControl sc, const QString &str) { using namespace StyleOptionHelper; const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget); if (buttonRect.isValid()) { const bool hover = option->activeSubControls == sc && isHover(option); if (hover) painter->fillRect(buttonRect, winUI3Color(subtleHighlightColor)); painter->setPen(option->palette.color(QPalette::WindowText)); painter->drawText(buttonRect, Qt::AlignCenter, str); } }; const auto drawTitleBarCloseButton = [&](ComplexControl control, SubControl sc, const QString &str) { using namespace StyleOptionHelper; const QRect buttonRect = proxy()->subControlRect(control, option, sc, widget); if (buttonRect.isValid()) { const auto state = (option->activeSubControls == sc) ? calcControlState(option) : ControlState::Normal; QPen pen; switch (state) { case ControlState::Hover: painter->fillRect(buttonRect, shellCaptionCloseFillColorPrimary); pen = shellCaptionCloseTextFillColorPrimary; break; case ControlState::Pressed: painter->fillRect(buttonRect, shellCaptionCloseFillColorSecondary); pen = shellCaptionCloseTextFillColorSecondary; break; case ControlState::Disabled: case ControlState::Normal: pen = option->palette.color(QPalette::WindowText); break; } painter->setPen(pen); painter->drawText(buttonRect, Qt::AlignCenter, str); } }; State state = option->state; SubControls sub = option->subControls; State flags = option->state; if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) flags |= State_MouseOver; painter->save(); painter->setRenderHint(QPainter::Antialiasing); if (d->transitionsEnabled() && option->styleObject) { if (control == CC_Slider) { if (const auto *slider = qstyleoption_cast(option)) { QObject *styleObject = option->styleObject; // Can be widget or qquickitem QRectF thumbRect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); const qreal outerRadius = qMin(8.0, (slider->orientation == Qt::Horizontal ? thumbRect.height() / 2.0 : thumbRect.width() / 2.0) - 1); bool isInsideHandle = option->activeSubControls == SC_SliderHandle; bool oldIsInsideHandle = styleObject->property("_q_insidehandle").toBool(); State oldState = State(styleObject->property("_q_stylestate").toInt()); SubControls oldActiveControls = SubControls(styleObject->property("_q_stylecontrols").toInt()); QRectF oldRect = styleObject->property("_q_stylerect").toRect(); styleObject->setProperty("_q_insidehandle", isInsideHandle); styleObject->setProperty("_q_stylestate", int(state)); styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls)); styleObject->setProperty("_q_stylerect", option->rect); if (option->styleObject->property("_q_end_radius").isNull()) option->styleObject->setProperty("_q_end_radius", outerRadius * 0.43); bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken) || (oldIsInsideHandle != isInsideHandle) || (oldActiveControls != option->activeSubControls)) && state & State_Enabled); if (oldRect != option->rect) { doTransition = false; d->stopAnimation(styleObject); styleObject->setProperty("_q_inner_radius", outerRadius * 0.43); } if (doTransition) { QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); t->setStartValue(styleObject->property("_q_inner_radius").toFloat()); t->setEndValue(outerRadius * sliderInnerRadius(state, isInsideHandle)); styleObject->setProperty("_q_end_radius", t->endValue()); t->setStartTime(d->animationTime()); t->setDuration(150); d->startAnimation(t); } } } } switch (control) { #if QT_CONFIG(spinbox) case CC_SpinBox: if (const QStyleOptionSpinBox *sb = qstyleoption_cast(option)) { QCachedPainter cp(painter, QLatin1StringView("win11_spinbox") % HexString(colorSchemeIndex), sb, sb->rect.size()); if (cp.needsPainting()) { const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); drawRoundedRect(cp.painter(), frameRect, Qt::NoPen, option->palette.brush(QPalette::Base)); if (sb->frame && (sub & SC_SpinBoxFrame)) drawLineEditFrame(cp.painter(), frameRect, option); const bool isMouseOver = state & State_MouseOver; const bool hasFocus = state & State_HasFocus; if (isMouseOver && !hasFocus && !highContrastTheme) drawRoundedRect(cp.painter(), frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor)); const auto drawUpDown = [&](QStyle::SubControl sc) { const bool isUp = sc == SC_SpinBoxUp; const QRect rect = proxy()->subControlRect(CC_SpinBox, option, sc, widget); if (sb->activeSubControls & sc) drawRoundedRect(cp.painter(), rect.adjusted(1, 1, -1, -2), Qt::NoPen, winUI3Color(subtleHighlightColor)); cp->setFont(d->assetFont); cp->setPen(sb->palette.buttonText().color()); cp->setBrush(Qt::NoBrush); cp->drawText(rect, Qt::AlignCenter, isUp ? ChevronUp : ChevronDown); }; if (sub & SC_SpinBoxUp) drawUpDown(SC_SpinBoxUp); if (sub & SC_SpinBoxDown) drawUpDown(SC_SpinBoxDown); if (state & State_KeyboardFocusChange && state & State_HasFocus) { QStyleOptionFocusRect fropt; fropt.QStyleOption::operator=(*option); proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, cp.painter(), widget); } } } break; #endif // QT_CONFIG(spinbox) #if QT_CONFIG(slider) case CC_Slider: if (const auto *slider = qstyleoption_cast(option)) { const auto &slrect = slider->rect; const bool isHorizontal = slider->orientation == Qt::Horizontal; const QRectF handleRect(proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget)); const QPointF handleCenter(handleRect.center()); if (sub & SC_SliderGroove) { QRectF rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); QRectF leftRect; QRectF rightRect; if (isHorizontal) { rect = QRectF(rect.left() + 2, rect.center().y() - 2, rect.width() - 2, 4); leftRect = QRectF(rect.left(), rect.top(), handleCenter.x() - rect.left(), rect.height()); rightRect = QRectF(handleCenter.x(), rect.top(), rect.width() - handleCenter.x(), rect.height()); } else { rect = QRect(rect.center().x() - 2, rect.top() + 2, 4, rect.height() - 2); leftRect = QRectF(rect.left(), rect.top(), rect.width(), handleCenter.y() - rect.top()); rightRect = QRectF(rect.left(), handleCenter.y(), rect.width(), rect.height() - handleCenter.y()); } if (slider->upsideDown) qSwap(leftRect, rightRect); painter->setPen(Qt::NoPen); painter->setBrush(calculateAccentColor(option)); painter->drawRoundedRect(leftRect,1,1); painter->setBrush(WINUI3Colors[colorSchemeIndex][controlStrongFill]); painter->drawRoundedRect(rightRect,1,1); } if (sub & SC_SliderTickmarks) { int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); int ticks = slider->tickPosition; int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); int interval = slider->tickInterval; if (interval <= 0) { interval = slider->singleStep; if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, available) - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, 0, available) < 3) interval = slider->pageStep; } if (!interval) interval = 1; int fudge = len / 2; painter->setPen(slider->palette.text().color()); QVarLengthArray lines; int v = slider->minimum; while (v <= slider->maximum + 1) { if (v == slider->maximum + 1 && interval == 1) break; const int v_ = qMin(v, slider->maximum); int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3; int pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, v_, available, slider->upsideDown); pos += fudge; if (isHorizontal) { if (ticks & QSlider::TicksAbove) { lines.append(QLineF(pos, tickOffset - 0.5, pos, tickOffset - tickLength - 0.5)); } if (ticks & QSlider::TicksBelow) { lines.append(QLineF(pos, tickOffset + thickness + 0.5, pos, tickOffset + thickness + tickLength + 0.5)); } } else { if (ticks & QSlider::TicksAbove) { lines.append(QLineF(tickOffset - 0.5, pos, tickOffset - tickLength - 0.5, pos)); } if (ticks & QSlider::TicksBelow) { lines.append(QLineF(tickOffset + thickness + 0.5, pos, tickOffset + thickness + tickLength + 0.5, pos)); } } // in the case where maximum is max int int nextInterval = v + interval; if (nextInterval < v) break; v = nextInterval; } if (!lines.isEmpty()) { painter->save(); painter->translate(slrect.topLeft()); painter->drawLines(lines.constData(), lines.size()); painter->restore(); } } if (sub & SC_SliderHandle) { const qreal outerRadius = qMin(8.0, (isHorizontal ? handleRect.height() / 2.0 : handleRect.width() / 2.0) - 1); float innerRadius = outerRadius * 0.43; if (option->styleObject) { const QNumberStyleAnimation* animation = qobject_cast(d->animation(option->styleObject)); if (animation != nullptr) { innerRadius = animation->currentValue(); option->styleObject->setProperty("_q_inner_radius", innerRadius); } else { bool isInsideHandle = option->activeSubControls == SC_SliderHandle; innerRadius = outerRadius * sliderInnerRadius(state, isInsideHandle); } } painter->setPen(Qt::NoPen); painter->setBrush(winUI3Color(controlFillSolid)); painter->drawEllipse(handleCenter, outerRadius, outerRadius); painter->setBrush(calculateAccentColor(option)); painter->drawEllipse(handleCenter, innerRadius, innerRadius); painter->setPen(winUI3Color(controlStrokeSecondary)); painter->setBrush(Qt::NoBrush); painter->drawEllipse(handleCenter, outerRadius + 0.5, outerRadius + 0.5); } if (slider->state & State_HasFocus) { QStyleOptionFocusRect fropt; fropt.QStyleOption::operator=(*slider); fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); } } break; #endif #if QT_CONFIG(combobox) case CC_ComboBox: if (const QStyleOptionComboBox *combobox = qstyleoption_cast(option)) { const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); drawRoundedRect(painter, frameRect, Qt::NoPen, option->palette.brush(QPalette::Base)); if (combobox->frame) drawLineEditFrame(painter, frameRect, combobox, combobox->editable); const bool isMouseOver = state & State_MouseOver; const bool hasFocus = state & State_HasFocus; if (isMouseOver && !hasFocus && !highContrastTheme) drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor)); if (sub & SC_ComboBoxArrow) { QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget).adjusted(4, 0, -4, 1); painter->setFont(d->assetFont); painter->setPen(combobox->palette.text().color()); painter->drawText(rect, Qt::AlignCenter, ChevronDownMed); } if (state & State_HasFocus) { drawPrimitive(PE_FrameFocusRect, option, painter, widget); } if (state & State_KeyboardFocusChange && state & State_HasFocus) { QStyleOptionFocusRect fropt; fropt.QStyleOption::operator=(*option); proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); } } break; #endif // QT_CONFIG(combobox) case CC_ScrollBar: if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(option)) { QCachedPainter cp(painter, QLatin1StringView("win11_scrollbar") % HexString(colorSchemeIndex) % HexString(scrollbar->minimum) % HexString(scrollbar->maximum) % HexString(scrollbar->sliderPosition), scrollbar, scrollbar->rect.size()); if (cp.needsPainting()) { const bool vertical = scrollbar->orientation == Qt::Vertical; const bool horizontal = scrollbar->orientation == Qt::Horizontal; const bool isMouseOver = state & State_MouseOver; const bool isRtl = option->direction == Qt::RightToLeft; if (isMouseOver) { QRectF rect = scrollbar->rect; const QPointF center = rect.center(); if (vertical && rect.width() > 24) { rect.marginsRemoved(QMargins(0, 2, 2, 2)); rect.setWidth(rect.width() / 2); } else if (horizontal && rect.height() > 24) { rect.marginsRemoved(QMargins(2, 0, 2, 2)); rect.setHeight(rect.height() / 2); } rect.moveCenter(center); cp->setBrush(scrollbar->palette.base()); cp->setPen(Qt::NoPen); cp->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius); rect = rect.marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5)); cp->setBrush(Qt::NoBrush); cp->setPen(WINUI3Colors[colorSchemeIndex][frameColorLight]); cp->drawRoundedRect(rect, topLevelRoundingRadius + 0.5, topLevelRoundingRadius + 0.5); } if (sub & SC_ScrollBarSlider) { QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); const QPointF center = rect.center(); if (vertical) rect.setWidth(isMouseOver ? rect.width() / 2 : 1); else rect.setHeight(isMouseOver ? rect.height() / 2 : 1); rect.moveCenter(center); cp->setBrush(Qt::gray); cp->setPen(Qt::NoPen); cp->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); } if (sub & SC_ScrollBarAddLine) { if (isMouseOver) { const QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); QFont f = QFont(d->assetFont); f.setPointSize(6); cp->setFont(f); cp->setPen(Qt::gray); const auto str = vertical ? CaretDownSolid8 : (isRtl ? CaretLeftSolid8 : CaretRightSolid8); cp->drawText(rect, Qt::AlignCenter, str); } } if (sub & SC_ScrollBarSubLine) { if (isMouseOver) { const QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); QFont f = QFont(d->assetFont); f.setPointSize(6); cp->setFont(f); cp->setPen(Qt::gray); const auto str = vertical ? CaretUpSolid8 : (isRtl ? CaretRightSolid8 : CaretLeftSolid8); cp->drawText(rect, Qt::AlignCenter, str); } } } } break; case CC_MdiControls:{ QFont buttonFont = QFont(d->assetFont); buttonFont.setPointSize(8); painter->setFont(buttonFont); drawTitleBarCloseButton(CC_MdiControls, SC_MdiCloseButton, ChromeClose); drawTitleBarButton(CC_MdiControls, SC_MdiNormalButton, ChromeRestore); drawTitleBarButton(CC_MdiControls, SC_MdiMinButton, ChromeMinimize); } break; case CC_TitleBar: if (const auto* titlebar = qstyleoption_cast(option)) { painter->setPen(Qt::NoPen); painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]); painter->setBrush(titlebar->palette.button()); painter->drawRect(titlebar->rect); // draw title QRect textRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel, widget); QColor textColor = titlebar->palette.color(titlebar->titleBarState & Qt::WindowActive ? QPalette::Active : QPalette::Disabled,QPalette::WindowText); painter->setPen(textColor); // Note workspace also does elliding but it does not use the correct font QString title = painter->fontMetrics().elidedText(titlebar->text, Qt::ElideRight, textRect.width() - 14); painter->drawText(textRect.adjusted(1, 1, -1, -1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); QFont buttonFont = QFont(d->assetFont); buttonFont.setPointSize(8); painter->setFont(buttonFont); auto shouldDrawButton = [titlebar](SubControl sc, Qt::WindowType flag) { return (titlebar->subControls & sc) && (titlebar->titleBarFlags & flag); }; // min button if (shouldDrawButton(SC_TitleBarMinButton, Qt::WindowMinimizeButtonHint) && !(titlebar->titleBarState & Qt::WindowMinimized)) { drawTitleBarButton(CC_TitleBar, SC_TitleBarMinButton, ChromeMinimize); } // max button if (shouldDrawButton(SC_TitleBarMaxButton, Qt::WindowMaximizeButtonHint) && !(titlebar->titleBarState & Qt::WindowMaximized)) { drawTitleBarButton(CC_TitleBar, SC_TitleBarMaxButton, ChromeMaximize); } // close button if (shouldDrawButton(SC_TitleBarCloseButton, Qt::WindowSystemMenuHint)) drawTitleBarCloseButton(CC_TitleBar, SC_TitleBarCloseButton, ChromeClose); // normalize button if ((titlebar->subControls & SC_TitleBarNormalButton) && (((titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) && (titlebar->titleBarState & Qt::WindowMinimized)) || ((titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) && (titlebar->titleBarState & Qt::WindowMaximized)))) { drawTitleBarButton(CC_TitleBar, SC_TitleBarNormalButton, ChromeRestore); } // context help button if (shouldDrawButton(SC_TitleBarContextHelpButton, Qt::WindowContextHelpButtonHint)) drawTitleBarButton(CC_TitleBar, SC_TitleBarContextHelpButton, Help); // shade button if (shouldDrawButton(SC_TitleBarShadeButton, Qt::WindowShadeButtonHint)) drawTitleBarButton(CC_TitleBar, SC_TitleBarShadeButton, ChevronUpSmall); // unshade button if (shouldDrawButton(SC_TitleBarUnshadeButton, Qt::WindowShadeButtonHint)) drawTitleBarButton(CC_TitleBar, SC_TitleBarUnshadeButton, ChevronDownSmall); // window icon for system menu if (shouldDrawButton(SC_TitleBarSysMenu, Qt::WindowSystemMenuHint)) { const QRect iconRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarSysMenu, widget); if (iconRect.isValid()) { if (!titlebar->icon.isNull()) { titlebar->icon.paint(painter, iconRect); } else { QStyleOption tool = *titlebar; const auto extent = proxy()->pixelMetric(PM_SmallIconSize, &tool, widget); const auto dpr = QStyleHelper::getDpr(widget); const auto icon = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget); const auto pm = icon.pixmap(QSize(extent, extent), dpr); proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm); } } } } break; default: QWindowsVistaStyle::drawComplexControl(control, option, painter, widget); } painter->restore(); } void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { QWindows11StylePrivate *d = const_cast(d_func()); int state = option->state; painter->save(); painter->setRenderHint(QPainter::Antialiasing); if (d->transitionsEnabled() && option->styleObject && (element == PE_IndicatorCheckBox || element == PE_IndicatorRadioButton)) { QObject *styleObject = option->styleObject; // Can be widget or qquickitem int oldState = styleObject->property("_q_stylestate").toInt(); styleObject->setProperty("_q_stylestate", int(option->state)); styleObject->setProperty("_q_stylerect", option->rect); bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken) || ((state & State_MouseOver) != (oldState & State_MouseOver)) || (state & State_On) != (oldState & State_On)) && state & State_Enabled); if (doTransition) { if (element == PE_IndicatorRadioButton) { QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); t->setStartValue(styleObject->property("_q_inner_radius").toFloat()); t->setEndValue(radioButtonInnerRadius(state)); styleObject->setProperty("_q_end_radius", t->endValue()); t->setStartTime(d->animationTime()); t->setDuration(150); d->startAnimation(t); } else if (element == PE_IndicatorCheckBox) { if ((oldState & State_Off && state & State_On) || (oldState & State_NoChange && state & State_On)) { QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); t->setStartValue(0.0f); t->setEndValue(1.0f); t->setStartTime(d->animationTime()); t->setDuration(150); d->startAnimation(t); } } } } switch (element) { case PE_FrameFocusRect: { if (const QStyleOptionFocusRect *fropt = qstyleoption_cast(option)) { if (!(fropt->state & State_KeyboardFocusChange)) break; QRectF focusRect = option->rect; focusRect = focusRect.marginsRemoved(QMarginsF(1.5,1.5,1.5,1.5)); painter->setPen(winUI3Color(focusFrameInnerStroke)); painter->drawRoundedRect(focusRect,4,4); focusRect = focusRect.marginsAdded(QMarginsF(1.0,1.0,1.0,1.0)); painter->setPen(QPen(winUI3Color(focusFrameOuterStroke),1)); painter->drawRoundedRect(focusRect,4,4); } break; } case PE_PanelTipLabel: { const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5)); const auto pen = highContrastTheme ? option->palette.buttonText().color() : winUI3Color(frameColorLight); drawRoundedRect(painter, rect, pen, option->palette.toolTipBase()); break; } case PE_FrameTabWidget: #if QT_CONFIG(tabwidget) if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast(option)) { const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(0.5, 0.5, 0.5, 0.5)); const auto pen = highContrastTheme ? frame->palette.buttonText().color() : winUI3Color(frameColorLight); drawRoundedRect(painter, rect, pen, frame->palette.base()); } #endif // QT_CONFIG(tabwidget) break; case PE_FrameGroupBox: if (const QStyleOptionFrame *frame = qstyleoption_cast(option)) { const auto pen = highContrastTheme ? frame->palette.buttonText().color() : winUI3Color(frameColorStrong); if (frame->features & QStyleOptionFrame::Flat) { painter->setBrush(Qt::NoBrush); painter->setPen(pen); const QRect &fr = frame->rect; QPoint p1(fr.x(), fr.y() + 1); QPoint p2(fr.x() + fr.width(), p1.y()); painter->drawLine(p1, p2); } else { const auto frameRect = QRectF(frame->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); drawRoundedRect(painter, frameRect, pen, Qt::NoBrush); } } break; case PE_IndicatorHeaderArrow: if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { QFont f(d->assetFont); f.setPointSize(6); painter->setFont(f); painter->setPen(header->palette.text().color()); QRectF rect = option->rect; if (header->sortIndicator & QStyleOptionHeader::SortUp) { painter->drawText(rect, Qt::AlignCenter, ChevronUpSmall); } else if (header->sortIndicator & QStyleOptionHeader::SortDown) { painter->drawText(rect, Qt::AlignCenter, ChevronDownSmall); } } break; case PE_IndicatorCheckBox: { const bool isOn = option->state & State_On; const bool isPartial = option->state & State_NoChange; const QRectF rect = option->rect; const QPointF center = rect.center(); drawRoundedRect(painter, option->rect, borderPenControlAlt(option), controlFillBrush(option, ControlType::ControlAlt)); if (isOn) { painter->setFont(d->assetFont); painter->setPen(controlTextColor(option, QPalette::Window)); qreal clipWidth = 1.0; QFontMetrics fm(d->assetFont); QRectF clipRect = fm.boundingRect(AcceptMedium); if (d->transitionsEnabled() && option->styleObject) { QNumberStyleAnimation *animation = qobject_cast( d->animation(option->styleObject)); if (animation) clipWidth = animation->currentValue(); } clipRect.moveCenter(center); clipRect.setLeft(rect.x() + (rect.width() - clipRect.width()) / 2.0 + 0.5); clipRect.setWidth(clipWidth * clipRect.width()); painter->drawText(clipRect, Qt::AlignVCenter | Qt::AlignLeft, AcceptMedium); } else if (isPartial) { QFont f(d->assetFont); f.setPointSize(6); painter->setFont(f); painter->setPen(controlTextColor(option, QPalette::Window)); painter->drawText(rect, Qt::AlignCenter, Dash12); } } break; case PE_IndicatorBranch: { if (option->state & State_Children) { const bool isReverse = option->direction == Qt::RightToLeft; const bool isOpen = option->state & QStyle::State_Open; QFont f(d->assetFont); f.setPointSize(6); painter->setFont(f); painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled, QPalette::WindowText)); const auto str = isOpen ? ChevronDownMed : (isReverse ? ChevronLeftMed : ChevronRightMed); painter->drawText(option->rect, Qt::AlignCenter, str); } } break; case PE_IndicatorRadioButton: { const bool isOn = option->state & State_On; qreal innerRadius = radioButtonInnerRadius(state); if (d->transitionsEnabled() && option->styleObject) { if (option->styleObject->property("_q_end_radius").isNull()) option->styleObject->setProperty("_q_end_radius", innerRadius); QNumberStyleAnimation *animation = qobject_cast(d->animation(option->styleObject)); innerRadius = animation ? animation->currentValue() : option->styleObject->property("_q_end_radius").toFloat(); option->styleObject->setProperty("_q_inner_radius", innerRadius); } const QRectF rect = option->rect; const QPointF center = rect.center(); painter->setPen(borderPenControlAlt(option)); painter->setBrush(controlFillBrush(option, ControlType::ControlAlt)); if (isOn) { QPainterPath path; path.addEllipse(center, 7.5, 7.5); path.addEllipse(center, innerRadius, innerRadius); painter->drawPath(path); // Text On Accent/Primary painter->setBrush(option->palette.window().color()); painter->drawEllipse(center, innerRadius, innerRadius); } else { painter->drawEllipse(center, 7.5, 7.5); } } break; case PE_PanelButtonTool: case PE_PanelButtonBevel:{ const bool isEnabled = state & QStyle::State_Enabled; const bool isMouseOver = state & QStyle::State_MouseOver; const bool isRaised = state & QStyle::State_Raised; const QRectF rect = option->rect.marginsRemoved(QMargins(2,2,2,2)); if (element == PE_PanelButtonTool && ((!isMouseOver && !isRaised) || !isEnabled)) painter->setPen(Qt::NoPen); else painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary]); painter->setBrush(controlFillBrush(option, ControlType::Control)); painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); if (isRaised) { const qreal sublineOffset = secondLevelRoundingRadius - 0.5; painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]); painter->drawLine(rect.bottomLeft() + QPointF(sublineOffset, 0.5), rect.bottomRight() + QPointF(-sublineOffset, 0.5)); } } break; case PE_FrameDefaultButton: painter->setPen(option->palette.accent().color()); painter->setBrush(Qt::NoBrush); painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius); break; case PE_FrameMenu: break; case PE_PanelMenu: { const QRect rect = option->rect.marginsRemoved(QMargins(2, 2, 2, 2)); painter->setPen(highContrastTheme ? QPen(option->palette.windowText().color(), 2) : winUI3Color(frameColorLight)); painter->setBrush(winUI3Color(menuPanelFill)); painter->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius); break; } case PE_PanelLineEdit: if (const auto *panel = qstyleoption_cast(option)) { const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); drawRoundedRect(painter, frameRect, Qt::NoPen, option->palette.brush(QPalette::Base)); if (panel->lineWidth > 0) proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); const bool isMouseOver = state & State_MouseOver; const bool hasFocus = state & State_HasFocus; if (isMouseOver && !hasFocus && !highContrastTheme) drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor)); } break; case PE_FrameLineEdit: { const auto frameRect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); drawLineEditFrame(painter, frameRect, option); if (state & State_KeyboardFocusChange && state & State_HasFocus) { QStyleOptionFocusRect fropt; fropt.QStyleOption::operator=(*option); proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); } break; } case PE_Frame: { if (const auto *frame = qstyleoption_cast(option)) { const auto rect = QRectF(option->rect).marginsRemoved(QMarginsF(1.5, 1.5, 1.5, 1.5)); if (qobject_cast(widget)) { QPen pen; if (highContrastTheme) pen = QPen(option->palette.windowText().color(), 2); else pen = Qt::NoPen; drawRoundedRect(painter, rect, pen, WINUI3Colors[colorSchemeIndex][menuPanelFill]); } else drawRoundedRect(painter, rect, Qt::NoPen, option->palette.brush(QPalette::Base)); if (frame->frameShape == QFrame::NoFrame) break; drawLineEditFrame(painter, rect, option, qobject_cast(widget) != nullptr); } break; } case PE_PanelItemViewItem: if (const QStyleOptionViewItem *vopt = qstyleoption_cast(option)) { if (vopt->backgroundBrush.style() != Qt::NoBrush) { QPainterStateGuard psg(painter); painter->setBrushOrigin(vopt->rect.topLeft()); painter->fillRect(vopt->rect, vopt->backgroundBrush); } } break; case PE_PanelItemViewRow: if (const QStyleOptionViewItem *vopt = qstyleoption_cast(option)) { // this is only called from a QTreeView to paint // - the tree branch decoration (incl. selected/hovered or not) // - the (alternate) background of the item in always unselected state const QRect &rect = vopt->rect; const bool isRtl = option->direction == Qt::RightToLeft; if (rect.width() <= 0) break; painter->setPen(Qt::NoPen); if (vopt->features & QStyleOptionViewItem::Alternate) painter->setBrush(vopt->palette.alternateBase()); else painter->setBrush(vopt->palette.base()); painter->drawRect(rect); const bool isTreeDecoration = vopt->features.testFlag( QStyleOptionViewItem::IsDecorationForRootColumn); if (isTreeDecoration && vopt->state.testAnyFlags(State_Selected | State_MouseOver) && vopt->showDecorationSelected) { const bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || vopt->viewItemPosition == QStyleOptionViewItem::Invalid; bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End; if (onlyOne) isFirst = true; if (isRtl) { isFirst = !isFirst; isLast = !isLast; } const QAbstractItemView *view = qobject_cast(widget); painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); painter->setPen(Qt::NoPen); if (isFirst) { painter->save(); painter->setClipRect(rect); painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)), secondLevelRoundingRadius, secondLevelRoundingRadius); painter->restore(); } else if (isLast) { painter->save(); painter->setClipRect(rect); painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)), secondLevelRoundingRadius, secondLevelRoundingRadius); painter->restore(); } else { painter->drawRect(vopt->rect.marginsRemoved(QMargins(0, 2, 0, 2))); } } } break; case QStyle::PE_Widget: { if (widget && widget->palette().isBrushSet(QPalette::Active, widget->backgroundRole())) { const QBrush bg = widget->palette().brush(widget->backgroundRole()); auto wp = QWidgetPrivate::get(widget); QPainterStateGuard psg(painter); wp->updateBrushOrigin(painter, bg); painter->fillRect(option->rect, bg); } break; } case QStyle::PE_FrameWindow: if (const auto *frm = qstyleoption_cast(option)) { QRectF rect= option->rect; int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); QRectF bottomLeftCorner = QRectF(rect.left() + 1.0, rect.bottom() - 1.0 - secondLevelRoundingRadius, secondLevelRoundingRadius, secondLevelRoundingRadius); QRectF bottomRightCorner = QRectF(rect.right() - 1.0 - secondLevelRoundingRadius, rect.bottom() - 1.0 - secondLevelRoundingRadius, secondLevelRoundingRadius, secondLevelRoundingRadius); //Draw Mask if (widget != nullptr) { QBitmap mask(widget->width(), widget->height()); mask.clear(); QPainter maskPainter(&mask); maskPainter.setRenderHint(QPainter::Antialiasing); maskPainter.setBrush(Qt::color1); maskPainter.setPen(Qt::NoPen); maskPainter.drawRoundedRect(option->rect,secondLevelRoundingRadius,secondLevelRoundingRadius); const_cast(widget)->setMask(mask); } //Draw Window painter->setPen(QPen(frm->palette.base(), fwidth)); painter->drawLine(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom() - fwidth)); painter->drawLine(QPointF(rect.left() + fwidth, rect.bottom()), QPointF(rect.right() - fwidth, rect.bottom())); painter->drawLine(QPointF(rect.right(), rect.top()), QPointF(rect.right(), rect.bottom() - fwidth)); painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]); painter->drawLine(QPointF(rect.left() + 0.5, rect.top() + 0.5), QPointF(rect.left() + 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius)); painter->drawLine(QPointF(rect.left() + 0.5 + secondLevelRoundingRadius, rect.bottom() - 0.5), QPointF(rect.right() - 0.5 - secondLevelRoundingRadius, rect.bottom() - 0.5)); painter->drawLine(QPointF(rect.right() - 0.5, rect.top() + 1.5), QPointF(rect.right() - 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius)); painter->setPen(Qt::NoPen); painter->setBrush(frm->palette.base()); painter->drawPie(bottomRightCorner.marginsAdded(QMarginsF(2.5,2.5,0.0,0.0)), 270 * 16,90 * 16); painter->drawPie(bottomLeftCorner.marginsAdded(QMarginsF(0.0,2.5,2.5,0.0)), -90 * 16,-90 * 16); painter->setPen(WINUI3Colors[colorSchemeIndex][surfaceStroke]); painter->setBrush(Qt::NoBrush); painter->drawArc(bottomRightCorner, 0 * 16,-90 * 16); painter->drawArc(bottomLeftCorner, -90 * 16,-90 * 16); } break; default: QWindowsVistaStyle::drawPrimitive(element, option, painter, widget); } painter->restore(); } /*! \internal */ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { Q_D(const QWindows11Style); State flags = option->state; painter->save(); painter->setRenderHint(QPainter::Antialiasing); switch (element) { case QStyle::CE_ComboBoxLabel: if (const QStyleOptionComboBox *cb = qstyleoption_cast(option)) { QStyleOptionComboBox newOption = *cb; newOption.rect.adjust(4,0,-4,0); QCommonStyle::drawControl(element, &newOption, painter, widget); } break; case QStyle::CE_TabBarTabShape: #if QT_CONFIG(tabbar) if (const QStyleOptionTab *tab = qstyleoption_cast(option)) { QRectF tabRect = tab->rect.marginsRemoved(QMargins(2,2,0,0)); painter->setPen(Qt::NoPen); painter->setBrush(tab->palette.base()); if (tab->state & State_MouseOver){ painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); } else if (tab->state & State_Selected) { painter->setBrush(tab->palette.base()); } else { painter->setBrush(tab->palette.window()); } painter->drawRoundedRect(tabRect,2,2); painter->setBrush(Qt::NoBrush); painter->setPen(highContrastTheme == true ? tab->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); painter->drawRoundedRect(tabRect.adjusted(0.5,0.5,-0.5,-0.5),2,2); } #endif // QT_CONFIG(tabbar) break; case CE_ToolButtonLabel: #if QT_CONFIG(toolbutton) if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast(option)) { QRect rect = toolbutton->rect; int shiftX = 0; int shiftY = 0; if (toolbutton->state & (State_Sunken | State_On)) { shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget); shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton, widget); } // Arrow type always overrules and is always shown bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow; if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty()) || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) { int alignment = Qt::AlignCenter | Qt::TextShowMnemonic; if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget)) alignment |= Qt::TextHideMnemonic; rect.translate(shiftX, shiftY); painter->setFont(toolbutton->font); const QString text = d->toolButtonElideText(toolbutton, rect, alignment); // option->state has no State_Sunken here, windowsvistastyle/CC_ToolButton removes it painter->setPen(controlTextColor(option)); proxy()->drawItemText(painter, rect, alignment, toolbutton->palette, toolbutton->state & State_Enabled, text); } else { QPixmap pm; QSize pmSize = toolbutton->iconSize; if (!toolbutton->icon.isNull()) { QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off; QIcon::Mode mode; if (!(toolbutton->state & State_Enabled)) mode = QIcon::Disabled; else if ((toolbutton->state & State_MouseOver) && (toolbutton->state & State_AutoRaise)) mode = QIcon::Active; else mode = QIcon::Normal; pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), painter->device()->devicePixelRatio(), mode, state); pmSize = pm.size() / pm.devicePixelRatio(); } if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) { painter->setFont(toolbutton->font); QRect pr = rect, tr = rect; int alignment = Qt::TextShowMnemonic; if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget)) alignment |= Qt::TextHideMnemonic; if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() tr.adjust(0, pr.height() - 1, 0, -1); pr.translate(shiftX, shiftY); if (!hasArrow) { proxy()->drawItemPixmap(painter, pr, Qt::AlignCenter, pm); } else { drawArrow(proxy(), toolbutton, pr, painter, widget); } alignment |= Qt::AlignCenter; } else { pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() tr.adjust(pr.width(), 0, 0, 0); pr.translate(shiftX, shiftY); if (!hasArrow) { proxy()->drawItemPixmap(painter, QStyle::visualRect(toolbutton->direction, rect, pr), Qt::AlignCenter, pm); } else { drawArrow(proxy(), toolbutton, pr, painter, widget); } alignment |= Qt::AlignLeft | Qt::AlignVCenter; } tr.translate(shiftX, shiftY); const QString text = d->toolButtonElideText(toolbutton, tr, alignment); painter->setPen(controlTextColor(option)); proxy()->drawItemText(painter, QStyle::visualRect(toolbutton->direction, rect, tr), alignment, toolbutton->palette, toolbutton->state & State_Enabled, text); } else { rect.translate(shiftX, shiftY); if (hasArrow) { drawArrow(proxy(), toolbutton, rect, painter, widget); } else { proxy()->drawItemPixmap(painter, rect, Qt::AlignCenter, pm); } } } } #endif // QT_CONFIG(toolbutton) break; case QStyle::CE_ShapedFrame: if (const QStyleOptionFrame *f = qstyleoption_cast(option)) { int frameShape = f->frameShape; int frameShadow = QFrame::Plain; if (f->state & QStyle::State_Sunken) frameShadow = QFrame::Sunken; else if (f->state & QStyle::State_Raised) frameShadow = QFrame::Raised; int lw = f->lineWidth; int mlw = f->midLineWidth; switch (frameShape) { case QFrame::Box: if (frameShadow == QFrame::Plain) qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw); else qDrawShadeRect(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw); break; case QFrame::Panel: if (frameShadow == QFrame::Plain) qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw); else qDrawShadePanel(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw); break; default: QWindowsVistaStyle::drawControl(element, option, painter, widget); } } break; #if QT_CONFIG(progressbar) case CE_ProgressBarGroove: if (const auto baropt = qstyleoption_cast(option)) { QRect rect = option->rect; QPointF center = rect.center(); if (baropt->state & QStyle::State_Horizontal) { rect.setHeight(1); rect.moveTop(center.y()); } else { rect.setWidth(1); rect.moveLeft(center.x()); } painter->setPen(Qt::NoPen); painter->setBrush(Qt::gray); painter->drawRect(rect); } break; case CE_ProgressBarContents: if (const auto baropt = qstyleoption_cast(option)) { QPainterStateGuard psg(painter); QRectF rect = option->rect; painter->translate(rect.topLeft()); rect.translate(-rect.topLeft()); constexpr qreal progressBarThickness = 3; constexpr qreal progressBarHalfThickness = progressBarThickness / 2.0; const auto isIndeterminate = baropt->maximum == 0 && baropt->minimum == 0; const auto orientation = (baropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical; const auto inverted = baropt->invertedAppearance; const auto reverse = (baropt->direction == Qt::RightToLeft) ^ inverted; // If the orientation is vertical, we use a transform to rotate // the progress bar 90 degrees (counter)clockwise. This way we can use the // same rendering code for both orientations. if (orientation == Qt::Vertical) { rect = QRectF(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height QTransform m; if (inverted) { m.rotate(90); m.translate(0, -rect.height() + 1); } else { m.rotate(-90); m.translate(-rect.width(), 0); } painter->setTransform(m, true); } else if (reverse) { QTransform m = QTransform::fromScale(-1, 1); m.translate(-rect.width(), 0); painter->setTransform(m, true); } const qreal offset = (int(rect.height()) % 2 == 0) ? 0.5f : 0.0f; if (isIndeterminate) { constexpr auto loopDurationMSec = 4000; const auto elapsedTime = std::chrono::time_point_cast( std::chrono::system_clock::now()); const auto elapsed = elapsedTime.time_since_epoch().count(); const auto handleCenter = (elapsed % loopDurationMSec) / float(loopDurationMSec); const auto isLongHandle = (elapsed / loopDurationMSec) % 2 == 0; const auto lengthFactor = (isLongHandle ? 33.0f : 25.0f) / 100.0f; const auto begin = qMax(handleCenter * (1 + lengthFactor) - lengthFactor, 0.0f); const auto end = qMin(handleCenter * (1 + lengthFactor), 1.0f); const auto barBegin = begin * rect.width(); const auto barEnd = end * rect.width(); rect = QRectF(QPointF(rect.left() + barBegin, rect.top()), QPointF(rect.left() + barEnd, rect.bottom())); const_cast(widget)->update(); } else { const auto fillPercentage = (float(baropt->progress - baropt->minimum)) / (float(baropt->maximum - baropt->minimum)); rect.setWidth(rect.width() * fillPercentage); } const QPointF center = rect.center(); rect.setHeight(progressBarThickness); rect.moveTop(center.y() - progressBarHalfThickness - offset); drawRoundedRect(painter, rect, Qt::NoPen, baropt->palette.accent()); } break; case CE_ProgressBarLabel: if (const auto baropt = qstyleoption_cast(option)) { const bool vertical = !(baropt->state & QStyle::State_Horizontal); if (!vertical) { proxy()->drawItemText(painter, baropt->rect, Qt::AlignCenter | Qt::TextSingleLine, baropt->palette, baropt->state & State_Enabled, baropt->text, QPalette::Text); } } break; #endif // QT_CONFIG(progressbar) case CE_PushButtonLabel: if (const QStyleOptionButton *btn = qstyleoption_cast(option)) { using namespace StyleOptionHelper; const bool isEnabled = !isDisabled(option); QRect textRect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0)); int tf = Qt::AlignCenter | Qt::TextShowMnemonic; if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget)) tf |= Qt::TextHideMnemonic; if (!btn->icon.isNull()) { //Center both icon and text QIcon::Mode mode = isEnabled ? QIcon::Normal : QIcon::Disabled; if (mode == QIcon::Normal && btn->state & State_HasFocus) mode = QIcon::Active; QIcon::State state = isChecked(btn) ? QIcon::On : QIcon::Off; int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint() QRect iconRect = QRect(textRect.x(), textRect.y(), btn->iconSize.width(), textRect.height()); QRect vIconRect = visualRect(btn->direction, btn->rect, iconRect); textRect.setLeft(textRect.left() + iconRect.width() + iconSpacing); if (isChecked(btn) || isPressed(btn)) vIconRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, option, widget), proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget)); btn->icon.paint(painter, vIconRect, Qt::AlignCenter, mode, state); } auto vTextRect = visualRect(btn->direction, btn->rect, textRect); painter->setPen(controlTextColor(option)); proxy()->drawItemText(painter, vTextRect, tf, option->palette, isEnabled, btn->text); } break; case CE_PushButtonBevel: if (const QStyleOptionButton *btn = qstyleoption_cast(option)) { using namespace StyleOptionHelper; QRectF rect = btn->rect.marginsRemoved(QMargins(2, 2, 2, 2)); painter->setPen(Qt::NoPen); if (btn->features.testFlag(QStyleOptionButton::Flat)) { painter->setBrush(btn->palette.button()); painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); if (flags & (State_Sunken | State_On)) { painter->setBrush(WINUI3Colors[colorSchemeIndex][subtlePressedColor]); } else if (flags & State_MouseOver) { painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); } painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); } else { painter->setBrush(controlFillBrush(option, ControlType::Control)); painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); rect.adjust(0.5,0.5,-0.5,-0.5); const bool defaultButton = btn->features.testFlag(QStyleOptionButton::DefaultButton); painter->setBrush(Qt::NoBrush); painter->setPen(defaultButton ? option->palette.accent().color() : WINUI3Colors[colorSchemeIndex][controlStrokePrimary]); painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); painter->setPen(defaultButton ? WINUI3Colors[colorSchemeIndex][controlStrokeOnAccentSecondary] : WINUI3Colors[colorSchemeIndex][controlStrokeSecondary]); } if (btn->features.testFlag(QStyleOptionButton::HasMenu)) { QPainterStateGuard psg(painter); const bool isEnabled = !isDisabled(option); QRect textRect = btn->rect.marginsRemoved(QMargins(contentHMargin, 0, contentHMargin, 0)); const auto indSize = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget); const auto indRect = QRect(btn->rect.right() - indSize - contentItemHMargin, textRect.top(), indSize + contentItemHMargin, btn->rect.height()); const auto vindRect = visualRect(btn->direction, btn->rect, indRect); textRect.setWidth(textRect.width() - indSize); int fontSize = painter->font().pointSize(); QFont f(d->assetFont); f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller painter->setFont(f); QColor penColor = option->palette.color( isEnabled ? QPalette::Active : QPalette::Disabled, QPalette::Text); if (isEnabled) penColor.setAlpha(percentToAlpha(60.63)); // fillColorTextSecondary painter->setPen(penColor); painter->drawText(vindRect, Qt::AlignCenter, ChevronDownMed); } } break; case CE_MenuBarItem: if (const auto *mbi = qstyleoption_cast(option)) { using namespace StyleOptionHelper; constexpr int hPadding = 11; constexpr int topPadding = 4; constexpr int bottomPadding = 6; QStyleOptionMenuItem newMbi = *mbi; if (auto mbiV2 = qstyleoption_cast(option)) newMbi.state.setFlag(State_Sunken, mbiV2->mouseDown); newMbi.font.setPointSize(10); newMbi.palette.setColor(QPalette::ButtonText, controlTextColor(&newMbi)); if (!isDisabled(&newMbi)) { QPen pen(Qt::NoPen); QBrush brush(Qt::NoBrush); if (highContrastTheme) { pen = QPen(newMbi.palette.highlight().color(), 2); brush = newMbi.palette.window(); } else if (isPressed(&newMbi)) { brush = winUI3Color(subtlePressedColor); } else if (isHover(&newMbi)) { brush = winUI3Color(subtleHighlightColor); } if (pen != Qt::NoPen || brush != Qt::NoBrush) { const QRect rect = mbi->rect.marginsRemoved(QMargins(5, 0, 5, 0)); drawRoundedRect(painter, rect, pen, brush); } } newMbi.rect.adjust(hPadding,topPadding,-hPadding,-bottomPadding); painter->setFont(newMbi.font); QCommonStyle::drawControl(element, &newMbi, painter, widget); } break; #if QT_CONFIG(menu) case CE_MenuEmptyArea: break; case CE_MenuItem: if (const auto *menuitem = qstyleoption_cast(option)) { const auto visualMenuRect = [&](const QRect &rect) { return visualRect(option->direction, menuitem->rect, rect); }; bool dis = !(menuitem->state & State_Enabled); bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable ? menuitem->checked : false; bool act = menuitem->state & State_Selected; const QRect rect = menuitem->rect.marginsRemoved(QMargins(2,2,2,2)); if (act && dis == false) { drawRoundedRect(painter, rect, Qt::NoPen, highContrastTheme ? menuitem->palette.brush(QPalette::Highlight) : QBrush(winUI3Color(subtleHighlightColor))); } if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { constexpr int yoff = 1; painter->setPen(highContrastTheme ? menuitem->palette.buttonText().color() : winUI3Color(dividerStrokeDefault)); painter->drawLine(menuitem->rect.topLeft() + QPoint(0, yoff), menuitem->rect.topRight() + QPoint(0, yoff)); break; } int xOffset = contentHMargin; // WinUI3 draws, in contrast to former windows styles, the checkmark and icon separately const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget); if (checked) { QRect vRect(visualMenuRect(QRect(rect.x() + xOffset, rect.y(), checkMarkWidth, rect.height()))); QPainterStateGuard psg(painter); painter->setFont(d->assetFont); painter->setPen(option->palette.text().color()); const auto textToDraw = QStringLiteral(u"\uE73E"); painter->drawText(vRect, Qt::AlignCenter, textToDraw); } if (menuitem->menuHasCheckableItems) xOffset += checkMarkWidth + contentItemHMargin; if (!menuitem->icon.isNull()) { // 4 is added to maxIconWidth in qmenu.cpp to PM_SmallIconSize QRect vRect(visualMenuRect(QRect(rect.x() + xOffset, rect.y(), menuitem->maxIconWidth - 4, rect.height()))); QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; if (act && !dis) mode = QIcon::Active; const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget); QRect pmr(QPoint(0, 0), QSize(size, size)); pmr.moveCenter(vRect.center()); menuitem->icon.paint(painter, pmr, Qt::AlignCenter, mode, checked ? QIcon::On : QIcon::Off); } if (menuitem->maxIconWidth > 0) xOffset += menuitem->maxIconWidth - 4 + contentItemHMargin; QStringView s(menuitem->text); if (!s.isEmpty()) { // draw text QPoint tl(rect.left() + xOffset, rect.top()); QPoint br(rect.right() - menuitem->reservedShortcutWidth - contentHMargin, rect.bottom()); QRect textRect(tl, br); QRect vRect(visualMenuRect(textRect)); qsizetype t = s.indexOf(u'\t'); int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) text_flags |= Qt::TextHideMnemonic; text_flags |= Qt::AlignLeft; // a submenu doesn't paint a possible shortcut in WinUI3 if (t >= 0 && menuitem->menuItemType != QStyleOptionMenuItem::SubMenu) { QRect shortcutRect(QPoint(textRect.right(), textRect.top()), QPoint(rect.right(), textRect.bottom())); QRect vShortcutRect(visualMenuRect(shortcutRect)); QColor penColor; if (highContrastTheme) { penColor = menuitem->palette.color(act ? QPalette::HighlightedText : QPalette::Text); } else { penColor = menuitem->palette.color(dis ? QPalette::Disabled : QPalette::Active, QPalette::Text); if (!dis) penColor.setAlpha(percentToAlpha(60.63)); // fillColorTextSecondary } painter->setPen(penColor); const QString textToDraw = s.mid(t + 1).toString(); painter->drawText(vShortcutRect, text_flags, textToDraw); s = s.left(t); } QFont font = menuitem->font; if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) font.setBold(true); painter->setFont(font); QColor penColor; if (highContrastTheme && act) penColor = menuitem->palette.color(QPalette::HighlightedText); else penColor = menuitem->palette.color(dis ? QPalette::Disabled : QPalette::Current, QPalette::Text); painter->setPen(penColor); const QString textToDraw = s.left(t).toString(); painter->drawText(vRect, text_flags, textToDraw); } if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow int fontSize = menuitem->font.pointSize(); QFont f(d->assetFont); f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller painter->setFont(f); int yOfs = qRound(fontSize / 3.0f); // an offset to align the '>' with the baseline of the text QPoint tl(rect.right() - 2 * QWindowsStylePrivate::windowsArrowHMargin - contentItemHMargin, rect.top() + yOfs); QRect submenuRect(tl, rect.bottomRight()); QRect vSubMenuRect = visualMenuRect(submenuRect); painter->setPen(option->palette.text().color()); const bool isReverse = option->direction == Qt::RightToLeft; const auto str = isReverse ? ChevronLeftMed : ChevronRightMed; painter->drawText(vSubMenuRect, Qt::AlignCenter, str); } } break; #endif // QT_CONFIG(menu) case CE_MenuBarEmptyArea: { break; } case CE_HeaderEmptyArea: break; case CE_HeaderSection: { if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { painter->setPen(Qt::NoPen); painter->setBrush(header->palette.button()); painter->drawRect(header->rect); painter->setPen(highContrastTheme == true ? header->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); painter->setBrush(Qt::NoBrush); if (header->position == QStyleOptionHeader::OnlyOneSection) { break; } else if (header->position == QStyleOptionHeader::Beginning) { painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); } else if (header->position == QStyleOptionHeader::End) { painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); } else { painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); } painter->drawLine(QPointF(option->rect.bottomLeft()) + QPointF(0.0,0.5), QPointF(option->rect.bottomRight()) + QPointF(0.0,0.5)); } break; } case CE_ItemViewItem: { if (const QStyleOptionViewItem *vopt = qstyleoption_cast(option)) { QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); // draw the background proxy()->drawPrimitive(PE_PanelItemViewItem, option, painter, widget); const QRect &rect = vopt->rect; const bool isRtl = option->direction == Qt::RightToLeft; bool onlyOne = vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || vopt->viewItemPosition == QStyleOptionViewItem::Invalid; bool isFirst = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; bool isLast = vopt->viewItemPosition == QStyleOptionViewItem::End; // the tree decoration already painted the left side of the rounded rect if (vopt->features.testFlag(QStyleOptionViewItem::IsDecoratedRootColumn) && vopt->showDecorationSelected) { isFirst = false; if (onlyOne) { onlyOne = false; isLast = true; } } if (isRtl) { if (isFirst) { isFirst = false; isLast = true; } else if (isLast) { isFirst = true; isLast = false; } } const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver); if (highlightCurrent) { if (highContrastTheme) { painter->setBrush(vopt->palette.highlight()); } else { const QAbstractItemView *view = qobject_cast(widget); painter->setBrush(view && view->alternatingRowColors() ? vopt->palette.highlight() : winUI3Color(subtleHighlightColor)); } } else { painter->setBrush(vopt->backgroundBrush); } painter->setPen(Qt::NoPen); if (onlyOne) { painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, 2, 2)), secondLevelRoundingRadius, secondLevelRoundingRadius); } else if (isFirst) { painter->save(); painter->setClipRect(rect); painter->drawRoundedRect(rect.marginsRemoved(QMargins(2, 2, -secondLevelRoundingRadius, 2)), secondLevelRoundingRadius, secondLevelRoundingRadius); painter->restore(); } else if (isLast) { painter->save(); painter->setClipRect(rect); painter->drawRoundedRect(rect.marginsRemoved(QMargins(-secondLevelRoundingRadius, 2, 2, 2)), secondLevelRoundingRadius, secondLevelRoundingRadius); painter->restore(); } else { painter->drawRect(rect.marginsRemoved(QMargins(0, 2, 0, 2))); } // draw the check mark if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { QStyleOptionViewItem option(*vopt); option.rect = checkRect; option.state = option.state & ~QStyle::State_HasFocus; switch (vopt->checkState) { case Qt::Unchecked: option.state |= QStyle::State_Off; break; case Qt::PartiallyChecked: option.state |= QStyle::State_NoChange; break; case Qt::Checked: option.state |= QStyle::State_On; break; } proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget); } // draw the icon if (iconRect.isValid()) { QIcon::Mode mode = QIcon::Normal; if (!(vopt->state & QStyle::State_Enabled)) mode = QIcon::Disabled; else if (vopt->state & QStyle::State_Selected) mode = QIcon::Selected; QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); } painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() : vopt->palette.text().color()); d->viewItemDrawText(painter, vopt, textRect); // paint a vertical marker for QListView if (vopt->state & State_Selected) { if (const QListView *lv = qobject_cast(widget); lv && lv->viewMode() != QListView::IconMode && !highContrastTheme) { painter->setPen(vopt->palette.accent().color()); const auto xPos = isRtl ? rect.right() - 1 : rect.left(); const QLineF lines[2] = { QLineF(xPos, rect.y() + 2, xPos, rect.y() + rect.height() - 2), QLineF(xPos + 1, rect.y() + 2, xPos + 1, rect.y() + rect.height() - 2), }; painter->drawLines(lines, 2); } } } break; } default: QWindowsVistaStyle::drawControl(element, option, painter, widget); } painter->restore(); } int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const { switch (hint) { case QStyle::SH_Menu_AllowActiveAndDisabled: return 0; case SH_GroupBox_TextLabelColor: if (opt!=nullptr && widget!=nullptr) return opt->palette.text().color().rgba(); return 0; case QStyle::SH_ItemView_ShowDecorationSelected: return 1; case QStyle::SH_Slider_AbsoluteSetButtons: return Qt::LeftButton; case QStyle::SH_Slider_PageSetButtons: return 0; default: return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData); } } QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const { QRect ret; switch (element) { case QStyle::SE_RadioButtonIndicator: case QStyle::SE_CheckBoxIndicator: ret = QWindowsVistaStyle::subElementRect(element, option, widget); ret.moveLeft(contentItemHMargin); break; case QStyle::SE_ComboBoxFocusRect: case QStyle::SE_CheckBoxFocusRect: case QStyle::SE_RadioButtonFocusRect: case QStyle::SE_PushButtonFocusRect: ret = option->rect; break; case QStyle::SE_LineEditContents: ret = option->rect.adjusted(4,0,-4,0); break; case QStyle::SE_ItemViewItemText: if (const auto *item = qstyleoption_cast(option)) { const int decorationOffset = item->features.testFlag(QStyleOptionViewItem::HasDecoration) ? item->decorationSize.width() : 0; const int checkboxOffset = item->features.testFlag(QStyleOptionViewItem::HasCheckIndicator) ? 16 : 0; if (widget && qobject_cast(widget->parentWidget())) { if (option->direction == Qt::LeftToRight) ret = option->rect.adjusted(decorationOffset + checkboxOffset + 5, 0, -5, 0); else ret = option->rect.adjusted(5, 0, decorationOffset - checkboxOffset - 5, 0); } else { ret = QWindowsVistaStyle::subElementRect(element, option, widget); } } else { ret = QWindowsVistaStyle::subElementRect(element, option, widget); } break; #if QT_CONFIG(progressbar) case SE_ProgressBarGroove: case SE_ProgressBarContents: case SE_ProgressBarLabel: if (const auto *pb = qstyleoption_cast(option)) { QStyleOptionProgressBar optCopy(*pb); // we only support label right from content optCopy.textAlignment = Qt::AlignRight; return QWindowsVistaStyle::subElementRect(element, &optCopy, widget); } break; #endif // QT_CONFIG(progressbar) case QStyle::SE_HeaderLabel: case QStyle::SE_HeaderArrow: ret = QCommonStyle::subElementRect(element, option, widget); break; case SE_PushButtonContents: { int border = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); ret = option->rect.marginsRemoved(QMargins(border, border, border, border)); break; } default: ret = QWindowsVistaStyle::subElementRect(element, option, widget); } return ret; } /*! \internal */ QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const { QRect ret; switch (control) { #if QT_CONFIG(spinbox) case CC_SpinBox: if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast(option)) { QSize bs; int fw = spinbox->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0; bs.setHeight(qMax(8, spinbox->rect.height() - fw)); bs.setWidth(16); int y = fw + spinbox->rect.y(); int x, lx, rx; x = spinbox->rect.x() + spinbox->rect.width() - fw - 2 * bs.width(); lx = fw; rx = x - fw; switch (subControl) { case SC_SpinBoxUp: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) return QRect(); ret = QRect(x, y, bs.width(), bs.height()); break; case SC_SpinBoxDown: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) return QRect(); ret = QRect(x + bs.width(), y, bs.width(), bs.height()); break; case SC_SpinBoxEditField: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) { ret = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw); } else { ret = QRect(lx, fw, rx, spinbox->rect.height() - 2*fw); } break; case SC_SpinBoxFrame: ret = spinbox->rect; default: break; } ret = visualRect(spinbox->direction, spinbox->rect, ret); } break; case CC_TitleBar: if (const QStyleOptionTitleBar *titlebar = qstyleoption_cast(option)) { SubControl sc = subControl; ret = QCommonStyle::subControlRect(control, option, subControl, widget); static constexpr int indent = 3; static constexpr int controlWidthMargin = 2; const int controlHeight = titlebar->rect.height(); const int controlWidth = 46; const int iconSize = proxy()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, option, widget); int offset = -(controlWidthMargin + indent); bool isMinimized = titlebar->titleBarState & Qt::WindowMinimized; bool isMaximized = titlebar->titleBarState & Qt::WindowMaximized; switch (sc) { case SC_TitleBarLabel: if (titlebar->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) { ret = titlebar->rect; if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) ret.adjust(iconSize + controlWidthMargin + indent, 0, -controlWidth, 0); if (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) ret.adjust(0, 0, -controlWidth, 0); if (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) ret.adjust(0, 0, -controlWidth, 0); if (titlebar->titleBarFlags & Qt::WindowShadeButtonHint) ret.adjust(0, 0, -controlWidth, 0); if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint) ret.adjust(0, 0, -controlWidth, 0); } break; case SC_TitleBarContextHelpButton: if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint) offset += controlWidth; Q_FALLTHROUGH(); case SC_TitleBarMinButton: if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)) offset += controlWidth; else if (sc == SC_TitleBarMinButton) break; Q_FALLTHROUGH(); case SC_TitleBarNormalButton: if (isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)) offset += controlWidth; else if (isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)) offset += controlWidth; else if (sc == SC_TitleBarNormalButton) break; Q_FALLTHROUGH(); case SC_TitleBarMaxButton: if (!isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)) offset += controlWidth; else if (sc == SC_TitleBarMaxButton) break; Q_FALLTHROUGH(); case SC_TitleBarShadeButton: if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) offset += controlWidth; else if (sc == SC_TitleBarShadeButton) break; Q_FALLTHROUGH(); case SC_TitleBarUnshadeButton: if (isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) offset += controlWidth; else if (sc == SC_TitleBarUnshadeButton) break; Q_FALLTHROUGH(); case SC_TitleBarCloseButton: if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) offset += controlWidth; else if (sc == SC_TitleBarCloseButton) break; ret.setRect(titlebar->rect.right() - offset, titlebar->rect.top(), controlWidth, controlHeight); break; case SC_TitleBarSysMenu: if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) { const auto yOfs = titlebar->rect.top() + (titlebar->rect.height() - iconSize) / 2; ret.setRect(titlebar->rect.left() + controlWidthMargin + indent, yOfs, iconSize, iconSize); } break; default: break; } if (widget && isMinimized && titlebar->rect.width() < offset) const_cast(widget)->resize(controlWidthMargin + indent + offset + iconSize + controlWidthMargin, controlWidth); ret = visualRect(titlebar->direction, titlebar->rect, ret); } break; #endif // Qt_NO_SPINBOX case CC_ScrollBar: { ret = QCommonStyle::subControlRect(control, option, subControl, widget); if (subControl == SC_ScrollBarAddLine || subControl == SC_ScrollBarSubLine) { if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(option)) { if (scrollbar->orientation == Qt::Vertical) ret = ret.adjusted(2,2,-2,-3); else ret = ret.adjusted(3,2,-2,-2); } } break; } default: ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget); } return ret; } /*! \internal */ QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const { QSize contentSize(size); switch (type) { #if QT_CONFIG(menubar) case CT_MenuBarItem: if (!contentSize.isEmpty()) { constexpr int hMargin = 2 * 6; constexpr int hPadding = 2 * 11; constexpr int itemHeight = 32; contentSize.setWidth(contentSize.width() + hMargin + hPadding); #if QT_CONFIG(tabwidget) if (widget->parent() && !qobject_cast(widget->parent())) #endif contentSize.setHeight(itemHeight); } break; #endif #if QT_CONFIG(menu) case CT_MenuItem: if (const auto *menuItem = qstyleoption_cast(option)) { int width = size.width(); int height; if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { width = 10; height = 3; } else { height = menuItem->fontMetrics.height() + 8; if (!menuItem->icon.isNull()) { int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option, widget); height = qMax(height, menuItem->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4); } } if (menuItem->text.contains(u'\t')) width += contentItemHMargin; // the text width is already in if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) width += 2 * QWindowsStylePrivate::windowsArrowHMargin + contentItemHMargin; if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) { const QFontMetrics fm(menuItem->font); QFont fontBold = menuItem->font; fontBold.setBold(true); const QFontMetrics fmBold(fontBold); width += fmBold.horizontalAdvance(menuItem->text) - fm.horizontalAdvance(menuItem->text); } // in contrast to windowsvista, the checkmark and icon are drawn separately if (menuItem->menuHasCheckableItems) { const auto checkMarkWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget); width += checkMarkWidth + contentItemHMargin * 2; } // we have an icon and it's already in the given size, only add margins // 4 is added in qmenu.cpp to PM_SmallIconSize if (menuItem->maxIconWidth > 0) width += contentItemHMargin * 2 + menuItem->maxIconWidth - 4; width += 2 * 2; // margins for rounded border width += 2 * contentHMargin; if (width < 100) // minimum size width = 100; contentSize = QSize(width, height); } break; #endif // QT_CONFIG(menu) #if QT_CONFIG(spinbox) case QStyle::CT_SpinBox: { if (const auto *spinBoxOpt = qstyleoption_cast(option)) { // Add button + frame widths const qreal dpi = QStyleHelper::dpi(option); const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons); const int margins = 8; const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0; const int frameWidth = spinBoxOpt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinBoxOpt, widget) : 0; contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth); } break; } #endif case CT_ComboBox: if (const auto *comboBoxOpt = qstyleoption_cast(option)) { contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData contentSize += QSize(4, 4); // default win11 style margins if (comboBoxOpt->subControls & SC_ComboBoxArrow) contentSize += QSize(8, 0); // arrow margins } break; case CT_HeaderSection: // windows vista does not honor the indicator (as it was drawn above the text, not on the // side) so call QWindowsStyle::styleHint directly to get the correct size hint contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); break; case CT_RadioButton: case CT_CheckBox: if (const auto *buttonOpt = qstyleoption_cast(option)) { const auto p = proxy(); const bool isRadio = (type == CT_RadioButton); const int width = p->pixelMetric( isRadio ? PM_ExclusiveIndicatorWidth : PM_IndicatorWidth, option, widget); const int height = p->pixelMetric( isRadio ? PM_ExclusiveIndicatorHeight : PM_IndicatorHeight, option, widget); int margins = 2 * contentItemHMargin; if (!buttonOpt->icon.isNull() || !buttonOpt->text.isEmpty()) { margins += p->pixelMetric(isRadio ? PM_RadioButtonLabelSpacing : PM_CheckBoxLabelSpacing, option, widget); } contentSize += QSize(width + margins, 4); contentSize.setHeight(qMax(size.height(), height + 2 * contentItemHMargin)); } break; // the indicator needs 2px more in width when there is no text, not needed when // the style draws the text contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget); if (size.width() == 0) contentSize.rwidth() += 2; break; case CT_PushButton: { contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget); // we want our own horizontal spacing const int oldMargin = proxy()->pixelMetric(PM_ButtonMargin, option, widget); contentSize.rwidth() += 2 * contentHMargin - oldMargin; break; } default: contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget); break; } return contentSize; } /*! \internal */ int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const { Q_D(const QWindows11Style); int res = 0; switch (metric) { case QStyle::PM_IndicatorWidth: case QStyle::PM_IndicatorHeight: case QStyle::PM_ExclusiveIndicatorWidth: case QStyle::PM_ExclusiveIndicatorHeight: res = 16; break; case PM_SliderThickness: // full height of a slider if (auto opt = qstyleoption_cast(option)) { // hard-coded in qslider.cpp, but we need a little bit more constexpr auto TickSpace = 5; if (opt->tickPosition & QSlider::TicksAbove) res += 6 - TickSpace; if (opt->tickPosition & QSlider::TicksBelow) res += 6 - TickSpace; } Q_FALLTHROUGH(); case PM_SliderControlThickness: // size of the control handle case PM_SliderLength: // same because handle is a circle with r=8 res += 2 * 8; break; case PM_RadioButtonLabelSpacing: case PM_CheckBoxLabelSpacing: res = 2 * contentItemHMargin; break; case QStyle::PM_TitleBarButtonIconSize: res = 16; break; case QStyle::PM_TitleBarButtonSize: res = 32; break; #if QT_CONFIG(toolbar) case PM_ToolBarExtensionExtent: res = int(QStyleHelper::dpiScaled(32., option)); break; case PM_ToolBarHandleExtent: res = int(QStyleHelper::dpiScaled(8., option)); break; #endif // QT_CONFIG(toolbar) case QStyle::PM_ScrollBarExtent: res = 12; break; case QStyle::PM_SubMenuOverlap: res = -1; break; case PM_MenuButtonIndicator: { res = contentItemHMargin; if (widget) { const int fontSize = widget->font().pointSize(); auto it = m_fontPoint2ChevronDownMedWidth.find(fontSize); if (it == m_fontPoint2ChevronDownMedWidth.end()) { QFont f(d->assetFont); f.setPointSize(qRound(fontSize * 0.9f)); // a little bit smaller QFontMetrics fm(f); const auto width = fm.horizontalAdvance(ChevronDownMed); m_fontPoint2ChevronDownMedWidth.insert(fontSize, width); res += width; } else { res += it.value(); } } else { res += 12; } break; } case PM_DefaultFrameWidth: res = 2; break; case PM_ButtonShiftHorizontal: case PM_ButtonShiftVertical: res = 0; break; default: res = QWindowsVistaStyle::pixelMetric(metric, option, widget); } return res; } void QWindows11Style::polish(QWidget* widget) { #if QT_CONFIG(commandlinkbutton) if (!qobject_cast(widget)) #endif // QT_CONFIG(commandlinkbutton) QWindowsVistaStyle::polish(widget); const bool isScrollBar = qobject_cast(widget); const auto comboBoxContainer = qobject_cast(widget); #if QT_CONFIG(menubar) if (qobject_cast(widget)) { constexpr int itemHeight = 32; if (widget->maximumHeight() < itemHeight) { widget->setProperty("_q_original_menubar_maxheight", widget->maximumHeight()); widget->setMaximumHeight(itemHeight); } } #endif if (isScrollBar || qobject_cast(widget) || comboBoxContainer) { bool wasCreated = widget->testAttribute(Qt::WA_WState_Created); bool layoutDirection = widget->testAttribute(Qt::WA_RightToLeft); widget->setAttribute(Qt::WA_OpaquePaintEvent,false); widget->setAttribute(Qt::WA_TranslucentBackground); if (!isScrollBar) widget->setWindowFlag(Qt::FramelessWindowHint); widget->setWindowFlag(Qt::NoDropShadowWindowHint); widget->setAttribute(Qt::WA_RightToLeft, layoutDirection); widget->setAttribute(Qt::WA_WState_Created, wasCreated); if (!isScrollBar) { bool inGraphicsView = widget->graphicsProxyWidget() != nullptr; if (!inGraphicsView && comboBoxContainer && comboBoxContainer->parentWidget()) inGraphicsView = comboBoxContainer->parentWidget()->graphicsProxyWidget() != nullptr; if (!inGraphicsView) { // for menus and combobox containers... QGraphicsDropShadowEffect *dropshadow = new QGraphicsDropShadowEffect(widget); dropshadow->setBlurRadius(3); dropshadow->setXOffset(3); dropshadow->setYOffset(3); widget->setGraphicsEffect(dropshadow); } } } else if (QComboBox* cb = qobject_cast(widget)) { if (cb->isEditable()) { QLineEdit *le = cb->lineEdit(); le->setFrame(false); } } else if (const auto *scrollarea = qobject_cast(widget); scrollarea && !qobject_cast(widget) #if QT_CONFIG(mdiarea) && !qobject_cast(widget) #endif ) { if (scrollarea->frameShape() == QFrame::StyledPanel) { const auto vp = scrollarea->viewport(); const bool isAutoFillBackground = vp->autoFillBackground(); const bool isStyledBackground = vp->testAttribute(Qt::WA_StyledBackground); vp->setProperty("_q_original_autofill_background", isAutoFillBackground); vp->setProperty("_q_original_styled_background", isStyledBackground); vp->setAutoFillBackground(false); vp->setAttribute(Qt::WA_StyledBackground, true); } // QTreeView & QListView are already set in the base windowsvista style if (auto table = qobject_cast(widget)) table->viewport()->setAttribute(Qt::WA_Hover, true); } } void QWindows11Style::unpolish(QWidget *widget) { #if QT_CONFIG(commandlinkbutton) if (!qobject_cast(widget)) #endif // QT_CONFIG(commandlinkbutton) QWindowsVistaStyle::unpolish(widget); #if QT_CONFIG(menubar) if (qobject_cast(widget) && !widget->property("_q_original_menubar_maxheight").isNull()) { widget->setMaximumHeight(widget->property("_q_original_menubar_maxheight").toInt()); widget->setProperty("_q_original_menubar_maxheight", QVariant()); } #endif if (const auto *scrollarea = qobject_cast(widget); scrollarea #if QT_CONFIG(mdiarea) && !qobject_cast(widget) #endif ) { const auto vp = scrollarea->viewport(); const auto wasAutoFillBackground = vp->property("_q_original_autofill_background").toBool(); vp->setAutoFillBackground(wasAutoFillBackground); vp->setProperty("_q_original_autofill_background", QVariant()); const auto origStyledBackground = vp->property("_q_original_styled_background").toBool(); vp->setAttribute(Qt::WA_StyledBackground, origStyledBackground); vp->setProperty("_q_original_styled_background", QVariant()); } } /* The colors for Windows 11 are taken from the official WinUI3 Figma style at https://www.figma.com/community/file/1159947337437047524 */ #define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE) \ if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged) \ result.setColor(GROUP, ROLE, VALUE) static void populateLightSystemBasePalette(QPalette &result) { static QString oldStyleSheet; const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet(); constexpr QColor textColor = QColor(0x00,0x00,0x00,0xE4); constexpr QColor textDisabled = QColor(0x00, 0x00, 0x00, 0x5C); constexpr QColor btnFace = QColor(0xFF, 0xFF, 0xFF, 0xB3); constexpr QColor base = QColor(0xFF, 0xFF, 0xFF, 0xFF); constexpr QColor alternateBase = QColor(0x00, 0x00, 0x00, 0x09); const QColor btnHighlight = result.accent().color(); const QColor btnColor = result.button().color(); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Highlight, btnHighlight); SET_IF_UNRESOLVED(QPalette::Active, QPalette::WindowText, textColor); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Button, btnFace); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Light, btnColor.lighter(150)); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Dark, btnColor.darker(200)); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Mid, btnColor.darker(150)); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Text, textColor); SET_IF_UNRESOLVED(QPalette::Active, QPalette::BrightText, btnHighlight); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Base, base); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF)); SET_IF_UNRESOLVED(QPalette::Active, QPalette::ButtonText, textColor); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Midlight, btnColor.lighter(125)); SET_IF_UNRESOLVED(QPalette::Active, QPalette::Shadow, Qt::black); SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipBase, result.window().color()); SET_IF_UNRESOLVED(QPalette::Active, QPalette::ToolTipText, result.windowText().color()); SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, btnHighlight); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, textColor); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, btnFace); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, btnColor.lighter(150)); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, btnColor.darker(200)); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Mid, btnColor.darker(150)); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, textColor); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::BrightText, btnHighlight); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Base, base); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF)); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ButtonText, textColor); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Midlight, btnColor.lighter(125)); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Shadow, Qt::black); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipBase, result.window().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::ToolTipText, result.windowText().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase); result.setColor(QPalette::Disabled, QPalette::WindowText, textDisabled); if (result.midlight() == result.button()) result.setColor(QPalette::Midlight, btnColor.lighter(110)); oldStyleSheet = qApp->styleSheet(); } static void populateDarkSystemBasePalette(QPalette &result) { static QString oldStyleSheet; const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet(); constexpr QColor alternateBase = QColor(0xFF, 0xFF, 0xFF, 0x0F); SET_IF_UNRESOLVED(QPalette::Active, QPalette::AlternateBase, alternateBase); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::AlternateBase, alternateBase); oldStyleSheet = qApp->styleSheet(); } /*! \internal */ void QWindows11Style::polish(QPalette& result) { highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown; colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1; if (!highContrastTheme && colorSchemeIndex == 0) populateLightSystemBasePalette(result); else if (!highContrastTheme && colorSchemeIndex == 1) populateDarkSystemBasePalette(result); const bool styleSheetChanged = false; // so the macro works SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color()); auto *d = const_cast(d_func()); d->m_titleBarMinIcon = QIcon(); d->m_titleBarMaxIcon = QIcon(); d->m_titleBarCloseIcon = QIcon(); d->m_titleBarNormalIcon = QIcon(); d->m_toolbarExtensionButton = QIcon(); } QPixmap QWindows11Style::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, const QWidget *widget) const { switch (standardPixmap) { case SP_ToolBarHorizontalExtensionButton: case SP_ToolBarVerticalExtensionButton: { const int size = proxy()->pixelMetric(PM_ToolBarExtensionExtent, option, widget); return standardIcon(standardPixmap, option, widget).pixmap(size); } default: break; } return QWindowsVistaStyle::standardPixmap(standardPixmap, option, widget); } QIcon QWindows11Style::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const { auto *d = const_cast(d_func()); switch (standardIcon) { case SP_ToolBarHorizontalExtensionButton: case SP_ToolBarVerticalExtensionButton: { if (d->m_toolbarExtensionButton.isNull()) { auto e = new WinFontIconEngine(More.at(0), d->assetFont); e->setScale(1.0); d->m_toolbarExtensionButton = QIcon(e); } return d->m_toolbarExtensionButton; } default: break; } return QWindowsVistaStyle::standardIcon(standardIcon, option, widget); } QColor QWindows11Style::calculateAccentColor(const QStyleOption *option) const { using namespace StyleOptionHelper; if (isDisabled(option)) return winUI3Color(fillAccentDisabled); const auto alphaColor = isPressed(option) ? fillAccentTertiary : isHover(option) ? fillAccentSecondary : fillAccentDefault; const auto alpha = winUI3Color(alphaColor); QColor col = option->palette.accent().color(); col.setAlpha(alpha.alpha()); return col; } QPen QWindows11Style::borderPenControlAlt(const QStyleOption *option) const { using namespace StyleOptionHelper; if (isChecked(option)) return Qt::NoPen; // same color as fill color, so no pen needed if (highContrastTheme) return option->palette.buttonText().color(); if (isDisabled(option) || isPressed(option)) return winUI3Color(frameColorStrongDisabled); return winUI3Color(frameColorStrong); } QBrush QWindows11Style::controlFillBrush(const QStyleOption *option, ControlType controlType) const { using namespace StyleOptionHelper; static constexpr WINUI3Color colorEnums[2][4] = { // Light & Dark Control { fillControlDefault, fillControlSecondary, fillControlTertiary, fillControlDisabled }, // Light & Dark Control Alt { fillControlAltSecondary, fillControlAltTertiary, fillControlAltQuarternary, fillControlAltDisabled }, }; if (option->palette.isBrushSet(QPalette::Current, QPalette::Button)) return option->palette.button(); if (!isChecked(option) && isAutoRaise(option)) return Qt::NoBrush; // checked is the same for Control (Buttons) and Control Alt (Radiobuttons/Checkboxes) if (isChecked(option)) return calculateAccentColor(option); const auto state = calcControlState(option); return winUI3Color(colorEnums[int(controlType)][int(state)]); } QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::ColorRole role) const { using namespace StyleOptionHelper; static constexpr WINUI3Color colorEnums[2][4] = { // Control, unchecked { textPrimary, textPrimary, textSecondary, textDisabled }, // Control, checked { textOnAccentPrimary, textOnAccentPrimary, textOnAccentSecondary, textOnAccentDisabled }, }; if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText)) return option->palette.buttonText().color(); const int colorIndex = isChecked(option) ? 1 : 0; const auto state = calcControlState(option); const auto alpha = winUI3Color(colorEnums[colorIndex][int(state)]); QColor col = option->palette.color(role); col.setAlpha(alpha.alpha()); return col; } void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const { const bool isHovered = o->state & State_MouseOver; const auto frameCol = highContrastTheme ? o->palette.color(isHovered ? QPalette::Accent : QPalette::ButtonText) : winUI3Color(frameColorLight); drawRoundedRect(p, rect, frameCol, Qt::NoBrush); if (!isEditable) return; QPainterStateGuard psg(p); p->setClipRect(rect.marginsRemoved(QMarginsF(0, rect.height() - 0.5, 0, -1))); const bool hasFocus = o->state & State_HasFocus; const auto underlineCol = hasFocus ? o->palette.color(QPalette::Accent) : colorSchemeIndex == 0 ? QColor(0x80, 0x80, 0x80) : QColor(0xa0, 0xa0, 0xa0); const auto penUnderline = QPen(underlineCol, hasFocus ? 2 : 1); drawRoundedRect(p, rect, penUnderline, Qt::NoBrush); } QColor QWindows11Style::winUI3Color(enum WINUI3Color col) const { return WINUI3Colors[colorSchemeIndex][col]; } #undef SET_IF_UNRESOLVED QT_END_NAMESPACE