mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-07 06:12:08 -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:
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -53,15 +52,43 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real shadowIntensity: barConfig?.shadowIntensity ?? 0
|
||||
readonly property bool shadowEnabled: shadowIntensity > 0
|
||||
readonly property int blurMax: 64
|
||||
readonly property real shadowBlurPx: shadowIntensity * 0.2
|
||||
readonly property real shadowBlur: Math.max(0, Math.min(1, shadowBlurPx / blurMax))
|
||||
readonly property real shadowOpacity: (barConfig?.shadowOpacity ?? 60) / 100
|
||||
readonly property string shadowColorMode: barConfig?.shadowColorMode ?? "text"
|
||||
readonly property color shadowBaseColor: {
|
||||
switch (shadowColorMode) {
|
||||
// M3 elevation shadow — Level 2 baseline (navigation bar), with per-bar override support
|
||||
readonly property bool hasPerBarOverride: (barConfig?.shadowIntensity ?? 0) > 0
|
||||
readonly property var elevLevel: Theme.elevationLevel2
|
||||
readonly property bool shadowEnabled: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || hasPerBarOverride
|
||||
readonly property string autoBarShadowDirection: isTop ? "top" : (isBottom ? "bottom" : (isLeft ? "left" : (isRight ? "right" : "top")))
|
||||
readonly property string globalShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
|
||||
readonly property string perBarShadowDirectionMode: barConfig?.shadowDirectionMode ?? "inherit"
|
||||
readonly property string perBarManualShadowDirection: {
|
||||
switch (barConfig?.shadowDirection) {
|
||||
case "top":
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
case "bottom":
|
||||
return barConfig.shadowDirection;
|
||||
default:
|
||||
return "top";
|
||||
}
|
||||
}
|
||||
readonly property string effectiveShadowDirection: {
|
||||
if (!hasPerBarOverride)
|
||||
return globalShadowDirection;
|
||||
switch (perBarShadowDirectionMode) {
|
||||
case "autoBar":
|
||||
return autoBarShadowDirection;
|
||||
case "manual":
|
||||
return perBarManualShadowDirection === "autoBar" ? autoBarShadowDirection : perBarManualShadowDirection;
|
||||
default:
|
||||
return globalShadowDirection;
|
||||
}
|
||||
}
|
||||
|
||||
// Per-bar override values (when barConfig.shadowIntensity > 0)
|
||||
readonly property real overrideBlurPx: (barConfig?.shadowIntensity ?? 0) * 0.2
|
||||
readonly property real overrideOpacity: (barConfig?.shadowOpacity ?? 60) / 100
|
||||
readonly property string overrideColorMode: barConfig?.shadowColorMode ?? "default"
|
||||
readonly property color overrideBaseColor: {
|
||||
switch (overrideColorMode) {
|
||||
case "surface":
|
||||
return Theme.surface;
|
||||
case "primary":
|
||||
@@ -71,10 +98,16 @@ Item {
|
||||
case "custom":
|
||||
return barConfig?.shadowCustomColor ?? "#000000";
|
||||
default:
|
||||
return Theme.surfaceText;
|
||||
return "#000000";
|
||||
}
|
||||
}
|
||||
readonly property color shadowColor: Theme.withAlpha(shadowBaseColor, shadowOpacity * barWindow._backgroundAlpha)
|
||||
|
||||
// Resolved values — per-bar override wins if set, otherwise use global M3 elevation
|
||||
readonly property real shadowBlurPx: hasPerBarOverride ? overrideBlurPx : (elevLevel.blurPx ?? 8)
|
||||
readonly property color shadowColor: hasPerBarOverride ? Theme.withAlpha(overrideBaseColor, overrideOpacity) : Theme.elevationShadowColor(elevLevel)
|
||||
readonly property real shadowOffsetMagnitude: hasPerBarOverride ? (overrideBlurPx * 0.5) : Theme.elevationOffsetMagnitude(elevLevel, 4, effectiveShadowDirection)
|
||||
readonly property real shadowOffsetX: Theme.elevationOffsetXFor(hasPerBarOverride ? null : elevLevel, effectiveShadowDirection, shadowOffsetMagnitude)
|
||||
readonly property real shadowOffsetY: Theme.elevationOffsetYFor(hasPerBarOverride ? null : elevLevel, effectiveShadowDirection, shadowOffsetMagnitude)
|
||||
|
||||
readonly property string mainPath: generatePathForPosition(width, height)
|
||||
readonly property string borderFullPath: generateBorderFullPath(width, height)
|
||||
@@ -118,42 +151,28 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: shadowLoader
|
||||
anchors.fill: parent
|
||||
active: root.shadowEnabled && mainPathCorrectShape
|
||||
asynchronous: false
|
||||
sourceComponent: Item {
|
||||
anchors.fill: parent
|
||||
ElevationShadow {
|
||||
id: barShadow
|
||||
visible: root.shadowEnabled && root.width > 0 && root.height > 0
|
||||
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
layer.samples: 8
|
||||
layer.textureSize: Qt.size(Math.round(width * barWindow._dpr * 2), Math.round(height * barWindow._dpr * 2))
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowBlur: root.shadowBlur
|
||||
shadowColor: root.shadowColor
|
||||
shadowVerticalOffset: root.isTop ? root.shadowBlurPx * 0.5 : (root.isBottom ? -root.shadowBlurPx * 0.5 : 0)
|
||||
shadowHorizontalOffset: root.isLeft ? root.shadowBlurPx * 0.5 : (root.isRight ? -root.shadowBlurPx * 0.5 : 0)
|
||||
autoPaddingEnabled: true
|
||||
}
|
||||
// Size to the bar's rectangular body, excluding gothic wing extensions
|
||||
x: root.isRight ? root.wing : 0
|
||||
y: root.isBottom ? root.wing : 0
|
||||
width: axis.isVertical ? (parent.width - root.wing) : parent.width
|
||||
height: axis.isVertical ? parent.height : (parent.height - root.wing)
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
shadowEnabled: root.shadowEnabled
|
||||
level: root.hasPerBarOverride ? null : root.elevLevel
|
||||
direction: root.effectiveShadowDirection
|
||||
fallbackOffset: 4
|
||||
targetRadius: root.rt
|
||||
targetColor: barWindow._bgColor
|
||||
|
||||
ShapePath {
|
||||
fillColor: barWindow._bgColor
|
||||
strokeColor: "transparent"
|
||||
strokeWidth: 0
|
||||
|
||||
PathSvg {
|
||||
path: root.mainPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shadowBlurPx: root.shadowBlurPx
|
||||
shadowOffsetX: root.shadowOffsetX
|
||||
shadowOffsetY: root.shadowOffsetY
|
||||
shadowColor: root.shadowColor
|
||||
blurMax: Theme.elevationBlurMax
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
||||
@@ -140,6 +140,20 @@ PanelWindow {
|
||||
}
|
||||
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
|
||||
|
||||
// Shadow buffer: extra window space for shadow to render beyond bar bounds
|
||||
readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (barConfig?.shadowIntensity ?? 0) > 0
|
||||
readonly property real _shadowBuffer: {
|
||||
if (!_shadowActive)
|
||||
return 0;
|
||||
const hasOverride = (barConfig?.shadowIntensity ?? 0) > 0;
|
||||
if (hasOverride) {
|
||||
const blur = (barConfig.shadowIntensity ?? 0) * 0.2;
|
||||
const offset = blur * 0.5;
|
||||
return Theme.snap(Math.max(16, blur + offset + 8), _dpr);
|
||||
}
|
||||
return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr);
|
||||
}
|
||||
|
||||
property string screenName: modelData.name
|
||||
|
||||
property bool hasMaximizedToplevel: false
|
||||
@@ -354,8 +368,8 @@ PanelWindow {
|
||||
}
|
||||
|
||||
screen: modelData
|
||||
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) : 0
|
||||
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) : 0
|
||||
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||||
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||||
color: "transparent"
|
||||
|
||||
property var nativeInhibitor: null
|
||||
|
||||
@@ -178,8 +178,9 @@ BasePill {
|
||||
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||
const globalPos = parent.mapToItem(null, 0, 0);
|
||||
const currentScreen = root.parentScreen || Screen;
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width);
|
||||
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen);
|
||||
const barPosition = root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1));
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width, root.barSpacing, barPosition, root.barConfig);
|
||||
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen, barPosition, root.barThickness, root.barSpacing, root.barConfig);
|
||||
}
|
||||
root.clicked();
|
||||
}
|
||||
@@ -334,8 +335,9 @@ BasePill {
|
||||
if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
|
||||
const globalPos = mapToItem(null, 0, 0);
|
||||
const currentScreen = root.parentScreen || Screen;
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.width);
|
||||
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen);
|
||||
const barPosition = root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1));
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.width, root.barSpacing, barPosition, root.barConfig);
|
||||
root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen, barPosition, root.barThickness, root.barSpacing, root.barConfig);
|
||||
}
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
@@ -940,9 +940,10 @@ BasePill {
|
||||
}
|
||||
})(), overflowMenu.dpr)
|
||||
|
||||
property real shadowBlurPx: 10
|
||||
property real shadowSpreadPx: 0
|
||||
property real shadowBaseAlpha: 0.60
|
||||
readonly property var elev: Theme.elevationLevel2
|
||||
property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
|
||||
property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
|
||||
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25
|
||||
readonly property real popupSurfaceAlpha: Theme.popupTransparency
|
||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||
|
||||
@@ -963,37 +964,26 @@ BasePill {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
ElevationShadow {
|
||||
id: bgShadowLayer
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
level: menuContainer.elev
|
||||
fallbackOffset: 4
|
||||
shadowBlurPx: menuContainer.shadowBlurPx
|
||||
shadowSpreadPx: menuContainer.shadowSpreadPx
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
targetRadius: Theme.cornerRadius
|
||||
sourceRect.antialiasing: true
|
||||
sourceRect.smooth: true
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
layer.smooth: true
|
||||
layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
layer.samples: 4
|
||||
|
||||
readonly property int blurMax: 64
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
autoPaddingEnabled: true
|
||||
shadowEnabled: true
|
||||
blurEnabled: false
|
||||
maskEnabled: false
|
||||
shadowBlur: Math.max(0, Math.min(1, menuContainer.shadowBlurPx / bgShadowLayer.blurMax))
|
||||
shadowScale: 1 + (2 * menuContainer.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, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
antialiasing: true
|
||||
smooth: true
|
||||
}
|
||||
}
|
||||
|
||||
Grid {
|
||||
@@ -1412,9 +1402,10 @@ BasePill {
|
||||
}
|
||||
})(), menuWindow.dpr)
|
||||
|
||||
property real shadowBlurPx: 10
|
||||
property real shadowSpreadPx: 0
|
||||
property real shadowBaseAlpha: 0.60
|
||||
readonly property var elev: Theme.elevationLevel2
|
||||
property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
|
||||
property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
|
||||
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25
|
||||
readonly property real popupSurfaceAlpha: Theme.popupTransparency
|
||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||
|
||||
@@ -1435,35 +1426,24 @@ BasePill {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
ElevationShadow {
|
||||
id: menuBgShadowLayer
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
level: menuContainer.elev
|
||||
fallbackOffset: 4
|
||||
shadowBlurPx: menuContainer.shadowBlurPx
|
||||
shadowSpreadPx: menuContainer.shadowSpreadPx
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
targetRadius: Theme.cornerRadius
|
||||
sourceRect.antialiasing: true
|
||||
shadowEnabled: Theme.elevationEnabled
|
||||
layer.smooth: true
|
||||
layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
|
||||
readonly property int blurMax: 64
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
autoPaddingEnabled: true
|
||||
shadowEnabled: true
|
||||
blurEnabled: false
|
||||
maskEnabled: false
|
||||
shadowBlur: Math.max(0, Math.min(1, menuContainer.shadowBlurPx / menuBgShadowLayer.blurMax))
|
||||
shadowScale: 1 + (2 * menuContainer.shadowSpreadPx) / Math.max(1, Math.min(menuBgShadowLayer.width, menuBgShadowLayer.height))
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, menuContainer.effectiveShadowAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
radius: Theme.cornerRadius
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
|
||||
QsMenuAnchor {
|
||||
|
||||
@@ -177,8 +177,9 @@ BasePill {
|
||||
if (popoutTarget && popoutTarget.setTriggerPosition) {
|
||||
const globalPos = root.visualContent.mapToItem(null, 0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth);
|
||||
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen);
|
||||
const barPosition = root.axis?.edge === "left" ? 2 : (root.axis?.edge === "right" ? 3 : (root.axis?.edge === "top" ? 0 : 1));
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth, root.barSpacing, barPosition, root.barConfig);
|
||||
popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen, barPosition, barThickness, root.barSpacing, root.barConfig);
|
||||
}
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user