1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 04:42: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:
purian23
2026-03-01 00:54:31 -05:00
parent cf4c4b7d69
commit f0fcc77bdb
37 changed files with 1599 additions and 653 deletions

View File

@@ -98,7 +98,7 @@ Item {
sourceComponent: IconImage {
anchors.fill: parent
source: root.iconPath
backer.sourceSize: Qt.size(root.iconSize, root.iconSize)
backer.sourceSize: Qt.size(root.iconSize * 2, root.iconSize * 2)
mipmap: true
asynchronous: true
visible: status === Image.Ready

View File

@@ -81,6 +81,8 @@ Rectangle {
mipmap: true
cache: true
visible: false
sourceSize.width: Math.max(width * 2, 128)
sourceSize.height: Math.max(height * 2, 128)
source: !root.shouldProbe ? root.imageSource : ""
}

View File

@@ -1,7 +1,6 @@
import "../Common/fzf.js" as Fzf
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Widgets
@@ -254,6 +253,8 @@ Item {
}
contentItem: Rectangle {
id: contentSurface
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 1)
@@ -261,12 +262,17 @@ Item {
border.width: 2
radius: Theme.cornerRadius
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
shadowBlur: 0.4
shadowColor: Theme.shadowStrong
shadowVerticalOffset: 4
ElevationShadow {
id: shadowLayer
anchors.fill: parent
z: -1
level: Theme.elevationLevel2
fallbackOffset: 4
targetRadius: contentSurface.radius
targetColor: contentSurface.color
borderColor: contentSurface.border.color
borderWidth: contentSurface.border.width
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
}
Column {

View File

@@ -1,6 +1,5 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import qs.Common
import qs.Widgets
@@ -132,16 +131,20 @@ Rectangle {
}
contentItem: Rectangle {
id: contentSurface
color: Theme.surface
radius: Theme.cornerRadius
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
shadowColor: Theme.shadowStrong
shadowBlur: 0.8
shadowHorizontalOffset: 0
shadowVerticalOffset: 4
ElevationShadow {
id: shadowLayer
anchors.fill: parent
z: -1
level: Theme.elevationLevel2
fallbackOffset: 4
targetRadius: contentSurface.radius
targetColor: contentSurface.color
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
shadowEnabled: Theme.elevationEnabled
}
Rectangle {

View File

@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Common
@@ -257,11 +256,7 @@ PanelWindow {
scale: shouldBeVisible ? 1 : 0.9
property bool childHovered: false
property real shadowBlurPx: 10
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0
Rectangle {
id: background
@@ -273,38 +268,20 @@ PanelWindow {
z: -1
}
Item {
ElevationShadow {
id: bgShadowLayer
anchors.fill: parent
visible: osdContainer.popupSurfaceAlpha >= 0.95
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.smooth: false
z: -1
level: Theme.elevationLevel3
fallbackOffset: 6
targetRadius: Theme.cornerRadius
targetColor: Theme.surfaceContainer
borderColor: Theme.outlineMedium
borderWidth: 1
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically
readonly property int blurMax: 64
layer.effect: MultiEffect {
id: shadowFx
autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax))
shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha);
}
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: Theme.surfaceContainer
border.color: Theme.outlineMedium
border.width: 1
}
}
MouseArea {

View File

@@ -1,5 +1,4 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Common
@@ -31,6 +30,7 @@ Item {
property bool backgroundInteractive: true
property bool contentHandlesKeys: false
property bool fullHeightSurface: false
property bool _primeContent: false
property bool _resizeActive: false
property real _surfaceMarginLeft: 0
property real _surfaceW: 0
@@ -77,6 +77,38 @@ Item {
property int effectiveBarPosition: 0
property real effectiveBarBottomGap: 0
readonly property string autoBarShadowDirection: {
const section = triggerSection || "center";
switch (effectiveBarPosition) {
case SettingsData.Position.Top:
if (section === "left")
return "topLeft";
if (section === "right")
return "topRight";
return "top";
case SettingsData.Position.Bottom:
if (section === "left")
return "bottomLeft";
if (section === "right")
return "bottomRight";
return "bottom";
case SettingsData.Position.Left:
if (section === "left")
return "topLeft";
if (section === "right")
return "bottomLeft";
return "left";
case SettingsData.Position.Right:
if (section === "left")
return "topRight";
if (section === "right")
return "bottomRight";
return "right";
default:
return "top";
}
}
readonly property string effectiveShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
// Snapshot mask geometry to prevent background damage on bar updates
property real _frozenMaskX: 0
@@ -89,6 +121,14 @@ Item {
effectiveBarBottomGap = bottomGap !== undefined ? bottomGap : 0;
}
function primeContent() {
_primeContent = true;
}
function clearPrimedContent() {
_primeContent = false;
}
function setTriggerPosition(x, y, width, section, targetScreen, barPosition, barThickness, barSpacing, barConfig) {
triggerX = x;
triggerY = y;
@@ -152,6 +192,7 @@ Item {
function close() {
shouldBeVisible = false;
_primeContent = false;
PopoutManager.popoutChanged();
closeTimer.restart();
}
@@ -197,7 +238,11 @@ Item {
readonly property real screenHeight: screen ? screen.height : 0
readonly property real dpr: screen ? screen.devicePixelRatio : 1
readonly property real shadowBuffer: 5
readonly property var shadowLevel: Theme.elevationLevel3
readonly property real shadowFallbackOffset: 6
readonly property real shadowRenderPadding: (Theme.elevationEnabled && SettingsData.popoutElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, effectiveShadowDirection, shadowFallbackOffset, 8, 16) : 0
readonly property real shadowMotionPadding: Math.max(0, animationOffset)
readonly property real shadowBuffer: Theme.snap(shadowRenderPadding + shadowMotionPadding, dpr)
readonly property real alignedWidth: Theme.px(popupWidth, dpr)
readonly property real alignedHeight: Theme.px(popupHeight, dpr)
@@ -257,29 +302,30 @@ Item {
}
})(), dpr)
readonly property real triggeringBarLeftExclusion: (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
readonly property real triggeringBarTopExclusion: (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
readonly property real triggeringBarRightExclusion: (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
readonly property real triggeringBarBottomExclusion: (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
readonly property real maskX: {
const triggeringBarX = (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? barWidth : 0;
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
return Math.max(triggeringBarX, adjacentLeftBar);
return Math.max(triggeringBarLeftExclusion, adjacentLeftBar);
}
readonly property real maskY: {
const triggeringBarY = (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? barHeight : 0;
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
return Math.max(triggeringBarY, adjacentTopBar);
return Math.max(triggeringBarTopExclusion, adjacentTopBar);
}
readonly property real maskWidth: {
const triggeringBarRight = (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? barWidth : 0;
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar);
const rightExclusion = Math.max(triggeringBarRightExclusion, adjacentRightBar);
return Math.max(100, screenWidth - maskX - rightExclusion);
}
readonly property real maskHeight: {
const triggeringBarBottom = (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? barHeight : 0;
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar);
const bottomExclusion = Math.max(triggeringBarBottomExclusion, adjacentBottomBar);
return Math.max(100, screenHeight - maskY - bottomExclusion);
}
@@ -395,7 +441,7 @@ Item {
implicitWidth: useBackgroundWindow ? root._surfaceW : 0
implicitHeight: (useBackgroundWindow && !_fullHeight) ? (root.alignedHeight + shadowBuffer * 2) : 0
mask: (useBackgroundWindow && _fullHeight) ? contentInputMask : null
mask: useBackgroundWindow ? contentInputMask : null
Region {
id: contentInputMask
@@ -405,10 +451,10 @@ Item {
Item {
id: contentMaskRect
visible: false
x: contentContainer.x - root.shadowBuffer
y: contentContainer.y - root.shadowBuffer
width: root.alignedWidth + root.shadowBuffer * 2
height: root.alignedHeight + root.shadowBuffer * 2
x: contentContainer.x
y: contentContainer.y
width: root.alignedWidth
height: root.alignedHeight
}
MouseArea {
@@ -480,42 +526,20 @@ Item {
}
}
Rectangle {
ElevationShadow {
id: shadowSource
anchors.centerIn: parent
width: parent.width
height: parent.height
radius: Theme.cornerRadius
color: "black"
visible: false
opacity: contentWrapper.opacity
scale: contentWrapper.scale
x: contentWrapper.x
y: contentWrapper.y
property real shadowBlurPx: 10
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
readonly property int blurMax: 64
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
layer.smooth: false
layer.effect: MultiEffect {
id: shadowFx
autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, shadowSource.shadowBlurPx / shadowSource.blurMax))
shadowScale: 1 + (2 * shadowSource.shadowSpreadPx) / Math.max(1, Math.min(shadowSource.width, shadowSource.height))
shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, shadowSource.effectiveShadowAlpha);
}
}
level: root.shadowLevel
direction: root.effectiveShadowDirection
fallbackOffset: root.shadowFallbackOffset
targetRadius: Theme.cornerRadius
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
}
Item {
@@ -546,13 +570,13 @@ Item {
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
border.color: Theme.outlineMedium
border.width: 1
border.width: 0
}
Loader {
id: contentLoader
anchors.fill: parent
active: shouldBeVisible || contentWindow.visible
active: root._primeContent || shouldBeVisible || contentWindow.visible
asynchronous: false
}
}