1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-03 02:52:07 -04:00

Compare commits

...

4 Commits

Author SHA1 Message Date
purian23
88b7889447 Core m3 spec complete 2026-02-27 10:10:28 -05:00
purian23
3c5d8ba66a Moar M3 iteration stuffs 2026-02-26 23:02:49 -05:00
purian23
972bc8397d theme: Continue to iterate m3 specs 2026-02-26 16:18:59 -05:00
purian23
788c1fa9e6 feat: Implement Material Design 3 elevation and shadow effects
- Added global toggle for enabling M3 elevation & shadow effects
- Refactored various components to sync updated specs
2026-02-26 01:09:21 -05:00
29 changed files with 915 additions and 410 deletions

View File

@@ -165,6 +165,22 @@ Singleton {
property int modalCustomAnimationDuration: 150 property int modalCustomAnimationDuration: 150
property bool enableRippleEffects: true property bool enableRippleEffects: true
onEnableRippleEffectsChanged: saveSettings() 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 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 string wallpaperFillMode: "Fill"
property bool blurredWallpaperLayer: false property bool blurredWallpaperLayer: false
property bool blurWallpaperOnOverview: false property bool blurWallpaperOnOverview: false
@@ -602,7 +618,7 @@ Singleton {
"scrollYBehavior": "workspace", "scrollYBehavior": "workspace",
"shadowIntensity": 0, "shadowIntensity": 0,
"shadowOpacity": 60, "shadowOpacity": 60,
"shadowColorMode": "text", "shadowColorMode": "default",
"shadowCustomColor": "#000000", "shadowCustomColor": "#000000",
"clickThrough": false "clickThrough": false
} }

View File

@@ -673,6 +673,92 @@ Singleton {
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08) property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3) 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
readonly property var elevationLevel1: ({
blurPx: 4 * _elevMult,
offsetY: 1 * _elevMult,
spreadPx: 0,
alpha: 0.2 * _opMult
})
readonly property var elevationLevel2: ({
blurPx: 8 * _elevMult,
offsetY: 4 * _elevMult,
spreadPx: 0,
alpha: 0.25 * _opMult
})
readonly property var elevationLevel3: ({
blurPx: 12 * _elevMult,
offsetY: 6 * _elevMult,
spreadPx: 0,
alpha: 0.3 * _opMult
})
readonly property var elevationLevel4: ({
blurPx: 16 * _elevMult,
offsetY: 8 * _elevMult,
spreadPx: 0,
alpha: 0.3 * _opMult
})
readonly property var elevationLevel5: ({
blurPx: 20 * _elevMult,
offsetY: 10 * _elevMult,
spreadPx: 0,
alpha: 0.3 * _opMult
})
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: [ readonly property var animationDurations: [
{ {
"shorter": 0, "shorter": 0,

View File

@@ -46,6 +46,14 @@ var SPEC = {
modalAnimationSpeed: { def: 1 }, modalAnimationSpeed: { def: 1 },
modalCustomAnimationDuration: { def: 150 }, modalCustomAnimationDuration: { def: 150 },
enableRippleEffects: { def: true }, enableRippleEffects: { def: true },
m3ElevationEnabled: { def: true },
m3ElevationIntensity: { def: 12 },
m3ElevationOpacity: { def: 30 },
m3ElevationColorMode: { def: "default" },
m3ElevationCustomColor: { def: "#000000" },
modalElevationEnabled: { def: true },
popoutElevationEnabled: { def: true },
barElevationEnabled: { def: true },
wallpaperFillMode: { def: "Fill" }, wallpaperFillMode: { def: "Fill" },
blurredWallpaperLayer: { def: false }, blurredWallpaperLayer: { def: false },
blurWallpaperOnOverview: { def: false }, blurWallpaperOnOverview: { def: false },
@@ -425,7 +433,7 @@ var SPEC = {
scrollYBehavior: "workspace", scrollYBehavior: "workspace",
shadowIntensity: 0, shadowIntensity: 0,
shadowOpacity: 60, shadowOpacity: 60,
shadowColorMode: "text", shadowColorMode: "default",
shadowCustomColor: "#000000", shadowCustomColor: "#000000",
clickThrough: false clickThrough: false
}], onChange: "updateBarConfigs" }], onChange: "updateBarConfigs"

View File

@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common import qs.Common
@@ -32,9 +33,9 @@ Item {
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
property color backgroundColor: Theme.surfaceContainer property color backgroundColor: Theme.surfaceContainer
property color borderColor: Theme.outlineMedium property color borderColor: Theme.outlineMedium
property real borderWidth: 1 property real borderWidth: 0
property real cornerRadius: Theme.cornerRadius property real cornerRadius: Theme.cornerRadius
property bool enableShadow: false property bool enableShadow: true
property alias modalFocusScope: focusScope property alias modalFocusScope: focusScope
property bool shouldBeVisible: false property bool shouldBeVisible: false
property bool shouldHaveFocus: shouldBeVisible property bool shouldHaveFocus: shouldBeVisible
@@ -142,7 +143,7 @@ Item {
} }
} }
readonly property real shadowBuffer: 5 readonly property real shadowBuffer: Theme.elevationBlurMax * 1.5 + 24
readonly property real alignedWidth: Theme.px(modalWidth, dpr) readonly property real alignedWidth: Theme.px(modalWidth, dpr)
readonly property real alignedHeight: Theme.px(modalHeight, dpr) readonly property real alignedHeight: Theme.px(modalHeight, dpr)
@@ -377,12 +378,34 @@ Item {
} }
} }
Rectangle { readonly property var elev: Theme.elevationLevel3
readonly property real shadowBlurNorm: Math.max(0, Math.min(1, (elev && elev.blurPx !== undefined ? elev.blurPx : 12) / Theme.elevationBlurMax))
Item {
id: modalShadowLayer
anchors.fill: parent anchors.fill: parent
color: root.backgroundColor layer.enabled: root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
border.color: root.borderColor
border.width: root.borderWidth layer.effect: MultiEffect {
radius: root.cornerRadius autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: animatedContent.shadowBlurNorm
shadowScale: 1
shadowVerticalOffset: animatedContent.elev && animatedContent.elev.offsetY !== undefined ? animatedContent.elev.offsetY : 6
shadowHorizontalOffset: 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3)
}
Rectangle {
anchors.fill: parent
radius: root.cornerRadius
color: root.backgroundColor
border.color: root.borderColor
border.width: root.borderWidth
}
} }
FocusScope { FocusScope {

View File

@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
@@ -75,7 +76,7 @@ Item {
return Theme.primary; return Theme.primary;
} }
} }
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 1 readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
signal dialogClosed signal dialogClosed
@@ -390,12 +391,30 @@ Item {
} }
} }
Rectangle { Item {
id: launcherShadowLayer
anchors.fill: parent anchors.fill: parent
color: root.backgroundColor layer.enabled: Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
border.color: root.borderColor layer.effect: MultiEffect {
border.width: root.borderWidth autoPaddingEnabled: true
radius: root.cornerRadius shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, Theme.elevationLevel3.blurPx / Theme.elevationBlurMax))
shadowScale: 1
shadowVerticalOffset: Theme.elevationLevel3.offsetY
shadowHorizontalOffset: 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3)
}
Rectangle {
anchors.fill: parent
color: root.backgroundColor
border.color: root.borderColor
border.width: root.borderWidth
radius: root.cornerRadius
}
} }
MouseArea { MouseArea {

View File

@@ -53,15 +53,19 @@ Item {
} }
} }
readonly property real shadowIntensity: barConfig?.shadowIntensity ?? 0 // M3 elevation shadow — Level 2 baseline (navigation bar), with per-bar override support
readonly property bool shadowEnabled: shadowIntensity > 0 readonly property bool hasPerBarOverride: (barConfig?.shadowIntensity ?? 0) > 0
readonly property int blurMax: 64 readonly property var elevLevel: Theme.elevationLevel2
readonly property real shadowBlurPx: shadowIntensity * 0.2 readonly property bool shadowEnabled: (Theme.elevationEnabled
readonly property real shadowBlur: Math.max(0, Math.min(1, shadowBlurPx / blurMax)) && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false))
readonly property real shadowOpacity: (barConfig?.shadowOpacity ?? 60) / 100 || hasPerBarOverride
readonly property string shadowColorMode: barConfig?.shadowColorMode ?? "text"
readonly property color shadowBaseColor: { // Per-bar override values (when barConfig.shadowIntensity > 0)
switch (shadowColorMode) { 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": case "surface":
return Theme.surface; return Theme.surface;
case "primary": case "primary":
@@ -71,10 +75,19 @@ Item {
case "custom": case "custom":
return barConfig?.shadowCustomColor ?? "#000000"; return barConfig?.shadowCustomColor ?? "#000000";
default: 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 real shadowBlur: Math.max(0, Math.min(1, shadowBlurPx / Theme.elevationBlurMax))
readonly property color shadowColor: hasPerBarOverride
? Theme.withAlpha(overrideBaseColor, overrideOpacity)
: Theme.elevationShadowColor(elevLevel)
readonly property real shadowOffsetY: hasPerBarOverride
? overrideBlurPx * 0.5
: (elevLevel.offsetY ?? 4)
readonly property string mainPath: generatePathForPosition(width, height) readonly property string mainPath: generatePathForPosition(width, height)
readonly property string borderFullPath: generateBorderFullPath(width, height) readonly property string borderFullPath: generateBorderFullPath(width, height)
@@ -121,7 +134,7 @@ Item {
Loader { Loader {
id: shadowLoader id: shadowLoader
anchors.fill: parent anchors.fill: parent
active: root.shadowEnabled && mainPathCorrectShape active: root.shadowEnabled && mainPathCorrectShape && Theme.elevationEnabled
asynchronous: false asynchronous: false
sourceComponent: Item { sourceComponent: Item {
anchors.fill: parent anchors.fill: parent
@@ -133,9 +146,10 @@ Item {
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
shadowBlur: root.shadowBlur shadowBlur: root.shadowBlur
blurMax: Theme.elevationBlurMax
shadowColor: root.shadowColor shadowColor: root.shadowColor
shadowVerticalOffset: root.isTop ? root.shadowBlurPx * 0.5 : (root.isBottom ? -root.shadowBlurPx * 0.5 : 0) shadowVerticalOffset: root.isTop ? root.shadowOffsetY : (root.isBottom ? -root.shadowOffsetY : 0)
shadowHorizontalOffset: root.isLeft ? root.shadowBlurPx * 0.5 : (root.isRight ? -root.shadowBlurPx * 0.5 : 0) shadowHorizontalOffset: root.isLeft ? root.shadowOffsetY : (root.isRight ? -root.shadowOffsetY : 0)
autoPaddingEnabled: true autoPaddingEnabled: true
} }

View File

@@ -940,9 +940,10 @@ BasePill {
} }
})(), overflowMenu.dpr) })(), overflowMenu.dpr)
property real shadowBlurPx: 10 readonly property var elev: Theme.elevationLevel2
property real shadowSpreadPx: 0 property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
property real shadowBaseAlpha: 0.60 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 popupSurfaceAlpha: Theme.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
@@ -966,7 +967,7 @@ BasePill {
Item { Item {
id: bgShadowLayer id: bgShadowLayer
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.smooth: true layer.smooth: true
layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2)) layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2))
layer.textureMirroring: ShaderEffectSource.MirrorVertically layer.textureMirroring: ShaderEffectSource.MirrorVertically
@@ -1412,9 +1413,10 @@ BasePill {
} }
})(), menuWindow.dpr) })(), menuWindow.dpr)
property real shadowBlurPx: 10 readonly property var elev: Theme.elevationLevel2
property real shadowSpreadPx: 0 property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8
property real shadowBaseAlpha: 0.60 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 popupSurfaceAlpha: Theme.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
@@ -1438,7 +1440,7 @@ BasePill {
Item { Item {
id: menuBgShadowLayer id: menuBgShadowLayer
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.smooth: true layer.smooth: true
layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr)) layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically layer.textureMirroring: ShaderEffectSource.MirrorVertically

View File

@@ -89,14 +89,15 @@ Item {
} }
} }
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 8 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowBlur: 1.0 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.4) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.7 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
} }
MouseArea { MouseArea {
@@ -223,14 +224,15 @@ Item {
} }
} }
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 8 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowBlur: 1.0 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.4) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.7 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
} }
Column { Column {
@@ -373,14 +375,15 @@ Item {
} }
} }
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 8 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowBlur: 1.0 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.4) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.7 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
} }
Column { Column {

View File

@@ -529,14 +529,15 @@ Item {
onClicked: activePlayer && activePlayer.togglePlaying() onClicked: activePlayer && activePlayer.togglePlaying()
} }
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 0 shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1
shadowBlur: 1.0 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.3) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.3 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2
} }
} }
} }

