1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

feat: Updated DMS Animations

- Based on Material 3 Expressive
- Now features custom timers options
- Thanks to Soramane/Caelestia for converting Google's Material 3 Expressive curves
This commit is contained in:
purian23
2025-10-19 21:42:18 -04:00
parent b1ae246c86
commit d6b690ae2f
5 changed files with 364 additions and 74 deletions

View File

@@ -25,10 +25,10 @@ Singleton {
enum AnimationSpeed {
None,
Shortest,
Short,
Medium,
Long
Long,
Custom
}
readonly property string defaultFontFamily: "Inter Variable"
@@ -64,6 +64,7 @@ Singleton {
property bool useFahrenheit: false
property bool nightModeEnabled: false
property int animationSpeed: SettingsData.AnimationSpeed.Short
property int customAnimationDuration: 500
property string wallpaperFillMode: "Fill"
property bool showLauncherButton: true
@@ -489,6 +490,7 @@ Singleton {
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill"
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
customAnimationDuration = settings.customAnimationDuration !== undefined ? settings.customAnimationDuration : 500
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
acLockTimeout = settings.acLockTimeout !== undefined ? settings.acLockTimeout : 0
acSuspendTimeout = settings.acSuspendTimeout !== undefined ? settings.acSuspendTimeout : 0
@@ -668,6 +670,7 @@ Singleton {
"updaterTerminalAdditionalParams": updaterTerminalAdditionalParams,
"screenPreferences": screenPreferences,
"animationSpeed": animationSpeed,
"customAnimationDuration": customAnimationDuration,
"acMonitorTimeout": acMonitorTimeout,
"acLockTimeout": acLockTimeout,
"acSuspendTimeout": acSuspendTimeout,
@@ -731,7 +734,7 @@ Singleton {
"customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend",
"customPowerActionHibernate", "customPowerActionReboot", "customPowerActionPowerOff",
"updaterUseCustomCommand", "updaterCustomCommand", "updaterTerminalAdditionalParams",
"screenPreferences", "animationSpeed", "acMonitorTimeout", "acLockTimeout",
"screenPreferences", "animationSpeed", "customAnimationDuration", "acMonitorTimeout", "acLockTimeout",
"acSuspendTimeout", "acHibernateTimeout", "batteryMonitorTimeout", "batteryLockTimeout",
"batterySuspendTimeout", "batteryHibernateTimeout", "lockBeforeSuspend",
"loginctlLockIntegration", "launchPrefix", "configVersion"
@@ -1058,6 +1061,11 @@ Singleton {
saveSettings()
}
function setCustomAnimationDuration(duration) {
customAnimationDuration = duration
saveSettings()
}
function setWallpaperFillMode(mode) {
wallpaperFillMode = mode
saveSettings()

View File

@@ -313,6 +313,61 @@ Singleton {
property int standardEasing: Easing.OutCubic
property int emphasizedEasing: Easing.OutQuart
readonly property var expressiveCurves: {
"emphasized": [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1],
"emphasizedAccel": [0.3, 0, 0.8, 0.15, 1, 1],
"emphasizedDecel": [0.05, 0.7, 0.1, 1, 1, 1],
"standard": [0.2, 0, 0, 1, 1, 1],
"standardAccel": [0.3, 0, 1, 1, 1, 1],
"standardDecel": [0, 0, 0, 1, 1, 1],
"expressiveFastSpatial": [0.42, 1.67, 0.21, 0.9, 1, 1],
"expressiveDefaultSpatial": [0.38, 1.21, 0.22, 1, 1, 1],
"expressiveEffects": [0.34, 0.8, 0.34, 1, 1, 1]
}
readonly property var animationPresetDurations: {
"none": 0,
"short": 250,
"medium": 500,
"long": 750
}
readonly property int currentAnimationBaseDuration: {
if (typeof SettingsData === "undefined") return 500
if (SettingsData.animationSpeed === SettingsData.AnimationSpeed.Custom) {
return SettingsData.customAnimationDuration
}
const presetMap = [0, 250, 500, 750]
return presetMap[SettingsData.animationSpeed] !== undefined ? presetMap[SettingsData.animationSpeed] : 500
}
readonly property var expressiveDurations: {
if (typeof SettingsData === "undefined") {
return {
"fast": 200,
"normal": 400,
"large": 600,
"extraLarge": 1000,
"expressiveFastSpatial": 350,
"expressiveDefaultSpatial": 500,
"expressiveEffects": 200
}
}
const baseDuration = currentAnimationBaseDuration
return {
"fast": baseDuration * 0.4,
"normal": baseDuration * 0.8,
"large": baseDuration * 1.2,
"extraLarge": baseDuration * 2.0,
"expressiveFastSpatial": baseDuration * 0.7,
"expressiveDefaultSpatial": baseDuration,
"expressiveEffects": baseDuration * 0.4
}
}
property real cornerRadius: typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12
property real spacingXS: 4
property real spacingS: 8

View File

@@ -35,8 +35,11 @@ PanelWindow {
property bool closeOnEscapeKey: true
property bool closeOnBackgroundClick: true
property string animationType: "scale"
property int animationDuration: Theme.shortDuration
property var animationEasing: Theme.emphasizedEasing
property int animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial
property real animationScaleCollapsed: 0.96
property real animationOffset: Theme.spacingL
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
property color backgroundColor: Theme.surfaceContainer
property color borderColor: Theme.outlineMedium
property real borderWidth: 1
@@ -104,7 +107,7 @@ PanelWindow {
Timer {
id: closeTimer
interval: animationDuration + 100
interval: animationDuration + 120
onTriggered: {
visible = false
}
@@ -139,7 +142,8 @@ PanelWindow {
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: root.animationEasing
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}
@@ -176,23 +180,67 @@ PanelWindow {
border.width: root.borderWidth
clip: false
layer.enabled: true
layer.smooth: true
opacity: root.shouldBeVisible ? 1 : 0
transform: root.animationType === "slide" ? slideTransform : null
transform: [scaleTransform, motionTransform]
Scale {
id: scaleTransform
origin.x: contentContainer.width / 2
origin.y: contentContainer.height / 2
xScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
yScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
Behavior on xScale {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
Behavior on yScale {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}
Translate {
id: slideTransform
id: motionTransform
readonly property real rawX: root.shouldBeVisible ? 0 : 15
readonly property real rawY: root.shouldBeVisible ? 0 : -30
readonly property bool slide: root.animationType === "slide"
readonly property real hiddenX: slide ? 15 : 0
readonly property real hiddenY: slide ? -30 : root.animationOffset
x: Theme.snap(rawX, root.dpr)
y: Theme.snap(rawY, root.dpr)
x: Theme.snap(root.shouldBeVisible ? 0 : hiddenX, root.dpr)
y: Theme.snap(root.shouldBeVisible ? 0 : hiddenY, root.dpr)
Behavior on x {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
Behavior on y {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}
Behavior on opacity {
NumberAnimation {
duration: animationDuration
easing.type: animationEasing
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}

View File

@@ -1016,6 +1016,179 @@ Item {
}
}
// Animation Settings
StyledRect {
width: parent.width
height: animationSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: animationSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "animation"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Animation Speed")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Item {
width: parent.width
height: childrenRect.height
DankButtonGroup {
id: animationSpeedGroup
x: (parent.width - width) / 2
model: ["None", "Short", "Medium", "Long", "Custom"]
selectionMode: "single"
currentIndex: SettingsData.animationSpeed
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.setAnimationSpeed(index)
}
}
Connections {
target: SettingsData
function onAnimationSpeedChanged() {
animationSpeedGroup.currentIndex = SettingsData.animationSpeed
}
}
}
}
Column {
width: parent.width
spacing: Theme.spacingM
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
}
Column {
width: parent.width
spacing: Theme.spacingS
Row {
width: parent.width
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Duration")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: 1
height: 1
}
StyledText {
text: Theme.currentAnimationBaseDuration + "ms"
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
width: parent.width
height: 40
spacing: Theme.spacingM
StyledText {
text: "0ms"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - 100
height: 40
anchors.verticalCenter: parent.verticalCenter
DankSlider {
id: customDurationSlider
anchors.fill: parent
minimum: 0
maximum: 750
value: Theme.currentAnimationBaseDuration
unit: "ms"
showValue: false
wheelEnabled: false
onSliderValueChanged: (newValue) => {
SettingsData.setAnimationSpeed(SettingsData.AnimationSpeed.Custom)
SettingsData.setCustomAnimationDuration(newValue)
}
Connections {
target: SettingsData
function onAnimationSpeedChanged() {
if (SettingsData.animationSpeed !== SettingsData.AnimationSpeed.Custom) {
customDurationSlider.value = Theme.currentAnimationBaseDuration
}
}
}
Connections {
target: Theme
function onCurrentAnimationBaseDurationChanged() {
if (SettingsData.animationSpeed !== SettingsData.AnimationSpeed.Custom) {
customDurationSlider.value = Theme.currentAnimationBaseDuration
}
}
}
}
}
StyledText {
text: "750ms"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
StyledText {
text: I18n.tr("Select a preset or drag the slider to customize")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
}
}
}
StyledRect {
width: parent.width
height: dynamicThemeSection.implicitHeight + Theme.spacingL * 2
@@ -1189,62 +1362,6 @@ Item {
}
}
// Animation Settings
StyledRect {
width: parent.width
height: animationSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: animationSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "animation"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Animation Speed")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Item {
width: parent.width
height: childrenRect.height
DankButtonGroup {
id: animationSpeedGroup
x: (parent.width - width) / 2
model: ["None", "Shortest", "Short", "Medium", "Long"]
selectionMode: "single"
currentIndex: SettingsData.animationSpeed
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.setAnimationSpeed(index)
}
}
}
}
}
}
StyledRect {
width: parent.width
height: soundsSection.implicitHeight + Theme.spacingL * 2

View File

@@ -19,8 +19,11 @@ PanelWindow {
property real triggerWidth: 40
property string triggerSection: ""
property string positioning: "center"
property int animationDuration: Theme.shortDuration
property var animationEasing: Theme.emphasizedEasing
property int animationDuration: Theme.expressiveDurations.expressiveDefaultSpatial
property real animationScaleCollapsed: 0.96
property real animationOffset: Theme.spacingL
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
property bool shouldBeVisible: false
signal opened
@@ -48,7 +51,7 @@ PanelWindow {
Timer {
id: closeTimer
interval: animationDuration + 50
interval: animationDuration + 120
onTriggered: {
if (!shouldBeVisible) {
visible = false
@@ -126,13 +129,72 @@ PanelWindow {
height: alignedHeight
active: root.visible
asynchronous: false
transformOrigin: Item.Center
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true"
layer.smooth: true
opacity: shouldBeVisible ? 1 : 0
transform: [scaleTransform, motionTransform]
Scale {
id: scaleTransform
origin.x: contentLoader.width / 2
origin.y: contentLoader.height / 2
xScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
yScale: root.shouldBeVisible ? 1 : root.animationScaleCollapsed
Behavior on xScale {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
Behavior on yScale {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}
Translate {
id: motionTransform
readonly property bool barTop: SettingsData.dankBarPosition === SettingsData.Position.Top
readonly property bool barBottom: SettingsData.dankBarPosition === SettingsData.Position.Bottom
readonly property bool barLeft: SettingsData.dankBarPosition === SettingsData.Position.Left
readonly property bool barRight: SettingsData.dankBarPosition === SettingsData.Position.Right
readonly property real hiddenX: barLeft ? root.animationOffset : (barRight ? -root.animationOffset : 0)
readonly property real hiddenY: barBottom ? -root.animationOffset : (barTop ? root.animationOffset : 0)
x: Theme.snap(root.shouldBeVisible ? 0 : hiddenX, root.dpr)
y: Theme.snap(root.shouldBeVisible ? 0 : hiddenY, root.dpr)
Behavior on x {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
Behavior on y {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}
Behavior on opacity {
NumberAnimation {
duration: animationDuration
easing.type: animationEasing
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
}
}
}