Windows11Style: rework color handling for Checkbox/Radiobutton/Button

Rework the color handling for CheckBox/Radiobutton and Buttons to match
the WinUI3 style.

Fixes: QTBUG-139202
Fixes: QTBUG-138094
Change-Id: I874e096a78fcf4f92eeac586517b0b987bdd4e38
Reviewed-by: Wladimir Leuschner <wladimir.leuschner@qt.io>
(cherry picked from commit 95d784cca4)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Christian Ehrlicher 2025-08-16 22:57:27 +02:00 committed by Qt Cherry-pick Bot
parent eb9abd562b
commit 81a4634993
2 changed files with 151 additions and 51 deletions

View File

@ -38,6 +38,43 @@ QT_BEGIN_NAMESPACE
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
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, Max = 4 };
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
template <typename R, typename P, typename B>
static inline void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush)
{
@ -46,16 +83,20 @@ static inline void drawRoundedRect(QPainter *p, R &&rect, P &&pen, B &&brush)
p->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
}
static const QColor WINUI3ColorsLight [] {
static constexpr int percentToAlpha(double percent)
{
return qRound(percent * 255. / 100.);
}
static constexpr std::array<QColor, 30> WINUI3ColorsLight {
QColor(0x00,0x00,0x00,0x09), //subtleHighlightColor
QColor(0x00,0x00,0x00,0x06), //subtlePressedColor
QColor(0x00,0x00,0x00,0x0F), //frameColorLight
QColor(0x00,0x00,0x00,0x9c), //frameColorStrong
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(0xF9,0xF9,0xF9,0x00), //controlFillTertiary
QColor(0xF9,0xF9,0xF9,0x80), //controlFillSecondary
QColor(0xFF,0xFF,0xFF,0xFF), //menuPanelFill
QColor(0xFF,0xFF,0xFF,0xFF), //textOnAccentPrimary
QColor(0xFF,0xFF,0xFF,0x7F), //textOnAccentSecondary
@ -63,22 +104,32 @@ static const QColor WINUI3ColorsLight [] {
QColor(0x00,0x00,0x00,0x66), //controlStrokeOnAccentSecondary
QColor(0xFF,0xFF,0xFF,0xFF), //controlFillSolid
QColor(0x75,0x75,0x75,0x66), //surfaceStroke
QColor(0x00,0x00,0x00,0x37), //controlAccentDisabled
QColor(0xFF,0xFF,0xFF,0xFF), //textAccentDisabled
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
};
static const QColor WINUI3ColorsDark[] {
static constexpr std::array<QColor, 30> WINUI3ColorsDark {
QColor(0xFF,0xFF,0xFF,0x0F), //subtleHighlightColor
QColor(0xFF,0xFF,0xFF,0x0A), //subtlePressedColor
QColor(0xFF,0xFF,0xFF,0x12), //frameColorLight
QColor(0xFF,0xFF,0xFF,0x8B), //frameColorStrong
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(0xF9,0xF9,0xF9,0x00), //controlFillTertiary
QColor(0xF9,0xF9,0xF9,0x80), //controlFillSecondary
QColor(0x0F,0x0F,0x0F,0xFF), //menuPanelFill
QColor(0x00,0x00,0x00,0xFF), //textOnAccentPrimary
QColor(0x00,0x00,0x00,0x80), //textOnAccentSecondary
@ -86,13 +137,24 @@ static const QColor WINUI3ColorsDark[] {
QColor(0xFF,0xFF,0xFF,0x14), //controlStrokeOnAccentSecondary
QColor(0x45,0x45,0x45,0xFF), //controlFillSolid
QColor(0x75,0x75,0x75,0x66), //surfaceStroke
QColor(0xFF,0xFF,0xFF,0x28), //controlAccentDisabled
QColor(0xFF,0xFF,0xFF,0x87), //textAccentDisabled
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
};
static const QColor* WINUI3Colors[] {
static constexpr std::array<std::array<QColor,30>, 2> WINUI3Colors {
WINUI3ColorsLight,
WINUI3ColorsDark
};
@ -296,7 +358,7 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt
}
painter->setPen(Qt::NoPen);
painter->setBrush(option->palette.accent());
painter->setBrush(calculateAccentColor(option));
painter->drawRoundedRect(leftRect,1,1);
painter->setBrush(WINUI3Colors[colorSchemeIndex][controlStrongFill]);
painter->drawRoundedRect(rightRect,1,1);
@ -390,7 +452,7 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt
painter->setPen(Qt::NoPen);
painter->setBrush(winUI3Color(controlFillSolid));
painter->drawEllipse(center, outerRadius, outerRadius);
painter->setBrush(option->palette.accent());
painter->setBrush(calculateAccentColor(option));
painter->drawEllipse(center, innerRadius, innerRadius);
painter->setPen(winUI3Color(controlStrokeSecondary));
@ -788,12 +850,8 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
rect.setHeight(15);
rect.moveCenter(center);
QPen borderPen(Qt::NoPen);
if (!isOn && !isPartial) {
borderPen = highContrastTheme ? option->palette.buttonText().color()
: winUI3Color(frameColorStrong);
}
drawRoundedRect(painter, rect, borderPen, buttonFillBrush(option));
drawRoundedRect(painter, rect, borderPenControlAlt(option),
controlFillBrush(option, ControlType::ControlAlt));
if (isOn) {
painter->setFont(assetFont);
@ -844,21 +902,17 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
QRectF rect = isRtl ? option->rect.adjusted(0, 0, -2, 0) : option->rect.adjusted(2, 0, 0, 0);
const QPointF center = rect.center();
painter->setPen(borderPenControlAlt(option));
painter->setBrush(controlFillBrush(option, ControlType::ControlAlt));
if (isOn) {
painter->setPen(Qt::NoPen);
painter->setBrush(buttonFillBrush(option));
QPainterPath path;
path.addEllipse(center, 7.5, 7.5);
path.addEllipse(center, innerRadius, innerRadius);
painter->drawPath(path);
QColor fillColor = option->palette.window().color();
if (option->state & State_MouseOver && option->state & State_Enabled)
fillColor = fillColor.darker(107);
painter->setBrush(fillColor);
// Text On Accent/Primary
painter->setBrush(option->palette.window().color());
painter->drawEllipse(center, innerRadius, innerRadius);
} else {
painter->setPen(winUI3Color(frameColorStrong));
painter->setBrush(buttonFillBrush(option));
painter->drawEllipse(center, 7.5, 7.5);
}
}
@ -873,7 +927,7 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
painter->setPen(Qt::NoPen);
else
painter->setPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary]);
painter->setBrush(buttonFillBrush(option));
painter->setBrush(controlFillBrush(option, ControlType::Control));
painter->drawRoundedRect(rect,
secondLevelRoundingRadius, secondLevelRoundingRadius);
@ -1423,16 +1477,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
}
painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
} else {
if (option->palette.isBrushSet(QPalette::Current, QPalette::Button))
painter->setBrush(option->palette.button());
else if (flags & (State_Sunken))
painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(120) : WINUI3Colors[colorSchemeIndex][controlFillTertiary]);
else if (flags & State_MouseOver)
painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(110) : WINUI3Colors[colorSchemeIndex][controlFillSecondary]);
else if (!(flags & State_Enabled))
painter->setBrush(flags & State_On ? WINUI3Colors[colorSchemeIndex][controlAccentDisabled] : option->palette.button());
else
painter->setBrush(flags & State_On ? option->palette.accent() : option->palette.button());
painter->setBrush(controlFillBrush(option, ControlType::Control));
painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
rect.adjust(0.5,0.5,-0.5,-0.5);
@ -2346,18 +2391,56 @@ void QWindows11Style::polish(QPalette& result)
result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color());
}
QBrush QWindows11Style::buttonFillBrush(const QStyleOption *option)
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][int(ControlState::Max)] = {
// 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();
const bool isOn = (option->state & QStyle::State_On || option->state & QStyle::State_NoChange);
QBrush brush = isOn ? option->palette.accent() : option->palette.window();
if (!isOn && option->state & QStyle::State_AutoRaise)
if (!isChecked(option) && isAutoRaise(option))
return Qt::NoBrush;
if (option->state & QStyle::State_MouseOver)
brush.setColor(isOn ? brush.color().lighter(115) : brush.color().darker(107));
return brush;
// 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::buttonLabelColor(const QStyleOption *option) const

View File

@ -26,12 +26,11 @@ enum WINUI3Color {
subtleHighlightColor, //Subtle highlight based on alpha used for hovered elements
subtlePressedColor, //Subtle highlight based on alpha used for pressed elements
frameColorLight, //Color of frame around flyouts and controls except for Checkbox and Radiobutton
frameColorStrong, //Color of frame around Checkbox and Radiobuttons
frameColorStrong, //Color of frame around Checkbox and Radiobuttons (normal and hover)
frameColorStrongDisabled, //Color of frame around Checkbox and Radiobuttons (pressed and disabled)
controlStrongFill, //Color of controls with strong filling such as the right side of a slider
controlStrokeSecondary,
controlStrokePrimary,
controlFillTertiary, //Color of filled sunken controls
controlFillSecondary, //Color of filled hovered controls
menuPanelFill, //Color of menu panel
textOnAccentPrimary, //Color of text on controls filled in accent color
textOnAccentSecondary, //Color of text of sunken controls in accent color
@ -39,10 +38,21 @@ enum WINUI3Color {
controlStrokeOnAccentSecondary, //Color of frame around Buttons in accent color
controlFillSolid, //Color for solid fill
surfaceStroke, //Color of MDI window frames
controlAccentDisabled,
textAccentDisabled,
focusFrameInnerStroke,
focusFrameOuterStroke
focusFrameOuterStroke,
fillControlDefault, // button default color (alpha)
fillControlSecondary, // button hover color (alpha)
fillControlTertiary, // button pressed color (alpha)
fillControlDisabled, // button disabled color (alpha)
fillControlAltSecondary, // checkbox/RadioButton default color (alpha)
fillControlAltTertiary, // checkbox/RadioButton hover color (alpha)
fillControlAltQuarternary, // checkbox/RadioButton pressed color (alpha)
fillControlAltDisabled, // checkbox/RadioButton disabled color (alpha)
fillAccentDefault, // button default color (alpha)
fillAccentSecondary, // button hover color (alpha)
fillAccentTertiary, // button pressed color (alpha)
fillAccentDisabled, // button disabled color (alpha)
};
class QWindows11Style : public QWindowsVistaStyle
@ -75,7 +85,14 @@ protected:
QWindows11Style(QWindows11StylePrivate &dd);
private:
static inline QBrush buttonFillBrush(const QStyleOption *option);
QColor calculateAccentColor(const QStyleOption *option) const;
QPen borderPenControlAlt(const QStyleOption *option) const;
enum class ControlType
{
Control,
ControlAlt
};
QBrush controlFillBrush(const QStyleOption *option, ControlType controlType) const;
QColor buttonLabelColor(const QStyleOption *option) const;
void drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable = true) const;
inline QColor winUI3Color(enum WINUI3Color col) const;