View File

@@ -241,14 +241,15 @@ Item {
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1
shadowBlur: 0.8 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.2) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.2 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2
} }
} }
@@ -812,14 +813,14 @@ Item {
x: (pos?.h ?? 0) * skyBox.effectiveWidth - (moonPhase.width / 2) + skyBox.hMargin x: (pos?.h ?? 0) * skyBox.effectiveWidth - (moonPhase.width / 2) + skyBox.hMargin
y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (moonPhase.height / 2) + skyBox.vMargin y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (moonPhase.height / 2) + skyBox.vMargin
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowBlur: 0.8 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.2) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.2 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
} }
} }
@@ -834,14 +835,14 @@ Item {
x: (pos?.h ?? 0) * skyBox.effectiveWidth - (sun.width / 2) + skyBox.hMargin x: (pos?.h ?? 0) * skyBox.effectiveWidth - (sun.width / 2) + skyBox.hMargin
y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (sun.height / 2) + skyBox.vMargin y: (pos?.v ?? 0) * -(skyBox.effectiveHeight / 2) + skyBox.effectiveHeight / 2 - (sun.height / 2) + skyBox.vMargin
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowBlur: 0.8 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.2) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.2 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
} }
} }
} }

View File

@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Effects
import Quickshell import Quickshell
import qs.Common import qs.Common
import qs.Services import qs.Services
@@ -30,7 +31,21 @@ Rectangle {
width: parent ? parent.width : 400 width: parent ? parent.width : 400
height: baseCardHeight + contentItem.extraHeight height: baseCardHeight + contentItem.extraHeight
radius: Theme.cornerRadius radius: Theme.cornerRadius
clip: true clip: false
layer.enabled: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.effect: MultiEffect {
autoPaddingEnabled: true
shadowEnabled: Theme.elevationEnabled
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, Theme.elevationLevel1.blurPx / Theme.elevationBlurMax))
shadowScale: 1
shadowVerticalOffset: Theme.elevationLevel1.offsetY
shadowHorizontalOffset: 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
}
color: { color: {
if (isSelected && keyboardNavigationActive) if (isSelected && keyboardNavigationActive)
@@ -49,7 +64,7 @@ Rectangle {
return 1.5; return 1.5;
if (historyItem.urgency === 2) if (historyItem.urgency === 2)
return 2; return 2;
return 1; return 0;
} }
Behavior on border.color { Behavior on border.color {

View File

@@ -264,7 +264,7 @@ Item {
width: ListView.view.width width: ListView.view.width
height: historyCard.height height: historyCard.height
clip: true clip: false
HistoryNotificationCard { HistoryNotificationCard {
id: historyCard id: historyCard

View File

@@ -1,5 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
import qs.Common import qs.Common
@@ -95,9 +96,23 @@ Rectangle {
if (notificationGroup?.latestNotification?.urgency === NotificationUrgency.Critical) { if (notificationGroup?.latestNotification?.urgency === NotificationUrgency.Critical) {
return 2; return 2;
} }
return 1; return 0;
}
clip: false
layer.enabled: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.effect: MultiEffect {
autoPaddingEnabled: true
shadowEnabled: Theme.elevationEnabled
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, Theme.elevationLevel1.blurPx / Theme.elevationBlurMax))
shadowScale: 1
shadowVerticalOffset: Theme.elevationLevel1.offsetY
shadowHorizontalOffset: 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
} }
clip: true
HoverHandler { HoverHandler {
id: cardHoverHandler id: cardHoverHandler
@@ -419,9 +434,7 @@ Rectangle {
id: delegateRect id: delegateRect
width: parent.width width: parent.width
readonly property bool isAdjacentToSwipe: root.swipingNotificationIndex !== -1 && readonly property bool isAdjacentToSwipe: root.swipingNotificationIndex !== -1 && (expandedDelegateWrapper.index === root.swipingNotificationIndex - 1 || expandedDelegateWrapper.index === root.swipingNotificationIndex + 1)
(expandedDelegateWrapper.index === root.swipingNotificationIndex - 1 ||
expandedDelegateWrapper.index === root.swipingNotificationIndex + 1)
readonly property real adjacentSwipeInfluence: isAdjacentToSwipe ? root.swipingNotificationOffset * 0.10 : 0 readonly property real adjacentSwipeInfluence: isAdjacentToSwipe ? root.swipingNotificationOffset * 0.10 : 0
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(root.swipingNotificationOffset) / width * 0.02 : 1.0 readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(root.swipingNotificationOffset) / width * 0.02 : 1.0

View File

@@ -185,8 +185,8 @@ PanelWindow {
anchors.top: true anchors.top: true
anchors.bottom: true anchors.bottom: true
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom anchors.left: true
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right anchors.right: true
mask: contentInputMask mask: contentInputMask
@@ -205,10 +205,10 @@ PanelWindow {
} }
margins { margins {
top: _storedTopMargin top: 0
bottom: _storedBottomMargin bottom: 0
left: getLeftMargin() left: 0
right: getRightMargin() right: 0
} }
function getBarInfo() { function getBarInfo() {
@@ -282,13 +282,29 @@ PanelWindow {
Item { Item {
id: content id: content
x: Theme.snap((win.width - alignedWidth) / 2, dpr) x: {
y: { const popupPos = SettingsData.notificationPopupPosition;
const isTop = isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left; const barLeft = getLeftMargin();
if (isTop) { const barRight = getRightMargin();
return Theme.snap(screenY, dpr);
if (isCenterPosition) {
return Theme.snap((screen.width - alignedWidth) / 2, dpr);
} else if (popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom) {
return Theme.snap(barLeft, dpr);
} else { } else {
return Theme.snap(win.height - alignedHeight - screenY, dpr); return Theme.snap(screen.width - alignedWidth - barRight, dpr);
}
}
y: {
const popupPos = SettingsData.notificationPopupPosition;
const barTop = getTopMargin();
const barBottom = getBottomMargin();
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
if (isTop) {
return Theme.snap(barTop, dpr);
} else {
return Theme.snap(screen.height - alignedHeight - barBottom, dpr);
} }
} }
width: alignedWidth width: alignedWidth
@@ -313,12 +329,12 @@ PanelWindow {
readonly property bool swipeActive: swipeDragHandler.active readonly property bool swipeActive: swipeDragHandler.active
property bool swipeDismissing: false property bool swipeDismissing: false
readonly property real radiusForShadow: Theme.cornerRadius readonly property bool shadowsAllowed: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled
property real shadowBlurPx: SettingsData.notificationPopupShadowEnabled ? ((2 + radiusForShadow * 0.2) * (cardHoverHandler.hovered ? 1.2 : 1)) : 0 readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3
property real shadowSpreadPx: SettingsData.notificationPopupShadowEnabled ? (radiusForShadow * (cardHoverHandler.hovered ? 0.06 : 0)) : 0 readonly property real cardInset: Theme.snap(4, win.dpr)
property real shadowBaseAlpha: 0.35 readonly property real shadowRenderPadding: shadowsAllowed ? Theme.snap(Theme.elevationBlurMax * 1.5 + 24, win.dpr) : 0
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency property real shadowBlurPx: shadowsAllowed ? (elevLevel && elevLevel.blurPx !== undefined ? elevLevel.blurPx : 12) : 0
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) property real shadowOffsetY: shadowsAllowed ? (elevLevel && elevLevel.offsetY !== undefined ? elevLevel.offsetY : 6) : 0
Behavior on shadowBlurPx { Behavior on shadowBlurPx {
NumberAnimation { NumberAnimation {
@@ -327,7 +343,7 @@ PanelWindow {
} }
} }
Behavior on shadowSpreadPx { Behavior on shadowOffsetY {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
@@ -337,31 +353,33 @@ PanelWindow {
Item { Item {
id: bgShadowLayer id: bgShadowLayer
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.snap(4, win.dpr) anchors.margins: -content.shadowRenderPadding
layer.enabled: !win._isDestroying && win.screenValid layer.enabled: !win._isDestroying && win.screenValid && content.shadowsAllowed
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr)) layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically layer.textureMirroring: ShaderEffectSource.MirrorVertically
readonly property int blurMax: 64 readonly property int blurMax: Theme.elevationBlurMax
layer.effect: MultiEffect { layer.effect: MultiEffect {
id: shadowFx id: shadowFx
autoPaddingEnabled: true autoPaddingEnabled: true
shadowEnabled: SettingsData.notificationPopupShadowEnabled shadowEnabled: content.shadowsAllowed
blurEnabled: false blurEnabled: false
maskEnabled: false maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, content.shadowBlurPx / bgShadowLayer.blurMax)) shadowBlur: Math.max(0, Math.min(1, content.shadowBlurPx / bgShadowLayer.blurMax))
shadowScale: 1 + (2 * content.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height)) shadowScale: 1
shadowColor: { shadowHorizontalOffset: 0
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest; shadowVerticalOffset: content.shadowOffsetY
return Theme.withAlpha(baseColor, content.effectiveShadowAlpha); blurMax: Theme.elevationBlurMax
} shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
} }
Rectangle { Rectangle {
id: shadowShapeSource id: shadowShapeSource
anchors.fill: parent x: content.shadowRenderPadding + content.cardInset
y: content.shadowRenderPadding + content.cardInset
width: Math.max(0, content.width - (content.cardInset * 2))
height: Math.max(0, content.height - (content.cardInset * 2))
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08) border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
@@ -369,7 +387,10 @@ PanelWindow {
} }
Rectangle { Rectangle {
anchors.fill: parent x: shadowShapeSource.x
y: shadowShapeSource.y
width: shadowShapeSource.width
height: shadowShapeSource.height
radius: shadowShapeSource.radius radius: shadowShapeSource.radius
visible: notificationData && notificationData.urgency === NotificationUrgency.Critical visible: notificationData && notificationData.urgency === NotificationUrgency.Critical
opacity: 1 opacity: 1
@@ -399,7 +420,7 @@ PanelWindow {
Item { Item {
id: backgroundContainer id: backgroundContainer
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.snap(4, win.dpr) anchors.margins: content.cardInset
clip: true clip: true
HoverHandler { HoverHandler {

View File

@@ -878,12 +878,14 @@ Item {
x: hoveredButton ? hoveredButton.mapToItem(aboutTab, hoveredButton.width / 2, 0).x - width / 2 : 0 x: hoveredButton ? hoveredButton.mapToItem(aboutTab, hoveredButton.width / 2, 0).x - width / 2 : 0
y: hoveredButton ? communityIcons.mapToItem(aboutTab, 0, 0).y - height - 8 : 0 y: hoveredButton ? communityIcons.mapToItem(aboutTab, 0, 0).y - height - 8 : 0
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowOpacity: 0.15 shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2
shadowVerticalOffset: 2 shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1
shadowBlur: 0.5 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
} }
StyledText { StyledText {

View File

@@ -52,9 +52,11 @@ Item {
} }
function _isBarActive(c) { function _isBarActive(c) {
if (!c.enabled) return false; if (!c.enabled)
return false;
const prefs = c.screenPreferences || ["all"]; const prefs = c.screenPreferences || ["all"];
if (prefs.length > 0) return true; if (prefs.length > 0)
return true;
return (c.showOnLastDisplay ?? true) && Quickshell.screens.length === 1; return (c.showOnLastDisplay ?? true) && Quickshell.screens.length === 1;
} }
@@ -64,7 +66,8 @@ Item {
return; return;
const hasHorizontal = configs.some(c => { const hasHorizontal = configs.some(c => {
if (!_isBarActive(c)) return false; if (!_isBarActive(c))
return false;
const p = c.position ?? SettingsData.Position.Top; const p = c.position ?? SettingsData.Position.Top;
return p === SettingsData.Position.Top || p === SettingsData.Position.Bottom; return p === SettingsData.Position.Top || p === SettingsData.Position.Bottom;
}); });
@@ -72,7 +75,8 @@ Item {
return; return;
const hasVertical = configs.some(c => { const hasVertical = configs.some(c => {
if (!_isBarActive(c)) return false; if (!_isBarActive(c))
return false;
const p = c.position ?? SettingsData.Position.Top; const p = c.position ?? SettingsData.Position.Top;
return p === SettingsData.Position.Left || p === SettingsData.Position.Right; return p === SettingsData.Position.Left || p === SettingsData.Position.Right;
}); });
@@ -136,7 +140,7 @@ Item {
scrollYBehavior: defaultBar.scrollYBehavior ?? "workspace", scrollYBehavior: defaultBar.scrollYBehavior ?? "workspace",
shadowIntensity: defaultBar.shadowIntensity ?? 0, shadowIntensity: defaultBar.shadowIntensity ?? 0,
shadowOpacity: defaultBar.shadowOpacity ?? 60, shadowOpacity: defaultBar.shadowOpacity ?? 60,
shadowColorMode: defaultBar.shadowColorMode ?? "text", shadowColorMode: defaultBar.shadowColorMode ?? "default",
shadowCustomColor: defaultBar.shadowCustomColor ?? "#000000" shadowCustomColor: defaultBar.shadowCustomColor ?? "#000000"
}; };
SettingsData.addBarConfig(newBar); SettingsData.addBarConfig(newBar);
@@ -1038,6 +1042,164 @@ Item {
} }
} }
SettingsCard {
id: shadowCard
iconName: "layers"
title: I18n.tr("Shadow Override", "bar shadow settings card")
settingKey: "barShadow"
collapsible: true
expanded: true
visible: selectedBarConfig?.enabled
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
StyledText {
width: parent.width
text: I18n.tr("Enable a custom override below to set per-bar shadow intensity, opacity, and color.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
SettingsToggleRow {
text: I18n.tr("Custom Shadow Override")
description: I18n.tr("Override the global shadow with per-bar settings")
checked: shadowCard.shadowActive
onToggled: checked => {
if (checked) {
SettingsData.updateBarConfig(selectedBarId, {
shadowIntensity: 12,
shadowOpacity: 60
});
} else {
SettingsData.updateBarConfig(selectedBarId, {
shadowIntensity: 0
});
}
}
}
SettingsSliderRow {
visible: shadowCard.shadowActive
text: I18n.tr("Intensity", "shadow intensity slider")
minimum: 0
maximum: 100
unit: "px"
defaultValue: 12
value: selectedBarConfig?.shadowIntensity ?? 0
onSliderValueChanged: newValue => SettingsData.updateBarConfig(selectedBarId, {
shadowIntensity: newValue
})
}
SettingsSliderRow {
visible: shadowCard.shadowActive
text: I18n.tr("Opacity")
minimum: 10
maximum: 100
unit: "%"
defaultValue: 60
value: selectedBarConfig?.shadowOpacity ?? 60
onSliderValueChanged: newValue => SettingsData.updateBarConfig(selectedBarId, {
shadowOpacity: newValue
})
}
Column {
visible: shadowCard.shadowActive
width: parent.width
spacing: Theme.spacingS
StyledText {
text: I18n.tr("Color")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
horizontalAlignment: Text.AlignLeft
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
}
Item {
width: parent.width
height: shadowColorGroup.implicitHeight
DankButtonGroup {
id: shadowColorGroup
anchors.horizontalCenter: parent.horizontalCenter
buttonPadding: parent.width < 420 ? Theme.spacingXS : Theme.spacingS
minButtonWidth: parent.width < 420 ? 36 : 56
textSize: parent.width < 420 ? Theme.fontSizeSmall : Theme.fontSizeMedium
model: [I18n.tr("Default (Black)"), I18n.tr("Surface", "shadow color option"), I18n.tr("Primary"), I18n.tr("Secondary"), I18n.tr("Custom")]
selectionMode: "single"
currentIndex: {
switch (selectedBarConfig?.shadowColorMode || "default") {
case "surface":
return 1;
case "primary":
return 2;
case "secondary":
return 3;
case "custom":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let mode = "default";
switch (index) {
case 1:
mode = "surface";
break;
case 2:
mode = "primary";
break;
case 3:
mode = "secondary";
break;
case 4:
mode = "custom";
break;
}
SettingsData.updateBarConfig(selectedBarId, {
shadowColorMode: mode
});
}
}
}
Rectangle {
visible: selectedBarConfig?.shadowColorMode === "custom"
width: 32
height: 32
radius: 16
color: selectedBarConfig?.shadowCustomColor ?? "#000000"
border.color: Theme.outline
border.width: 1
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
PopoutService.colorPickerModal.selectedColor = selectedBarConfig?.shadowCustomColor ?? "#000000";
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Color");
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
SettingsData.updateBarConfig(selectedBarId, {
shadowCustomColor: color.toString()
});
};
PopoutService.colorPickerModal.show();
}
}
}
}
}
SettingsCard { SettingsCard {
iconName: "rounded_corner" iconName: "rounded_corner"
title: I18n.tr("Corners & Background") title: I18n.tr("Corners & Background")
@@ -1140,134 +1302,6 @@ Item {
} }
} }
SettingsCard {
id: shadowCard
iconName: "layers"
title: I18n.tr("Shadow", "bar shadow settings card")
settingKey: "barShadow"
collapsible: true
expanded: false
visible: selectedBarConfig?.enabled
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "text") === "custom"
SettingsSliderRow {
text: I18n.tr("Intensity", "shadow intensity slider")
minimum: 0
maximum: 100
unit: "%"
value: selectedBarConfig?.shadowIntensity ?? 0
onSliderValueChanged: newValue => SettingsData.updateBarConfig(selectedBarId, {
shadowIntensity: newValue
})
}
SettingsSliderRow {
visible: shadowCard.shadowActive
text: I18n.tr("Opacity")
minimum: 10
maximum: 100
unit: "%"
value: selectedBarConfig?.shadowOpacity ?? 60
onSliderValueChanged: newValue => SettingsData.updateBarConfig(selectedBarId, {
shadowOpacity: newValue
})
}
Column {
visible: shadowCard.shadowActive
width: parent.width
spacing: Theme.spacingS
StyledText {
text: I18n.tr("Color")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
horizontalAlignment: Text.AlignLeft
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
}
Item {
width: parent.width
height: shadowColorGroup.implicitHeight
DankButtonGroup {
id: shadowColorGroup
anchors.horizontalCenter: parent.horizontalCenter
buttonPadding: parent.width < 420 ? Theme.spacingXS : Theme.spacingS
minButtonWidth: parent.width < 420 ? 36 : 56
textSize: parent.width < 420 ? Theme.fontSizeSmall : Theme.fontSizeMedium
model: [I18n.tr("Text", "shadow color option"), I18n.tr("Surface", "shadow color option"), I18n.tr("Primary"), I18n.tr("Secondary"), I18n.tr("Custom")]
selectionMode: "single"
currentIndex: {
switch (selectedBarConfig?.shadowColorMode || "text") {
case "surface":
return 1;
case "primary":
return 2;
case "secondary":
return 3;
case "custom":
return 4;
default:
return 0;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let mode = "text";
switch (index) {
case 1:
mode = "surface";
break;
case 2:
mode = "primary";
break;
case 3:
mode = "secondary";
break;
case 4:
mode = "custom";
break;
}
SettingsData.updateBarConfig(selectedBarId, {
shadowColorMode: mode
});
}
}
}
Rectangle {
visible: selectedBarConfig?.shadowColorMode === "custom"
width: 32
height: 32
radius: 16
color: selectedBarConfig?.shadowCustomColor ?? "#000000"
border.color: Theme.outline
border.width: 1
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
PopoutService.colorPickerModal.selectedColor = selectedBarConfig?.shadowCustomColor ?? "#000000";
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Color");
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
SettingsData.updateBarConfig(selectedBarId, {
shadowCustomColor: color.toString()
});
};
PopoutService.colorPickerModal.show();
}
}
}
}
}
SettingsToggleCard { SettingsToggleCard {
iconName: "border_style" iconName: "border_style"
title: I18n.tr("Border") title: I18n.tr("Border")

View File

@@ -274,7 +274,7 @@ Item {
settingKey: "notificationPopupShadowEnabled" settingKey: "notificationPopupShadowEnabled"
tags: ["notification", "popup", "shadow", "radius", "rounded"] tags: ["notification", "popup", "shadow", "radius", "rounded"]
text: I18n.tr("Popup Shadow") text: I18n.tr("Popup Shadow")
description: I18n.tr("Show drop shadow on notification popups") description: I18n.tr("Show drop shadow on notification popups. Requires M3 Elevation to be enabled in Theme & Colors.")
checked: SettingsData.notificationPopupShadowEnabled checked: SettingsData.notificationPopupShadowEnabled
onToggled: checked => SettingsData.set("notificationPopupShadowEnabled", checked) onToggled: checked => SettingsData.set("notificationPopupShadowEnabled", checked)
} }

View File

@@ -126,6 +126,15 @@ Item {
return Theme.warning; return Theme.warning;
} }
function openM3ShadowColorPicker() {
PopoutService.colorPickerModal.selectedColor = SettingsData.m3ElevationCustomColor ?? "#000000";
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Shadow Color");
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
SettingsData.set("m3ElevationCustomColor", color.toString());
};
PopoutService.colorPickerModal.show();
}
function formatThemeAutoTime(isoString) { function formatThemeAutoTime(isoString) {
if (!isoString) if (!isoString)
return ""; return "";
@@ -1592,6 +1601,152 @@ Item {
defaultValue: 12 defaultValue: 12
onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue) onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue)
} }
SettingsToggleRow {
tab: "theme"
tags: ["elevation", "shadow", "lift", "m3", "material"]
settingKey: "m3ElevationEnabled"
text: I18n.tr("Shadows")
description: I18n.tr("Material inspired shadows and elevation on modals, popouts, and dialogs")
checked: SettingsData.m3ElevationEnabled ?? true
onToggled: checked => SettingsData.set("m3ElevationEnabled", checked)
}
SettingsSliderRow {
tab: "theme"
tags: ["elevation", "shadow", "intensity", "blur", "m3"]
settingKey: "m3ElevationIntensity"
text: I18n.tr("Shadow Intensity")
description: I18n.tr("Controls the base blur radius and offset of shadows")
value: SettingsData.m3ElevationIntensity ?? 12
minimum: 0
maximum: 100
unit: "px"
defaultValue: 12
visible: SettingsData.m3ElevationEnabled ?? true
onSliderValueChanged: newValue => SettingsData.set("m3ElevationIntensity", newValue)
}
SettingsSliderRow {
tab: "theme"
tags: ["elevation", "shadow", "opacity", "transparency", "m3"]
settingKey: "m3ElevationOpacity"
text: I18n.tr("Shadow Opacity")
description: I18n.tr("Controls the transparency of the shadow")
value: SettingsData.m3ElevationOpacity ?? 30
minimum: 0
maximum: 100
unit: "%"
defaultValue: 30
visible: SettingsData.m3ElevationEnabled ?? true
onSliderValueChanged: newValue => SettingsData.set("m3ElevationOpacity", newValue)
}
SettingsDropdownRow {
tab: "theme"
tags: ["elevation", "shadow", "color", "m3"]
settingKey: "m3ElevationColorMode"
text: I18n.tr("Shadow Color")
description: I18n.tr("Base color for shadows (opacity is applied automatically)")
options: [I18n.tr("Default (Black)", "shadow color option"), I18n.tr("Text Color", "shadow color option"), I18n.tr("Primary", "shadow color option"), I18n.tr("Surface Variant", "shadow color option"), I18n.tr("Custom", "shadow color option")]
currentValue: {
switch (SettingsData.m3ElevationColorMode) {
case "text":
return I18n.tr("Text Color", "shadow color option");
case "primary":
return I18n.tr("Primary", "shadow color option");
case "surfaceVariant":
return I18n.tr("Surface Variant", "shadow color option");
case "custom":
return I18n.tr("Custom", "shadow color option");
default:
return I18n.tr("Default (Black)", "shadow color option");
}
}
visible: SettingsData.m3ElevationEnabled ?? true
onValueChanged: value => {
if (value === I18n.tr("Primary", "shadow color option")) {
SettingsData.set("m3ElevationColorMode", "primary");
} else if (value === I18n.tr("Surface Variant", "shadow color option")) {
SettingsData.set("m3ElevationColorMode", "surfaceVariant");
} else if (value === I18n.tr("Custom", "shadow color option")) {
SettingsData.set("m3ElevationColorMode", "custom");
openM3ShadowColorPicker();
} else if (value === I18n.tr("Text Color", "shadow color option")) {
SettingsData.set("m3ElevationColorMode", "text");
} else {
SettingsData.set("m3ElevationColorMode", "default");
}
}
}
Item {
visible: (SettingsData.m3ElevationEnabled ?? true) && SettingsData.m3ElevationColorMode === "custom"
width: parent.width
implicitHeight: 36
height: implicitHeight
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Custom Shadow Color")
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
verticalAlignment: Text.AlignVCenter
}
Rectangle {
width: 26
height: 26
radius: 13
color: SettingsData.m3ElevationCustomColor ?? "#000000"
border.color: Theme.outline
border.width: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: openM3ShadowColorPicker()
}
}
}
}
SettingsToggleRow {
tab: "theme"
tags: ["elevation", "shadow", "modal", "dialog", "m3"]
settingKey: "modalElevationEnabled"
text: I18n.tr("Modal Shadows")
description: I18n.tr("Shadow elevation on modals and dialogs")
checked: SettingsData.modalElevationEnabled ?? true
visible: SettingsData.m3ElevationEnabled ?? true
onToggled: checked => SettingsData.set("modalElevationEnabled", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["elevation", "shadow", "popout", "popup", "osd", "dropdown", "m3"]
settingKey: "popoutElevationEnabled"
text: I18n.tr("Popout Shadows")
description: I18n.tr("Shadow elevation on popouts, OSDs, and dropdowns")
checked: SettingsData.popoutElevationEnabled ?? true
visible: SettingsData.m3ElevationEnabled ?? true
onToggled: checked => SettingsData.set("popoutElevationEnabled", checked)
}
SettingsToggleRow {
tab: "theme"
tags: ["elevation", "shadow", "bar", "panel", "navigation", "m3"]
settingKey: "barElevationEnabled"
text: I18n.tr("Bar Shadows")
description: I18n.tr("Shadow elevation on bars and panels")
checked: SettingsData.barElevationEnabled ?? true
visible: SettingsData.m3ElevationEnabled ?? true
onToggled: checked => SettingsData.set("barElevationEnabled", checked)
}
} }
SettingsCard { SettingsCard {

View File

@@ -663,14 +663,15 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1
shadowBlur: 0.8 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.2) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.2 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1)
shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2
} }
} }

View File

@@ -96,7 +96,7 @@ PanelWindow {
} }
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
layer.enabled: true layer.enabled: Theme.elevationEnabled
opacity: shouldBeVisible ? 1 : 0 opacity: shouldBeVisible ? 1 : 0
Column { Column {
@@ -407,12 +407,14 @@ PanelWindow {
} }
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true autoPaddingEnabled: true
shadowEnabled: Theme.elevationEnabled
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel3 && Theme.elevationLevel3.offsetY !== undefined ? Theme.elevationLevel3.offsetY : 6
shadowBlur: 0.8 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined ? Theme.elevationLevel3.blurPx : 12) / Theme.elevationBlurMax)) : 0
shadowColor: Qt.rgba(0, 0, 0, 0.3) blurMax: Theme.elevationBlurMax
shadowOpacity: 0.3 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3)
shadowOpacity: Theme.elevationLevel3 && Theme.elevationLevel3.alpha !== undefined ? Theme.elevationLevel3.alpha : 0.3
} }
Behavior on opacity { Behavior on opacity {

View File

@@ -17,59 +17,61 @@ Item {
readonly property var allWorkspaces: Hyprland.workspaces?.values || [] readonly property var allWorkspaces: Hyprland.workspaces?.values || []
readonly property var allWorkspaceIds: { readonly property var allWorkspaceIds: {
const workspaces = allWorkspaces const workspaces = allWorkspaces;
if (!workspaces || workspaces.length === 0) return [] if (!workspaces || workspaces.length === 0)
return [];
try { try {
const ids = workspaces.map(ws => ws?.id).filter(id => id !== null && id !== undefined) const ids = workspaces.map(ws => ws?.id).filter(id => id !== null && id !== undefined);
return ids.sort((a, b) => a - b) return ids.sort((a, b) => a - b);
} catch (e) { } catch (e) {
return [] return [];
} }
} }
readonly property var thisMonitorWorkspaceIds: { readonly property var thisMonitorWorkspaceIds: {
const workspaces = allWorkspaces const workspaces = allWorkspaces;
const mon = monitor const mon = monitor;
if (!workspaces || workspaces.length === 0 || !mon) return [] if (!workspaces || workspaces.length === 0 || !mon)
return [];
try { try {
const filtered = workspaces.filter(ws => ws?.monitor?.name === mon.name) const filtered = workspaces.filter(ws => ws?.monitor?.name === mon.name);
return filtered.map(ws => ws?.id).filter(id => id !== null && id !== undefined).sort((a, b) => a - b) return filtered.map(ws => ws?.id).filter(id => id !== null && id !== undefined).sort((a, b) => a - b);
} catch (e) { } catch (e) {
return [] return [];
} }
} }
readonly property var displayedWorkspaceIds: { readonly property var displayedWorkspaceIds: {
if (!allWorkspaceIds || allWorkspaceIds.length === 0) { if (!allWorkspaceIds || allWorkspaceIds.length === 0) {
const result = [] const result = [];
for (let i = 1; i <= workspacesShown; i++) { for (let i = 1; i <= workspacesShown; i++) {
result.push(i) result.push(i);
} }
return result return result;
} }
try { try {
const maxExisting = Math.max(...allWorkspaceIds) const maxExisting = Math.max(...allWorkspaceIds);
const totalNeeded = Math.max(workspacesShown, allWorkspaceIds.length) const totalNeeded = Math.max(workspacesShown, allWorkspaceIds.length);
const result = [] const result = [];
for (let i = 1; i <= maxExisting; i++) { for (let i = 1; i <= maxExisting; i++) {
result.push(i) result.push(i);
} }
let nextId = maxExisting + 1 let nextId = maxExisting + 1;
while (result.length < totalNeeded) { while (result.length < totalNeeded) {
result.push(nextId) result.push(nextId);
nextId++ nextId++;
} }
return result return result;
} catch (e) { } catch (e) {
const result = [] const result = [];
for (let i = 1; i <= workspacesShown; i++) { for (let i = 1; i <= workspacesShown; i++) {
result.push(i) result.push(i);
} }
return result return result;
} }
} }
@@ -81,24 +83,27 @@ Item {
readonly property int effectiveRows: Math.max(SettingsData.overviewRows, Math.ceil(displayWorkspaceCount / effectiveColumns)) readonly property int effectiveRows: Math.max(SettingsData.overviewRows, Math.ceil(displayWorkspaceCount / effectiveColumns))
function getWorkspaceMonitorName(workspaceId) { function getWorkspaceMonitorName(workspaceId) {
if (!allWorkspaces || !workspaceId) return "" if (!allWorkspaces || !workspaceId)
return "";
try { try {
const ws = allWorkspaces.find(w => w?.id === workspaceId) const ws = allWorkspaces.find(w => w?.id === workspaceId);
return ws?.monitor?.name ?? "" return ws?.monitor?.name ?? "";
} catch (e) { } catch (e) {
return "" return "";
} }
} }
function workspaceHasWindows(workspaceId) { function workspaceHasWindows(workspaceId) {
if (!workspaceId) return false if (!workspaceId)
return false;
try { try {
const workspace = allWorkspaces.find(ws => ws?.id === workspaceId) const workspace = allWorkspaces.find(ws => ws?.id === workspaceId);
if (!workspace) return false if (!workspace)
const toplevels = workspace?.toplevels?.values || [] return false;
return toplevels.length > 0 const toplevels = workspace?.toplevels?.values || [];
return toplevels.length > 0;
} catch (e) { } catch (e) {
return false return false;
} }
} }
@@ -124,16 +129,16 @@ Item {
implicitHeight: overviewBackground.implicitHeight + Theme.spacingL * 2 implicitHeight: overviewBackground.implicitHeight + Theme.spacingL * 2
Component.onCompleted: { Component.onCompleted: {
Hyprland.refreshToplevels() Hyprland.refreshToplevels();
Hyprland.refreshWorkspaces() Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors() Hyprland.refreshMonitors();
} }
onOverviewOpenChanged: { onOverviewOpenChanged: {
if (overviewOpen) { if (overviewOpen) {
Hyprland.refreshToplevels() Hyprland.refreshToplevels();
Hyprland.refreshWorkspaces() Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors() Hyprland.refreshMonitors();
} }
} }
@@ -148,15 +153,15 @@ Item {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainer color: Theme.surfaceContainer
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: Theme.elevationEnabled
shadowBlur: 0.5 shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowColor: Theme.shadowStrong blurMax: Theme.elevationBlurMax
shadowOpacity: 1 shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
blurMax: 32 shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
} }
ColumnLayout { ColumnLayout {
@@ -217,8 +222,8 @@ Item {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onClicked: { onClicked: {
if (root.draggingTargetWorkspace === -1) { if (root.draggingTargetWorkspace === -1) {
root.overviewOpen = false root.overviewOpen = false;
Hyprland.dispatch(`workspace ${workspaceValue}`) Hyprland.dispatch(`workspace ${workspaceValue}`);
} }
} }
} }
@@ -226,13 +231,15 @@ Item {
DropArea { DropArea {
anchors.fill: parent anchors.fill: parent
onEntered: { onEntered: {
root.draggingTargetWorkspace = workspaceValue root.draggingTargetWorkspace = workspaceValue;
if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return if (root.draggingFromWorkspace == root.draggingTargetWorkspace)
hoveredWhileDragging = true return;
hoveredWhileDragging = true;
} }
onExited: { onExited: {
hoveredWhileDragging = false hoveredWhileDragging = false;
if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1 if (root.draggingTargetWorkspace == workspaceValue)
root.draggingTargetWorkspace = -1;
} }
} }
} }
@@ -250,27 +257,28 @@ Item {
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
values: { values: {
const workspaces = root.allWorkspaces const workspaces = root.allWorkspaces;
const minId = root.minWorkspaceId const minId = root.minWorkspaceId;
const maxId = root.maxWorkspaceId const maxId = root.maxWorkspaceId;
if (!workspaces || workspaces.length === 0) return [] if (!workspaces || workspaces.length === 0)
return [];
try { try {
const result = [] const result = [];
for (const workspace of workspaces) { for (const workspace of workspaces) {
const wsId = workspace?.id ?? -1 const wsId = workspace?.id ?? -1;
if (wsId >= minId && wsId <= maxId) { if (wsId >= minId && wsId <= maxId) {
const toplevels = workspace?.toplevels?.values || [] const toplevels = workspace?.toplevels?.values || [];
for (const toplevel of toplevels) { for (const toplevel of toplevels) {
result.push(toplevel) result.push(toplevel);
} }
} }
} }
return result return result;
} catch (e) { } catch (e) {
console.error("OverviewWidget filter error:", e) console.error("OverviewWidget filter error:", e);
return [] return [];
} }
} }
} }
@@ -282,17 +290,19 @@ Item {
readonly property int windowWorkspaceId: modelData?.workspace?.id ?? -1 readonly property int windowWorkspaceId: modelData?.workspace?.id ?? -1
function getWorkspaceIndex() { function getWorkspaceIndex() {
if (!root.displayedWorkspaceIds || root.displayedWorkspaceIds.length === 0) return 0 if (!root.displayedWorkspaceIds || root.displayedWorkspaceIds.length === 0)
if (!windowWorkspaceId || windowWorkspaceId < 0) return 0 return 0;
if (!windowWorkspaceId || windowWorkspaceId < 0)
return 0;
try { try {
for (let i = 0; i < root.displayedWorkspaceIds.length; i++) { for (let i = 0; i < root.displayedWorkspaceIds.length; i++) {
if (root.displayedWorkspaceIds[i] === windowWorkspaceId) { if (root.displayedWorkspaceIds[i] === windowWorkspaceId) {
return i return i;
} }
} }
return 0 return 0;
} catch (e) { } catch (e) {
return 0 return 0;
} }
} }
@@ -325,48 +335,48 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.MiddleButton acceptedButtons: Qt.LeftButton | Qt.MiddleButton
drag.target: parent drag.target: parent
onPressed: (mouse) => { onPressed: mouse => {
root.draggingFromWorkspace = windowData?.workspace.id root.draggingFromWorkspace = windowData?.workspace.id;
window.pressed = true window.pressed = true;
window.Drag.active = true window.Drag.active = true;
window.Drag.source = window window.Drag.source = window;
window.Drag.hotSpot.x = mouse.x window.Drag.hotSpot.x = mouse.x;
window.Drag.hotSpot.y = mouse.y window.Drag.hotSpot.y = mouse.y;
} }
onReleased: { onReleased: {
const targetWorkspace = root.draggingTargetWorkspace const targetWorkspace = root.draggingTargetWorkspace;
window.pressed = false window.pressed = false;
window.Drag.active = false window.Drag.active = false;
root.draggingFromWorkspace = -1 root.draggingFromWorkspace = -1;
root.draggingTargetWorkspace = -1 root.draggingTargetWorkspace = -1;
if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace},address:${windowData?.address}`) Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace},address:${windowData?.address}`);
Qt.callLater(() => { Qt.callLater(() => {
Hyprland.refreshToplevels() Hyprland.refreshToplevels();
Hyprland.refreshWorkspaces() Hyprland.refreshWorkspaces();
Qt.callLater(() => { Qt.callLater(() => {
window.x = window.initX window.x = window.initX;
window.y = window.initY window.y = window.initY;
}) });
}) });
} else { } else {
window.x = window.initX window.x = window.initX;
window.y = window.initY window.y = window.initY;
} }
} }
onClicked: (event) => { onClicked: event => {
if (!windowData || !windowData.address) return if (!windowData || !windowData.address)
return;
if (event.button === Qt.LeftButton) { if (event.button === Qt.LeftButton) {
root.overviewOpen = false root.overviewOpen = false;
Hyprland.dispatch(`focuswindow address:${windowData.address}`) Hyprland.dispatch(`focuswindow address:${windowData.address}`);
event.accepted = true event.accepted = true;
} else if (event.button === Qt.MiddleButton) { } else if (event.button === Qt.MiddleButton) {
Hyprland.dispatch(`closewindow address:${windowData.address}`) Hyprland.dispatch(`closewindow address:${windowData.address}`);
event.accepted = true event.accepted = true;
} }
} }
} }

