Apply accent-aware shell surface theming

Add a shared SurfaceColors helper and route shell, action drawer, popup,
and Folio surfaces through accent-aware background colors.
This commit is contained in:
Marco Allegretti 2026-06-01 12:38:17 +02:00
parent 60a0246256
commit b1f8d17f88
14 changed files with 85 additions and 32 deletions

View file

@ -26,6 +26,7 @@ set_source_files_properties(
qml/components/AppLaunch.qml qml/components/AppLaunch.qml
qml/components/Constants.qml qml/components/Constants.qml
qml/components/Motion.qml qml/components/Motion.qml
qml/components/SurfaceColors.qml
qml/dataproviders/AudioInfo.qml qml/dataproviders/AudioInfo.qml
qml/dataproviders/BatteryInfo.qml qml/dataproviders/BatteryInfo.qml
qml/dataproviders/BluetoothInfo.qml qml/dataproviders/BluetoothInfo.qml
@ -60,6 +61,7 @@ ecm_target_qml_sources(mobileshellplugin SOURCES
qml/components/ScreenEdgeDragEffect.qml qml/components/ScreenEdgeDragEffect.qml
qml/components/StartupFeedbackPanelFill.qml qml/components/StartupFeedbackPanelFill.qml
qml/components/StartupFeedbackWindows.qml qml/components/StartupFeedbackWindows.qml
qml/components/SurfaceColors.qml
qml/components/TextDropShadow.qml qml/components/TextDropShadow.qml
qml/components/VelocityCalculator.qml qml/components/VelocityCalculator.qml

View file

@ -77,10 +77,7 @@ Item {
// Background color // Background color
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.9)
Kirigami.Theme.backgroundColor.g,
Kirigami.Theme.backgroundColor.b,
0.9)
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.StandardDecel } } Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.StandardDecel } }
opacity: { opacity: {
let base = Math.max(0, Math.min(brightnessPressedValue, actionDrawer.offset / root.minimizedQuickSettingsOffset)); let base = Math.max(0, Math.min(brightnessPressedValue, actionDrawer.offset / root.minimizedQuickSettingsOffset));

View file

@ -28,6 +28,7 @@ QQC2.Popup {
padding: Kirigami.Units.smallSpacing padding: Kirigami.Units.smallSpacing
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast) readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property color surfaceColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
property string currentPluginId: "" property string currentPluginId: ""
property Item __currentItem: null property Item __currentItem: null
@ -81,11 +82,11 @@ QQC2.Popup {
} }
background: Kirigami.ShadowedRectangle { background: Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor color: popup.surfaceColor
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
border.color: Kirigami.ColorUtils.linearInterpolation( border.color: Kirigami.ColorUtils.linearInterpolation(
Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2) popup.surfaceColor, Kirigami.Theme.textColor, 0.2)
border.width: 1 border.width: 1
shadow.size: Kirigami.Units.gridUnit shadow.size: Kirigami.Units.gridUnit

View file

@ -74,7 +74,7 @@ Item {
id: actionDrawerSurfacePath id: actionDrawerSurfacePath
readonly property real cornerRadius: actionDrawerSurface.cornerRadius readonly property real cornerRadius: actionDrawerSurface.cornerRadius
fillColor: Kirigami.Theme.backgroundColor fillColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
strokeWidth: 0 strokeWidth: 0
startX: actionDrawerSurface.width startX: actionDrawerSurface.width
startY: 0 startY: 0

View file

@ -28,6 +28,7 @@ QQC2.Popup {
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast) readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property int trayItemCount: trayList.count readonly property int trayItemCount: trayList.count
readonly property color surfaceColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
function show() { function show() {
popup.open(); popup.open();
@ -38,11 +39,11 @@ QQC2.Popup {
} }
background: Kirigami.ShadowedRectangle { background: Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor color: popup.surfaceColor
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
border.color: Kirigami.ColorUtils.linearInterpolation( border.color: Kirigami.ColorUtils.linearInterpolation(
Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2) popup.surfaceColor, Kirigami.Theme.textColor, 0.2)
border.width: 1 border.width: 1
shadow.size: Kirigami.Units.gridUnit shadow.size: Kirigami.Units.gridUnit

View file

@ -13,7 +13,7 @@ Item {
property bool active: false property bool active: false
property bool stateLayerEnabled: true property bool stateLayerEnabled: true
property color color: Kirigami.Theme.textColor property color color: Kirigami.Theme.textColor
property color activeColor: Kirigami.Theme.highlightColor property color activeColor: SurfaceColors.accent()
property real hoverOpacity: 0.08 property real hoverOpacity: 0.08
property real pressedOpacity: 0.14 property real pressedOpacity: 0.14
property real activeOpacity: 0.12 property real activeOpacity: 0.12

View file

@ -48,15 +48,24 @@ Item {
// adjust color depending on panel type // adjust color depending on panel type
property color panelColor: { property color panelColor: {
let tintPercent let tintPercent
let accentTintDark
let accentTintLight
if (panelType === PanelBackground.PanelType.Popup) { if (panelType === PanelBackground.PanelType.Popup) {
tintPercent = 0.035 tintPercent = 0.035
accentTintDark = 0.16
accentTintLight = 0.08
} else if (panelType === PanelBackground.PanelType.Base || panelType === PanelBackground.PanelType.Stacked || panelType === PanelBackground.PanelType.Flat) { } else if (panelType === PanelBackground.PanelType.Base || panelType === PanelBackground.PanelType.Stacked || panelType === PanelBackground.PanelType.Flat) {
tintPercent = 0 tintPercent = 0
accentTintDark = 0.18
accentTintLight = 0.10
} else { } else {
tintPercent = 0.06 tintPercent = 0.06
accentTintDark = 0.22
accentTintLight = 0.12
} }
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", tintPercent) const baseColor = Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", tintPercent)
return SurfaceColors.accentSurface(baseColor, accentTintDark, accentTintLight)
} }
// in some circumstances, panels can change there type // in some circumstances, panels can change there type
// for example, popup notifition when opening the popup notifition drawer // for example, popup notifition when opening the popup notifition drawer

View file

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2026 Marco Allegretti
// SPDX-License-Identifier: EUPL-1.2
pragma Singleton
import QtQuick
import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
QtObject {
function hasColor(color) {
return color && color.a > 0
}
function accent() {
if (hasColor(ShellSettings.Settings.accentColor)) {
return ShellSettings.Settings.accentColor
}
if ((ShellSettings.Settings.wallpaperThemeEnabled || ShellSettings.Settings.wallpaperAccentEnabled)
&& hasColor(ShellSettings.Settings.wallpaperThemeColor)) {
return ShellSettings.Settings.wallpaperThemeColor
}
return Kirigami.Theme.highlightColor
}
function mix(base, overlay, ratio) {
return Qt.rgba(
base.r + (overlay.r - base.r) * ratio,
base.g + (overlay.g - base.g) * ratio,
base.b + (overlay.b - base.b) * ratio,
base.a + (overlay.a - base.a) * ratio)
}
function withAlpha(color, alpha) {
return Qt.rgba(color.r, color.g, color.b, alpha)
}
function accentSurface(base, darkRatio, lightRatio) {
if (!ShellSettings.Settings.wallpaperThemeEnabled && !ShellSettings.Settings.wallpaperAccentEnabled) {
return base
}
const darkSurface = Kirigami.ColorUtils.brightnessForColor(base) === Kirigami.ColorUtils.Dark
return mix(base, accent(), darkSurface ? darkRatio : lightRatio)
}
}

View file

@ -57,7 +57,7 @@ Window {
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
readonly property color backgroundColor: Qt.darker(Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95), 1.05) readonly property color backgroundColor: Qt.darker(MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.95), 1.05)
color: popupDrawerOpened && visible ? backgroundColor : "transparent" color: popupDrawerOpened && visible ? backgroundColor : "transparent"
Behavior on color { Behavior on color {
MobileShell.MotionColorAnimation { MobileShell.MotionColorAnimation {

View file

@ -34,7 +34,7 @@ Window {
LayerShell.Window.layer: LayerShell.Window.LayerOverlay LayerShell.Window.layer: LayerShell.Window.LayerOverlay
LayerShell.Window.exclusionZone: -1 LayerShell.Window.exclusionZone: -1
readonly property color backgroundColor: Qt.darker(Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95), 1.05) readonly property color backgroundColor: Qt.darker(MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.95), 1.05)
readonly property int overlayAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.25) readonly property int overlayAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.25)
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary Kirigami.Theme.colorSet: Kirigami.Theme.Complementary

View file

@ -57,7 +57,8 @@ MouseArea {
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18) return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18)
} }
if (active) { if (active) {
return Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, hovered ? 0.18 : 0.12) const accent = MobileShell.SurfaceColors.accent()
return Qt.rgba(accent.r, accent.g, accent.b, hovered ? 0.18 : 0.12)
} }
if (hovered) { if (hovered) {
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08) return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
@ -756,7 +757,7 @@ MouseArea {
PC3.Label { PC3.Label {
anchors.centerIn: parent anchors.centerIn: parent
text: (leftDesktopBtn.index + 1).toString() text: (leftDesktopBtn.index + 1).toString()
color: leftDesktopBtn.isCurrent ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor color: leftDesktopBtn.isCurrent ? MobileShell.SurfaceColors.accent() : Kirigami.Theme.textColor
font.pixelSize: Math.round(parent.height * 0.3) font.pixelSize: Math.round(parent.height * 0.3)
font.bold: leftDesktopBtn.isCurrent font.bold: leftDesktopBtn.isCurrent
} }
@ -1487,7 +1488,7 @@ MouseArea {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: Kirigami.Theme.backgroundColor color: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
border.color: Qt.rgba( border.color: Qt.rgba(
Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.r,
Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.g,
@ -1544,9 +1545,7 @@ MouseArea {
anchors.fill: parent anchors.fill: parent
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
color: thumbEntry.containsMouse color: thumbEntry.containsMouse
? Qt.rgba(Kirigami.Theme.highlightColor.r, ? MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accent(), 0.15)
Kirigami.Theme.highlightColor.g,
Kirigami.Theme.highlightColor.b, 0.15)
: "transparent" : "transparent"
} }
@ -1790,7 +1789,7 @@ MouseArea {
width: Kirigami.Units.smallSpacing * 3 width: Kirigami.Units.smallSpacing * 3
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio)) height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
radius: height / 2 radius: height / 2
color: Kirigami.Theme.highlightColor color: MobileShell.SurfaceColors.accent()
opacity: taskDelegate.dynamicTilingMaximized ? 0.95 : 0 opacity: taskDelegate.dynamicTilingMaximized ? 0.95 : 0
Behavior on opacity { Behavior on opacity {
@ -1861,7 +1860,7 @@ MouseArea {
width: taskDelegate.model.IsActive === true ? Kirigami.Units.smallSpacing * 3 : Kirigami.Units.smallSpacing * 1.5 width: taskDelegate.model.IsActive === true ? Kirigami.Units.smallSpacing * 3 : Kirigami.Units.smallSpacing * 1.5
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio)) height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
radius: height / 2 radius: height / 2
color: Kirigami.Theme.highlightColor color: MobileShell.SurfaceColors.accent()
opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.45 opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.45
Behavior on width { Behavior on width {

View file

@ -33,6 +33,7 @@ Window {
readonly property string backButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB) readonly property string backButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB)
readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonX) readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonX)
readonly property string exitButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonY) readonly property string exitButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonY)
readonly property string leftShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonLeftShoulder) readonly property string leftShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonLeftShoulder)
readonly property string rightShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonRightShoulder) readonly property string rightShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonRightShoulder)
readonly property string quickSettingsButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonBack) readonly property string quickSettingsButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonBack)
@ -481,9 +482,7 @@ Window {
anchors.fill: parent anchors.fill: parent
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.colorSet: Kirigami.Theme.Window
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), 0.92)
Kirigami.Theme.backgroundColor.g,
Kirigami.Theme.backgroundColor.b, 0.92)
} }
FocusScope { FocusScope {

View file

@ -155,9 +155,7 @@ Item {
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.colorSet: Kirigami.Theme.Window
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), 0.96)
Kirigami.Theme.backgroundColor.g,
Kirigami.Theme.backgroundColor.b, 0.96)
// Subtle left border // Subtle left border
Rectangle { Rectangle {

View file

@ -251,7 +251,7 @@ ContainmentItem {
// Convergence: no scrim (popup has own background); mobile: dark scrim // Convergence: no scrim (popup has own background); mobile: dark scrim
color: ShellSettings.Settings.convergenceModeEnabled color: ShellSettings.Settings.convergenceModeEnabled
? "transparent" ? "transparent"
: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.46) : MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.46)
opacity: folio.HomeScreenState.appDrawerOpenProgress opacity: folio.HomeScreenState.appDrawerOpenProgress
} }
@ -261,7 +261,7 @@ ContainmentItem {
anchors.fill: parent anchors.fill: parent
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.colorSet: Kirigami.Theme.Window
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.30) color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.30)
opacity: folio.HomeScreenState.searchWidgetOpenProgress opacity: folio.HomeScreenState.searchWidgetOpenProgress
} }
@ -271,7 +271,7 @@ ContainmentItem {
anchors.fill: parent anchors.fill: parent
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.colorSet: Kirigami.Theme.Window
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.30) color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.30)
opacity: folio.HomeScreenState.settingsOpenProgress opacity: folio.HomeScreenState.settingsOpenProgress
} }
@ -348,7 +348,7 @@ ContainmentItem {
readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength
readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength
readonly property real rightFrameBulgeTangent: rightFrameBulgeHalfLength * 0.55 readonly property real rightFrameBulgeTangent: rightFrameBulgeHalfLength * 0.55
readonly property color chromeColor: Kirigami.Theme.backgroundColor readonly property color chromeColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
readonly property color edgeColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1) readonly property color edgeColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
readonly property int dockAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault) readonly property int dockAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
@ -1430,7 +1430,7 @@ ContainmentItem {
id: drawerSurfacePath id: drawerSurfacePath
readonly property real cornerRadius: drawerSurface.cornerRadius readonly property real cornerRadius: drawerSurface.cornerRadius
fillColor: Kirigami.Theme.backgroundColor fillColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
strokeWidth: 0 strokeWidth: 0
startX: 0 startX: 0
startY: 0 startY: 0