mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-08 23:02:05 -04:00
feat: Implement M3 design elevation & shadow effects
- Added global toggles in the Themes tab - Light color & directional user ovverides - Independent shadow overrides per/bar - Refactored various components to sync the updated designs
This commit is contained in:
54
quickshell/Common/ElevationShadow.qml
Normal file
54
quickshell/Common/ElevationShadow.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var level: Theme.elevationLevel2
|
||||
property string direction: Theme.elevationLightDirection
|
||||
property real fallbackOffset: 4
|
||||
|
||||
property color targetColor: "white"
|
||||
property real targetRadius: Theme.cornerRadius
|
||||
property color borderColor: "transparent"
|
||||
property real borderWidth: 0
|
||||
|
||||
property bool shadowEnabled: Theme.elevationEnabled
|
||||
property real shadowBlurPx: level && level.blurPx !== undefined ? level.blurPx : 0
|
||||
property real shadowSpreadPx: level && level.spreadPx !== undefined ? level.spreadPx : 0
|
||||
property real shadowOffsetX: Theme.elevationOffsetXFor(level, direction, fallbackOffset)
|
||||
property real shadowOffsetY: Theme.elevationOffsetYFor(level, direction, fallbackOffset)
|
||||
property color shadowColor: Theme.elevationShadowColor(level)
|
||||
property real shadowOpacity: 1
|
||||
property real blurMax: Theme.elevationBlurMax
|
||||
|
||||
property alias sourceRect: sourceRect
|
||||
|
||||
layer.enabled: shadowEnabled
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
autoPaddingEnabled: true
|
||||
shadowEnabled: true
|
||||
blurEnabled: false
|
||||
maskEnabled: false
|
||||
shadowBlur: Math.max(0, Math.min(1, root.shadowBlurPx / Math.max(1, root.blurMax)))
|
||||
shadowScale: 1 + (2 * root.shadowSpreadPx) / Math.max(1, Math.min(root.width, root.height))
|
||||
shadowHorizontalOffset: root.shadowOffsetX
|
||||
shadowVerticalOffset: root.shadowOffsetY
|
||||
blurMax: root.blurMax
|
||||
shadowColor: root.shadowColor
|
||||
shadowOpacity: root.shadowOpacity
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: sourceRect
|
||||
anchors.fill: parent
|
||||
radius: root.targetRadius
|
||||
color: root.targetColor
|
||||
border.color: root.borderColor
|
||||
border.width: root.borderWidth
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import "settings/SettingsStore.js" as Store
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property int settingsConfigVersion: 5
|
||||
readonly property int settingsConfigVersion: 6
|
||||
|
||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||
|
||||
@@ -166,6 +166,24 @@ Singleton {
|
||||
property int modalCustomAnimationDuration: 150
|
||||
property bool enableRippleEffects: true
|
||||
onEnableRippleEffectsChanged: saveSettings()
|
||||
property bool m3ElevationEnabled: true
|
||||
onM3ElevationEnabledChanged: saveSettings()
|
||||
property int m3ElevationIntensity: 12
|
||||
onM3ElevationIntensityChanged: saveSettings()
|
||||
property int m3ElevationOpacity: 30
|
||||
onM3ElevationOpacityChanged: saveSettings()
|
||||
property string m3ElevationColorMode: "default"
|
||||
onM3ElevationColorModeChanged: saveSettings()
|
||||
property string m3ElevationLightDirection: "top"
|
||||
onM3ElevationLightDirectionChanged: saveSettings()
|
||||
property string m3ElevationCustomColor: "#000000"
|
||||
onM3ElevationCustomColorChanged: saveSettings()
|
||||
property bool modalElevationEnabled: true
|
||||
onModalElevationEnabledChanged: saveSettings()
|
||||
property bool popoutElevationEnabled: true
|
||||
onPopoutElevationEnabledChanged: saveSettings()
|
||||
property bool barElevationEnabled: true
|
||||
onBarElevationEnabledChanged: saveSettings()
|
||||
property string wallpaperFillMode: "Fill"
|
||||
property bool blurredWallpaperLayer: false
|
||||
property bool blurWallpaperOnOverview: false
|
||||
@@ -609,7 +627,7 @@ Singleton {
|
||||
"scrollYBehavior": "workspace",
|
||||
"shadowIntensity": 0,
|
||||
"shadowOpacity": 60,
|
||||
"shadowColorMode": "text",
|
||||
"shadowColorMode": "default",
|
||||
"shadowCustomColor": "#000000",
|
||||
"clickThrough": false
|
||||
}
|
||||
|
||||
@@ -673,6 +673,232 @@ Singleton {
|
||||
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
||||
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
|
||||
|
||||
readonly property bool elevationEnabled: typeof SettingsData !== "undefined" && (SettingsData.m3ElevationEnabled ?? true)
|
||||
readonly property real elevationBlurMax: typeof SettingsData !== "undefined" && SettingsData.m3ElevationIntensity !== undefined ? Math.min(128, Math.max(32, SettingsData.m3ElevationIntensity * 2)) : 64
|
||||
|
||||
readonly property real _elevMult: typeof SettingsData !== "undefined" && SettingsData.m3ElevationIntensity !== undefined ? SettingsData.m3ElevationIntensity / 12 : 1
|
||||
readonly property real _opMult: typeof SettingsData !== "undefined" && SettingsData.m3ElevationOpacity !== undefined ? SettingsData.m3ElevationOpacity / 60 : 1
|
||||
function normalizeElevationDirection(direction) {
|
||||
switch (direction) {
|
||||
case "top":
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
case "bottom":
|
||||
case "bottomLeft":
|
||||
case "bottomRight":
|
||||
case "left":
|
||||
case "right":
|
||||
case "autoBar":
|
||||
return direction;
|
||||
default:
|
||||
return "top";
|
||||
}
|
||||
}
|
||||
|
||||
readonly property string elevationLightDirection: {
|
||||
if (typeof SettingsData === "undefined" || !SettingsData.m3ElevationLightDirection)
|
||||
return "top";
|
||||
switch (SettingsData.m3ElevationLightDirection) {
|
||||
case "autoBar":
|
||||
case "top":
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
case "bottom":
|
||||
return SettingsData.m3ElevationLightDirection;
|
||||
default:
|
||||
return "top";
|
||||
}
|
||||
}
|
||||
readonly property real _elevDiagRatio: 0.55
|
||||
readonly property string _globalElevationDirForTokens: {
|
||||
const normalized = normalizeElevationDirection(elevationLightDirection);
|
||||
return normalized === "autoBar" ? "top" : normalized;
|
||||
}
|
||||
readonly property real _elevDirX: {
|
||||
switch (_globalElevationDirForTokens) {
|
||||
case "topLeft":
|
||||
case "bottomLeft":
|
||||
case "left":
|
||||
return 1;
|
||||
case "topRight":
|
||||
case "bottomRight":
|
||||
case "right":
|
||||
return -1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real _elevDirY: {
|
||||
switch (_globalElevationDirForTokens) {
|
||||
case "bottom":
|
||||
case "bottomLeft":
|
||||
case "bottomRight":
|
||||
return -1;
|
||||
case "left":
|
||||
case "right":
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
readonly property real _elevDirXScale: (_globalElevationDirForTokens === "left" || _globalElevationDirForTokens === "right") ? 1 : _elevDiagRatio
|
||||
|
||||
readonly property var elevationLevel1: ({
|
||||
blurPx: 4 * _elevMult,
|
||||
offsetX: 1 * _elevMult * _elevDirXScale * _elevDirX,
|
||||
offsetY: 1 * _elevMult * _elevDirY,
|
||||
spreadPx: 0,
|
||||
alpha: 0.2 * _opMult
|
||||
})
|
||||
readonly property var elevationLevel2: ({
|
||||
blurPx: 8 * _elevMult,
|
||||
offsetX: 4 * _elevMult * _elevDirXScale * _elevDirX,
|
||||
offsetY: 4 * _elevMult * _elevDirY,
|
||||
spreadPx: 0,
|
||||
alpha: 0.25 * _opMult
|
||||
})
|
||||
readonly property var elevationLevel3: ({
|
||||
blurPx: 12 * _elevMult,
|
||||
offsetX: 6 * _elevMult * _elevDirXScale * _elevDirX,
|
||||
offsetY: 6 * _elevMult * _elevDirY,
|
||||
spreadPx: 0,
|
||||
alpha: 0.3 * _opMult
|
||||
})
|
||||
readonly property var elevationLevel4: ({
|
||||
blurPx: 16 * _elevMult,
|
||||
offsetX: 8 * _elevMult * _elevDirXScale * _elevDirX,
|
||||
offsetY: 8 * _elevMult * _elevDirY,
|
||||
spreadPx: 0,
|
||||
alpha: 0.3 * _opMult
|
||||
})
|
||||
readonly property var elevationLevel5: ({
|
||||
blurPx: 20 * _elevMult,
|
||||
offsetX: 10 * _elevMult * _elevDirXScale * _elevDirX,
|
||||
offsetY: 10 * _elevMult * _elevDirY,
|
||||
spreadPx: 0,
|
||||
alpha: 0.3 * _opMult
|
||||
})
|
||||
|
||||
function elevationOffsetMagnitude(level, fallback, direction) {
|
||||
if (!level) {
|
||||
return fallback !== undefined ? Math.abs(fallback) : 0;
|
||||
}
|
||||
const yMag = Math.abs(level.offsetY !== undefined ? level.offsetY : 0);
|
||||
if (yMag > 0)
|
||||
return yMag;
|
||||
const xMag = Math.abs(level.offsetX !== undefined ? level.offsetX : 0);
|
||||
if (xMag > 0) {
|
||||
if (direction === "left" || direction === "right")
|
||||
return xMag;
|
||||
return xMag / _elevDiagRatio;
|
||||
}
|
||||
return fallback !== undefined ? Math.abs(fallback) : 0;
|
||||
}
|
||||
|
||||
function elevationOffsetXFor(level, direction, fallback) {
|
||||
const dir = normalizeElevationDirection(direction || elevationLightDirection);
|
||||
const mag = elevationOffsetMagnitude(level, fallback, dir);
|
||||
switch (dir) {
|
||||
case "topLeft":
|
||||
case "bottomLeft":
|
||||
return mag * _elevDiagRatio;
|
||||
case "topRight":
|
||||
case "bottomRight":
|
||||
return -mag * _elevDiagRatio;
|
||||
case "left":
|
||||
return mag;
|
||||
case "right":
|
||||
return -mag;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function elevationOffsetYFor(level, direction, fallback) {
|
||||
const dir = normalizeElevationDirection(direction || elevationLightDirection);
|
||||
const mag = elevationOffsetMagnitude(level, fallback, dir);
|
||||
switch (dir) {
|
||||
case "bottom":
|
||||
case "bottomLeft":
|
||||
case "bottomRight":
|
||||
return -mag;
|
||||
case "left":
|
||||
case "right":
|
||||
return 0;
|
||||
default:
|
||||
return mag;
|
||||
}
|
||||
}
|
||||
|
||||
function elevationOffsetX(level, fallback) {
|
||||
return elevationOffsetXFor(level, elevationLightDirection, fallback);
|
||||
}
|
||||
|
||||
function elevationOffsetY(level, fallback) {
|
||||
return elevationOffsetYFor(level, elevationLightDirection, fallback);
|
||||
}
|
||||
|
||||
function elevationRenderPadding(level, direction, fallbackOffset, extraPadding, minPadding) {
|
||||
const dir = direction !== undefined ? direction : elevationLightDirection;
|
||||
const blur = (level && level.blurPx !== undefined) ? Math.max(0, level.blurPx) : 0;
|
||||
const spread = (level && level.spreadPx !== undefined) ? Math.max(0, level.spreadPx) : 0;
|
||||
const fallback = fallbackOffset !== undefined ? fallbackOffset : 0;
|
||||
const extra = extraPadding !== undefined ? extraPadding : 8;
|
||||
const minPad = minPadding !== undefined ? minPadding : 16;
|
||||
const offsetX = Math.abs(elevationOffsetXFor(level, dir, fallback));
|
||||
const offsetY = Math.abs(elevationOffsetYFor(level, dir, fallback));
|
||||
return Math.max(minPad, blur + spread + Math.max(offsetX, offsetY) + extra);
|
||||
}
|
||||
|
||||
function elevationShadowColor(level) {
|
||||
const alpha = (level && level.alpha !== undefined) ? level.alpha : 0.3;
|
||||
let r = 0;
|
||||
let g = 0;
|
||||
let b = 0;
|
||||
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
const mode = SettingsData.m3ElevationColorMode || "default";
|
||||
if (mode === "default") {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
} else if (mode === "text") {
|
||||
r = surfaceText.r;
|
||||
g = surfaceText.g;
|
||||
b = surfaceText.b;
|
||||
} else if (mode === "primary") {
|
||||
r = primary.r;
|
||||
g = primary.g;
|
||||
b = primary.b;
|
||||
} else if (mode === "surfaceVariant") {
|
||||
r = surfaceVariant.r;
|
||||
g = surfaceVariant.g;
|
||||
b = surfaceVariant.b;
|
||||
} else if (mode === "custom" && SettingsData.m3ElevationCustomColor) {
|
||||
const c = Qt.color(SettingsData.m3ElevationCustomColor);
|
||||
r = c.r;
|
||||
g = c.g;
|
||||
b = c.b;
|
||||
}
|
||||
}
|
||||
return Qt.rgba(r, g, b, alpha);
|
||||
}
|
||||
function elevationTintOpacity(level) {
|
||||
if (!level)
|
||||
return 0;
|
||||
if (level === elevationLevel1)
|
||||
return 0.05;
|
||||
if (level === elevationLevel2)
|
||||
return 0.08;
|
||||
if (level === elevationLevel3)
|
||||
return 0.11;
|
||||
if (level === elevationLevel4)
|
||||
return 0.12;
|
||||
if (level === elevationLevel5)
|
||||
return 0.14;
|
||||
return 0.08;
|
||||
}
|
||||
|
||||
readonly property var animationDurations: [
|
||||
{
|
||||
"shorter": 0,
|
||||
|
||||
@@ -21,7 +21,7 @@ var SPEC = {
|
||||
widgetColorMode: { def: "default" },
|
||||
controlCenterTileColorMode: { def: "primary" },
|
||||
buttonColorMode: { def: "primary" },
|
||||
cornerRadius: { def: 12, onChange: "updateCompositorLayout" },
|
||||
cornerRadius: { def: 16, onChange: "updateCompositorLayout" },
|
||||
niriLayoutGapsOverride: { def: -1, onChange: "updateCompositorLayout" },
|
||||
niriLayoutRadiusOverride: { def: -1, onChange: "updateCompositorLayout" },
|
||||
niriLayoutBorderSize: { def: -1, onChange: "updateCompositorLayout" },
|
||||
@@ -47,6 +47,15 @@ var SPEC = {
|
||||
modalAnimationSpeed: { def: 1 },
|
||||
modalCustomAnimationDuration: { def: 150 },
|
||||
enableRippleEffects: { def: true },
|
||||
m3ElevationEnabled: { def: true },
|
||||
m3ElevationIntensity: { def: 12 },
|
||||
m3ElevationOpacity: { def: 30 },
|
||||
m3ElevationColorMode: { def: "default" },
|
||||
m3ElevationLightDirection: { def: "top" },
|
||||
m3ElevationCustomColor: { def: "#000000" },
|
||||
modalElevationEnabled: { def: true },
|
||||
popoutElevationEnabled: { def: true },
|
||||
barElevationEnabled: { def: true },
|
||||
wallpaperFillMode: { def: "Fill" },
|
||||
blurredWallpaperLayer: { def: false },
|
||||
blurWallpaperOnOverview: { def: false },
|
||||
@@ -432,7 +441,7 @@ var SPEC = {
|
||||
scrollYBehavior: "workspace",
|
||||
shadowIntensity: 0,
|
||||
shadowOpacity: 60,
|
||||
shadowColorMode: "text",
|
||||
shadowColorMode: "default",
|
||||
shadowCustomColor: "#000000",
|
||||
clickThrough: false
|
||||
}], onChange: "updateBarConfigs"
|
||||
|
||||
@@ -229,6 +229,25 @@ function migrateToVersion(obj, targetVersion) {
|
||||
settings.configVersion = 5;
|
||||
}
|
||||
|
||||
if (currentVersion < 6) {
|
||||
console.info("Migrating settings from version", currentVersion, "to version 6");
|
||||
|
||||
if (settings.barElevationEnabled === undefined) {
|
||||
var legacyBars = Array.isArray(settings.barConfigs) ? settings.barConfigs : [];
|
||||
var hadLegacyBarShadowEnabled = false;
|
||||
for (var j = 0; j < legacyBars.length; j++) {
|
||||
var legacyIntensity = Number(legacyBars[j] && legacyBars[j].shadowIntensity);
|
||||
if (!isNaN(legacyIntensity) && legacyIntensity > 0) {
|
||||
hadLegacyBarShadowEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
settings.barElevationEnabled = hadLegacyBarShadowEnabled;
|
||||
}
|
||||
|
||||
settings.configVersion = 6;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user