View File

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

View File

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

View File

@@ -261,12 +261,16 @@ Item {
border.width: 2 border.width: 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
layer.enabled: true layer.enabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true autoPaddingEnabled: true
shadowBlur: 0.4 blurEnabled: false
shadowColor: Theme.shadowStrong maskEnabled: false
shadowVerticalOffset: 4 shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
} }
Column { Column {

View File

@@ -135,13 +135,16 @@ Rectangle {
color: Theme.surface color: Theme.surface
radius: Theme.cornerRadius radius: Theme.cornerRadius
layer.enabled: true layer.enabled: Theme.elevationEnabled
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true autoPaddingEnabled: true
shadowColor: Theme.shadowStrong shadowEnabled: Theme.elevationEnabled
shadowBlur: 0.8 blurMax: Theme.elevationBlurMax
shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2)
shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 4 shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
} }
Rectangle { Rectangle {

View File

@@ -257,9 +257,10 @@ PanelWindow {
scale: shouldBeVisible ? 1 : 0.9 scale: shouldBeVisible ? 1 : 0.9
property bool childHovered: false property bool childHovered: false
property real shadowBlurPx: 10 readonly property var elev: Theme.elevationLevel3
property real shadowSpreadPx: 0 property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 12
property real shadowBaseAlpha: 0.60 property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.3
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0 readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0
@@ -269,7 +270,7 @@ PanelWindow {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha) color: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha)
border.color: Theme.outlineMedium border.color: Theme.outlineMedium
border.width: 1 border.width: 0
z: -1 z: -1
} }
@@ -277,12 +278,11 @@ PanelWindow {
id: bgShadowLayer id: bgShadowLayer
anchors.fill: parent anchors.fill: parent
visible: osdContainer.popupSurfaceAlpha >= 0.95 visible: osdContainer.popupSurfaceAlpha >= 0.95
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" layer.enabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr)) layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically layer.textureMirroring: ShaderEffectSource.MirrorVertically
readonly property int blurMax: 64 readonly property int blurMax: Theme.elevationBlurMax
layer.effect: MultiEffect { layer.effect: MultiEffect {
id: shadowFx id: shadowFx
@@ -292,10 +292,8 @@ PanelWindow {
maskEnabled: false maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax)) 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)) shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
shadowColor: { blurMax: Theme.elevationBlurMax
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest; shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3)
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha);
}
} }
Rectangle { Rectangle {

View File

@@ -197,7 +197,7 @@ Item {
readonly property real screenHeight: screen ? screen.height : 0 readonly property real screenHeight: screen ? screen.height : 0
readonly property real dpr: screen ? screen.devicePixelRatio : 1 readonly property real dpr: screen ? screen.devicePixelRatio : 1
readonly property real shadowBuffer: 5 readonly property real shadowBuffer: Theme.elevationBlurMax * 1.5 + 24
readonly property real alignedWidth: Theme.px(popupWidth, dpr) readonly property real alignedWidth: Theme.px(popupWidth, dpr)
readonly property real alignedHeight: Theme.px(popupHeight, dpr) readonly property real alignedHeight: Theme.px(popupHeight, dpr)
@@ -487,21 +487,20 @@ Item {
height: parent.height height: parent.height
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: "black" color: "black"
visible: false
opacity: contentWrapper.opacity opacity: contentWrapper.opacity
scale: contentWrapper.scale scale: contentWrapper.scale
x: contentWrapper.x x: contentWrapper.x
y: contentWrapper.y y: contentWrapper.y
property real shadowBlurPx: 10 readonly property var elev: Theme.elevationLevel3
property real shadowSpreadPx: 0 property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 12
property real shadowBaseAlpha: 0.60 property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0
property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.3
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
readonly property int blurMax: 64 readonly property int blurMax: Theme.elevationBlurMax
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) layer.enabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
layer.smooth: false
layer.effect: MultiEffect { layer.effect: MultiEffect {
id: shadowFx id: shadowFx
@@ -511,10 +510,9 @@ Item {
maskEnabled: false maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, shadowSource.shadowBlurPx / shadowSource.blurMax)) 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)) shadowScale: 1 + (2 * shadowSource.shadowSpreadPx) / Math.max(1, Math.min(shadowSource.width, shadowSource.height))
shadowColor: { shadowVerticalOffset: parent.elev && parent.elev.offsetY !== undefined ? parent.elev.offsetY : 6
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest; blurMax: Theme.elevationBlurMax
return Theme.withAlpha(baseColor, shadowSource.effectiveShadowAlpha); shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3)
}
} }
} }
@@ -546,7 +544,7 @@ Item {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
border.color: Theme.outlineMedium border.color: Theme.outlineMedium
border.width: 1 border.width: 0
} }
Loader { Loader {

View File

@@ -53,8 +53,11 @@
--m3-radius: 12px; --m3-radius: 12px;
--m3-radius-sm: 10px; --m3-radius-sm: 10px;
--m3-elev-0: none; --m3-elev-0: none;
--m3-elev-1: 0 1px 2px rgba(0,0,0,.08), 0 1px 3px rgba(0,0,0,.06); --m3-elev-1: 0 1px 2px color-mix(in srgb, var(--md-sys-color-shadow) 8%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 6%, transparent);
--m3-elev-2: 0 2px 6px rgba(0,0,0,.10), 0 1px 3px rgba(0,0,0,.06); --m3-elev-2: 0 2px 6px color-mix(in srgb, var(--md-sys-color-shadow) 10%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 6%, transparent);
--m3-elev-3: 0 11px 7px color-mix(in srgb, var(--md-sys-color-shadow) 19%, transparent), 0 13px 25px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent);
--m3-elev-4: 0 14px 12px color-mix(in srgb, var(--md-sys-color-shadow) 17%, transparent), 0 20px 40px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent);
--m3-elev-5: 0 17px 17px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent), 0 27px 55px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent);
--tab-height: 34px; --tab-height: 34px;
--urlbar-height: 38px; --urlbar-height: 38px;
@@ -118,8 +121,11 @@
--md-sys-color-surface-container-high: {{colors.surface_container_high.dark.hex}}; --md-sys-color-surface-container-high: {{colors.surface_container_high.dark.hex}};
--md-sys-color-surface-container-highest: {{colors.surface_container_highest.dark.hex}}; --md-sys-color-surface-container-highest: {{colors.surface_container_highest.dark.hex}};
--m3-elev-1: 0 1px 2px rgba(0,0,0,.50), 0 1px 3px rgba(0,0,0,.35); --m3-elev-1: 0 1px 2px color-mix(in srgb, var(--md-sys-color-shadow) 50%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 35%, transparent);
--m3-elev-2: 0 4px 10px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.35); --m3-elev-2: 0 4px 10px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 35%, transparent);
--m3-elev-3: 0 11px 7px color-mix(in srgb, var(--md-sys-color-shadow) 45%, transparent), 0 13px 25px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent);
--m3-elev-4: 0 14px 12px color-mix(in srgb, var(--md-sys-color-shadow) 42%, transparent), 0 20px 40px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent);
--m3-elev-5: 0 17px 17px color-mix(in srgb, var(--md-sys-color-shadow) 40%, transparent), 0 27px 55px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent);
--state-hover: color-mix(in srgb, var(--md-sys-color-on-surface) 6%, transparent); --state-hover: color-mix(in srgb, var(--md-sys-color-on-surface) 6%, transparent);
--state-press: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent); --state-press: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent);

