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/Constants.qml
qml/components/Motion.qml
qml/components/SurfaceColors.qml
qml/dataproviders/AudioInfo.qml
qml/dataproviders/BatteryInfo.qml
qml/dataproviders/BluetoothInfo.qml
@ -60,6 +61,7 @@ ecm_target_qml_sources(mobileshellplugin SOURCES
qml/components/ScreenEdgeDragEffect.qml
qml/components/StartupFeedbackPanelFill.qml
qml/components/StartupFeedbackWindows.qml
qml/components/SurfaceColors.qml
qml/components/TextDropShadow.qml
qml/components/VelocityCalculator.qml

View file

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

View file

@ -28,6 +28,7 @@ QQC2.Popup {
padding: Kirigami.Units.smallSpacing
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 Item __currentItem: null
@ -81,11 +82,11 @@ QQC2.Popup {
}
background: Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
color: popup.surfaceColor
radius: Kirigami.Units.cornerRadius
border.color: Kirigami.ColorUtils.linearInterpolation(
Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2)
popup.surfaceColor, Kirigami.Theme.textColor, 0.2)
border.width: 1
shadow.size: Kirigami.Units.gridUnit

View file

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

View file

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

View file

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

View file

@ -48,15 +48,24 @@ Item {
// adjust color depending on panel type
property color panelColor: {
let tintPercent
let accentTintDark
let accentTintLight
if (panelType === PanelBackground.PanelType.Popup) {
tintPercent = 0.035
accentTintDark = 0.16
accentTintLight = 0.08
} else if (panelType === PanelBackground.PanelType.Base || panelType === PanelBackground.PanelType.Stacked || panelType === PanelBackground.PanelType.Flat) {
tintPercent = 0
accentTintDark = 0.18
accentTintLight = 0.10
} else {
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
// 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.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"
Behavior on color {
MobileShell.MotionColorAnimation {

View file

@ -34,7 +34,7 @@ Window {
LayerShell.Window.layer: LayerShell.Window.LayerOverlay
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)
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)
}
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) {
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
@ -756,7 +757,7 @@ MouseArea {
PC3.Label {
anchors.centerIn: parent
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.bold: leftDesktopBtn.isCurrent
}
@ -1487,7 +1488,7 @@ MouseArea {
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.backgroundColor
color: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
border.color: Qt.rgba(
Kirigami.Theme.textColor.r,
Kirigami.Theme.textColor.g,
@ -1544,9 +1545,7 @@ MouseArea {
anchors.fill: parent
radius: Kirigami.Units.cornerRadius
color: thumbEntry.containsMouse
? Qt.rgba(Kirigami.Theme.highlightColor.r,
Kirigami.Theme.highlightColor.g,
Kirigami.Theme.highlightColor.b, 0.15)
? MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accent(), 0.15)
: "transparent"
}
@ -1790,7 +1789,7 @@ MouseArea {
width: Kirigami.Units.smallSpacing * 3
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
radius: height / 2
color: Kirigami.Theme.highlightColor
color: MobileShell.SurfaceColors.accent()
opacity: taskDelegate.dynamicTilingMaximized ? 0.95 : 0
Behavior on opacity {
@ -1861,7 +1860,7 @@ MouseArea {
width: taskDelegate.model.IsActive === true ? Kirigami.Units.smallSpacing * 3 : Kirigami.Units.smallSpacing * 1.5
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
radius: height / 2
color: Kirigami.Theme.highlightColor
color: MobileShell.SurfaceColors.accent()
opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.45
Behavior on width {

View file

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

View file

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

View file

@ -251,7 +251,7 @@ ContainmentItem {
// Convergence: no scrim (popup has own background); mobile: dark scrim
color: ShellSettings.Settings.convergenceModeEnabled
? "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
}
@ -261,7 +261,7 @@ ContainmentItem {
anchors.fill: parent
Kirigami.Theme.inherit: false
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
}
@ -271,7 +271,7 @@ ContainmentItem {
anchors.fill: parent
Kirigami.Theme.inherit: false
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
}
@ -348,7 +348,7 @@ ContainmentItem {
readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength
readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength
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 int dockAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
@ -1430,7 +1430,7 @@ ContainmentItem {
id: drawerSurfacePath
readonly property real cornerRadius: drawerSurface.cornerRadius
fillColor: Kirigami.Theme.backgroundColor
fillColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
strokeWidth: 0
startX: 0
startY: 0