View File

@@ -2400,6 +2400,74 @@
], ],
"description": "0 = square corners" "description": "0 = square corners"
}, },
{
"section": "m3ElevationEnabled",
"label": "M3 Elevation & Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"appearance",
"elevation",
"lift",
"material",
"m3",
"shadow",
"shadows",
"theme"
],
"description": "Material Design 3 shadows and elevation on modals, popouts, and dialogs"
},
{
"section": "modalElevationEnabled",
"label": "Modal Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"dialog",
"elevation",
"m3",
"material",
"modal",
"shadow",
"shadows"
],
"description": "Shadow elevation on modals and dialogs"
},
{
"section": "popoutElevationEnabled",
"label": "Popout Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"dropdown",
"elevation",
"m3",
"material",
"osd",
"popout",
"popup",
"shadow",
"shadows"
],
"description": "Shadow elevation on popouts, OSDs, and dropdowns"
},
{
"section": "barElevationEnabled",
"label": "Bar Shadows",
"tabIndex": 10,
"category": "Theme & Colors",
"keywords": [
"bar",
"elevation",
"m3",
"material",
"navigation",
"panel",
"shadow",
"shadows"
],
"description": "Shadow elevation on bars and panels"
},
{ {
"section": "cursorSize", "section": "cursorSize",
"label": "Cursor Size", "label": "Cursor Size",