diff --git a/bin/omarchy-hw-vm b/bin/omarchy-hw-vm new file mode 100755 index 00000000..b6d27a92 --- /dev/null +++ b/bin/omarchy-hw-vm @@ -0,0 +1,5 @@ +#!/bin/bash + +# omarchy:summary=Returns true when running in a virtual machine. + +systemd-detect-virt --vm --quiet diff --git a/bin/omarchy-toggle-animations b/bin/omarchy-toggle-animations new file mode 100755 index 00000000..3040461a --- /dev/null +++ b/bin/omarchy-toggle-animations @@ -0,0 +1,22 @@ +#!/bin/bash + +# omarchy:summary=Toggle animations, transparency and other effects that are slow without a GPU +# omarchy:args=[toggle|on|off] +# omarchy:examples=omarchy toggle animations | omarchy toggle animations off | omarchy toggle animations on + +# The flag records animations being off, so on and off swap on the way through. +case ${1:-toggle} in +toggle) omarchy-hyprland-toggle no-animations toggle ;; +on) omarchy-hyprland-toggle no-animations off ;; +off) omarchy-hyprland-toggle no-animations on ;; +*) + echo "Usage: omarchy-toggle-animations [toggle|on|off]" >&2 + exit 1 + ;; +esac + +if omarchy-hyprland-toggle-enabled no-animations; then + omarchy-notification-send -g 󰕟 "Animations disabled" +else + omarchy-notification-send -g 󰕟 "Animations enabled" +fi diff --git a/default/hypr/toggles/no-animations.lua b/default/hypr/toggles/no-animations.lua new file mode 100644 index 00000000..504b3b13 --- /dev/null +++ b/default/hypr/toggles/no-animations.lua @@ -0,0 +1,20 @@ +-- Draw nothing just for show, for machines that render on the CPU, like a VM: +-- no animations, blur or shadows, and opaque windows, so nothing behind a +-- window has to be blended into it. +hl.config({ + animations = { + enabled = false, + }, + + decoration = { + blur = { + enabled = false, + }, + + shadow = { + enabled = false, + }, + }, +}) + +o.window(".*", { opacity = "1 1" }) diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 161504f2..36518d09 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -97,6 +97,7 @@ "trigger.toggle.battery-percentage": {"icon":"󰁹","label":"Battery Percentage","when":"omarchy-hw-laptop","action":"omarchy-shell omarchy.power togglePercentage"}, "trigger.toggle.workspace-layout": {"icon":"󱂬","label":"Workspace Layout","action":"omarchy-hyprland-workspace-layout-toggle"}, "trigger.toggle.window-gaps": {"icon":"","label":"Window Gaps","action":"omarchy-hyprland-window-gaps-toggle"}, + "trigger.toggle.animations": {"icon":"󰕟","label":"Animations","action":"omarchy-toggle-animations"}, "trigger.toggle.one-window-ratio": {"icon":"","label":"1-Window Ratio","action":"omarchy-hyprland-window-single-square-aspect-toggle"}, "trigger.tests.network-speedtest": {"icon":"󰓅","label":"Network Speed Test","action":"omarchy-shell shell summon omarchy.speedtest"}, "trigger.tests.disk-speedtest": {"icon":"󰋊","label":"Disk Speed Test","action":"omarchy-shell shell summon omarchy.disk-speedtest"}, diff --git a/install/user/all.sh b/install/user/all.sh index 39d98ea5..d81c3e75 100644 --- a/install/user/all.sh +++ b/install/user/all.sh @@ -9,6 +9,7 @@ run_logged "$OMARCHY_INSTALL/user/hardware/asus/fix-mic.sh" run_logged "$OMARCHY_INSTALL/user/hardware/framework/fix-f13-amd-audio-input.sh" run_logged "$OMARCHY_INSTALL/user/hardware/dell/xps13-text-scaling.sh" run_logged "$OMARCHY_INSTALL/user/hardware/fix-nouveau-cursor.sh" +run_logged "$OMARCHY_INSTALL/user/hardware/vm-no-animations.sh" run_logged "$OMARCHY_INSTALL/user/default-keyring.sh" run_logged "$OMARCHY_INSTALL/user/mise.sh" diff --git a/install/user/hardware/vm-no-animations.sh b/install/user/hardware/vm-no-animations.sh new file mode 100644 index 00000000..428fa969 --- /dev/null +++ b/install/user/hardware/vm-no-animations.sh @@ -0,0 +1,9 @@ +# A VM usually renders on the CPU, where animations and transparency cost +# every frame, so start it without them. omarchy-toggle-animations brings them +# back. This runs before any Hyprland session exists, so place the flag +# directly rather than through omarchy-hyprland-toggle, which reloads Hyprland. +if omarchy-hw-vm; then + echo "Detected a virtual machine. Turning off animations and transparency." + mkdir -p "$HOME/.local/state/omarchy/toggles/hypr" + cp "$OMARCHY_PATH/default/hypr/toggles/no-animations.lua" "$HOME/.local/state/omarchy/toggles/hypr/" +fi diff --git a/shell/Commons/Style.qml b/shell/Commons/Style.qml index ec61252b..0582356c 100644 --- a/shell/Commons/Style.qml +++ b/shell/Commons/Style.qml @@ -1,6 +1,7 @@ pragma Singleton import QtQuick import Quickshell +import Quickshell.Hyprland import Quickshell.Io // Shared structural style tokens for the shell. Color is the palette @@ -347,9 +348,19 @@ QtObject { readonly property int statusSlot: root.barToken("status-slot", 21) } + // Off with Hyprland's own animations, as `omarchy toggle animations` turns + // them off for machines that render on the CPU. Every shell animation runs + // for Style.duration(ms), which is then 0, so nothing is drawn in between. + property bool reduceMotion: false + + function duration(ms) { + return reduceMotion ? 0 : ms + } + function refresh() { hyprctlProc.running = true gapsOutProc.running = true + animationsProc.running = true } function scheduleRefresh() { @@ -448,6 +459,30 @@ QtObject { } } + property Process animationsProc: Process { + id: animationsProc + command: ["hyprctl", "-j", "getoption", "animations:enabled"] + stdout: StdioCollector { + waitForEnd: true + onStreamFinished: { + try { + root.reduceMotion = JSON.parse(text || "{}").bool === false + } catch (e) { + // hyprctl missing / Hyprland not running — leave the previous value. + } + } + } + } + + // A reload is when the animations toggle, a theme or the user's own config + // lands, and Hyprland only announces it once the new values are in effect. + property Connections hyprlandEvents: Connections { + target: Hyprland + function onRawEvent(event) { + if (event && String(event.name) === "configreloaded") root.refresh() + } + } + property Process gapsOutProc: Process { id: gapsOutProc command: ["hyprctl", "-j", "getoption", "general:gaps_out"] diff --git a/shell/Ui/Button.qml b/shell/Ui/Button.qml index 2b84577a..09c5806c 100644 --- a/shell/Ui/Button.qml +++ b/shell/Ui/Button.qml @@ -125,7 +125,7 @@ BorderSurface { // dedicated selected border. borderSpec: _borderSpec - Behavior on color { ColorAnimation { duration: 120 } } + Behavior on color { ColorAnimation { duration: Style.duration(120) } } ToolTip { visible: root.tooltipText !== "" && mouseArea.containsMouse @@ -174,7 +174,7 @@ BorderSurface { to: 360 duration: 900 loops: Animation.Infinite - running: root.iconSpinning + running: root.iconSpinning && !Style.reduceMotion } } diff --git a/shell/Ui/CursorSurface.qml b/shell/Ui/CursorSurface.qml index 58e46d06..cb715df9 100644 --- a/shell/Ui/CursorSurface.qml +++ b/shell/Ui/CursorSurface.qml @@ -36,6 +36,6 @@ BorderSurface { : Border.none())) Behavior on color { - ColorAnimation { duration: 60 } + ColorAnimation { duration: Style.duration(60) } } } diff --git a/shell/Ui/KeyboardPanel.qml b/shell/Ui/KeyboardPanel.qml index b2fb218f..38a2979c 100644 --- a/shell/Ui/KeyboardPanel.qml +++ b/shell/Ui/KeyboardPanel.qml @@ -390,7 +390,7 @@ PanelWindow { Behavior on opacity { enabled: !root.popoutSwitching && !root.popoutSwitchClosing - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } // Swallow clicks on the card so they don't bubble to the dismissal @@ -411,7 +411,7 @@ PanelWindow { Behavior on opacity { enabled: root.popoutSwitching - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } } } diff --git a/shell/Ui/MultiSelect.qml b/shell/Ui/MultiSelect.qml index 85705779..ad2e2955 100644 --- a/shell/Ui/MultiSelect.qml +++ b/shell/Ui/MultiSelect.qml @@ -461,7 +461,7 @@ Item { font.pixelSize: Style.font.body RotationAnimator on rotation { - running: root.loadingOptions + running: root.loadingOptions && !Style.reduceMotion from: 0; to: 360 duration: 800 loops: Animation.Infinite diff --git a/shell/Ui/PanelActionButton.qml b/shell/Ui/PanelActionButton.qml index 05f7d6be..d4115e2b 100644 --- a/shell/Ui/PanelActionButton.qml +++ b/shell/Ui/PanelActionButton.qml @@ -66,7 +66,7 @@ BorderSurface { : "transparent") borderSpec: _borderSpec - Behavior on color { ColorAnimation { duration: 60 } } + Behavior on color { ColorAnimation { duration: Style.duration(60) } } Text { textFormat: Text.PlainText diff --git a/shell/Ui/PanelSlider.qml b/shell/Ui/PanelSlider.qml index d7bf1303..64396713 100644 --- a/shell/Ui/PanelSlider.qml +++ b/shell/Ui/PanelSlider.qml @@ -62,7 +62,7 @@ Item { Behavior on width { enabled: !root.dragging - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } } @@ -93,11 +93,11 @@ Item { Behavior on x { enabled: !root.dragging - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } Behavior on scale { - NumberAnimation { duration: 110; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(110); easing.type: Easing.OutCubic } } } diff --git a/shell/Ui/PopupCard.qml b/shell/Ui/PopupCard.qml index 058b068f..3bc8193a 100644 --- a/shell/Ui/PopupCard.qml +++ b/shell/Ui/PopupCard.qml @@ -157,7 +157,7 @@ PopupWindow { opacity: root.open ? 1.0 : 0 Behavior on opacity { - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } Item { diff --git a/shell/Ui/SpeedTestOverlay.qml b/shell/Ui/SpeedTestOverlay.qml index a8f84c7b..f1beef92 100644 --- a/shell/Ui/SpeedTestOverlay.qml +++ b/shell/Ui/SpeedTestOverlay.qml @@ -61,7 +61,7 @@ PanelWindow { onRightValueChanged: expandScale(rightValue) Behavior on fullScale { - NumberAnimation { duration: 400; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(400); easing.type: Easing.OutCubic } } // The scrim below is a fixed near-black regardless of theme, so text and @@ -178,7 +178,7 @@ PanelWindow { onClicked: root.runAgainRequested() Behavior on opacity { - NumberAnimation { duration: 240; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(240); easing.type: Easing.OutCubic } } } @@ -236,13 +236,13 @@ PanelWindow { opacity: engaged ? 1 : 0.5 Behavior on opacity { - NumberAnimation { duration: 240; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(240); easing.type: Easing.OutCubic } } // Live readings land once a second; glide between them rather than snap. Behavior on shown { enabled: !ignition.running - NumberAnimation { duration: 600; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(600); easing.type: Easing.OutCubic } } onValueChanged: { @@ -257,8 +257,8 @@ PanelWindow { // the live figures take over. SequentialAnimation { id: ignition - NumberAnimation { target: dial; property: "shown"; to: dial.fullScale; duration: 550; easing.type: Easing.InOutCubic } - NumberAnimation { target: dial; property: "shown"; to: 0; duration: 650; easing.type: Easing.OutCubic } + NumberAnimation { target: dial; property: "shown"; to: dial.fullScale; duration: Style.duration(550); easing.type: Easing.InOutCubic } + NumberAnimation { target: dial; property: "shown"; to: 0; duration: Style.duration(650); easing.type: Easing.OutCubic } onFinished: dial.shown = dial.value } diff --git a/shell/Ui/Toggle.qml b/shell/Ui/Toggle.qml index b861fec7..0f2c9b7c 100644 --- a/shell/Ui/Toggle.qml +++ b/shell/Ui/Toggle.qml @@ -52,7 +52,7 @@ BorderSurface { color: Style.controlFill(activeFocus, _hot, foreground, accent) borderSpec: _borderSpec - Behavior on color { ColorAnimation { duration: 100 } } + Behavior on color { ColorAnimation { duration: Style.duration(100) } } Row { id: content diff --git a/shell/Ui/ToggleSwitch.qml b/shell/Ui/ToggleSwitch.qml index 22e6461a..f1aa80fa 100644 --- a/shell/Ui/ToggleSwitch.qml +++ b/shell/Ui/ToggleSwitch.qml @@ -84,7 +84,7 @@ Item { : Style.normalFillFor(root.foreground, root.accent) borderSpec: Border.controlSpec(root.checked ? "selected" : "normal", root.foreground, root.accent) - Behavior on color { ColorAnimation { duration: 120 } } + Behavior on color { ColorAnimation { duration: Style.duration(120) } } Rectangle { width: root.knobSize @@ -94,8 +94,8 @@ Item { anchors.verticalCenter: parent.verticalCenter color: root.checked ? Style.selectedStateColor(root.foreground, root.accent) : Qt.darker(root.foreground, 1.25) - Behavior on x { NumberAnimation { duration: 120; easing.type: Easing.OutCubic } } - Behavior on color { ColorAnimation { duration: 120 } } + Behavior on x { NumberAnimation { duration: Style.duration(120); easing.type: Easing.OutCubic } } + Behavior on color { ColorAnimation { duration: Style.duration(120) } } } } diff --git a/shell/Ui/WidgetButton.qml b/shell/Ui/WidgetButton.qml index 87d18050..20c27acd 100644 --- a/shell/Ui/WidgetButton.qml +++ b/shell/Ui/WidgetButton.qml @@ -69,7 +69,7 @@ Item { implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + scaledVerticalPadding * 2) : barSize) Behavior on opacity { - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } Text { @@ -88,7 +88,7 @@ Item { Behavior on color { enabled: !root.bar || root.bar.foregroundAnimationEnabled - ColorAnimation { duration: 160 } + ColorAnimation { duration: Style.duration(160) } } } diff --git a/shell/plugins/agents/Panel.qml b/shell/plugins/agents/Panel.qml index fa676a6a..910e9bb4 100644 --- a/shell/plugins/agents/Panel.qml +++ b/shell/plugins/agents/Panel.qml @@ -788,7 +788,7 @@ Panel { color: meter.alarming ? root.urgent : root.foreground Behavior on width { - NumberAnimation { duration: 160; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(160); easing.type: Easing.OutCubic } } } @@ -837,7 +837,7 @@ Panel { color: dayRow.today ? root.foreground : root.alpha(root.foreground, 0.55) Behavior on width { - NumberAnimation { duration: 160; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(160); easing.type: Easing.OutCubic } } } } @@ -894,7 +894,7 @@ Panel { color: root.alpha(root.foreground, 0.14) Behavior on width { - NumberAnimation { duration: 160; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(160); easing.type: Easing.OutCubic } } } diff --git a/shell/plugins/background/Background.qml b/shell/plugins/background/Background.qml index 7a078858..7dee71dc 100644 --- a/shell/plugins/background/Background.qml +++ b/shell/plugins/background/Background.qml @@ -258,7 +258,7 @@ Item { property: "revealProgress" from: 0 to: 1 - duration: 420 + duration: Style.duration(420) easing.type: Easing.InOutCubic onFinished: { if (root.incomingBackground) { diff --git a/shell/plugins/bar/Bar.qml b/shell/plugins/bar/Bar.qml index ffd33a85..437daef0 100644 --- a/shell/plugins/bar/Bar.qml +++ b/shell/plugins/bar/Bar.qml @@ -80,9 +80,9 @@ Item { property color background: Color.bar.background property color urgent: Color.bar.active - Behavior on barForeground { enabled: root.foregroundAnimationEnabled; ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } } - Behavior on background { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } } - Behavior on urgent { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } } + Behavior on barForeground { enabled: root.foregroundAnimationEnabled; ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } } + Behavior on background { ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } } + Behavior on urgent { ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } } property var tooltipTarget: null property var pendingTooltipTarget: null property string tooltipText: "" @@ -1509,7 +1509,7 @@ Item { opacity: root.barMoveCandidate === modelData ? (root.transparent ? 0.45 : 0.7) : 0 Behavior on opacity { - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } } } @@ -1895,7 +1895,7 @@ Item { z: 50 Behavior on opacity { - NumberAnimation { duration: 120; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(120); easing.type: Easing.OutCubic } } } diff --git a/shell/plugins/bar/widgets/ActiveWindow.qml b/shell/plugins/bar/widgets/ActiveWindow.qml index ff7e83d8..251d48ab 100644 --- a/shell/plugins/bar/widgets/ActiveWindow.qml +++ b/shell/plugins/bar/widgets/ActiveWindow.qml @@ -18,7 +18,7 @@ BarWidget { implicitHeight: barSize Behavior on implicitWidth { - NumberAnimation { duration: 180; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(180); easing.type: Easing.OutCubic } } Item { diff --git a/shell/plugins/bar/widgets/Tray.qml b/shell/plugins/bar/widgets/Tray.qml index 650358c1..dfe11ad7 100644 --- a/shell/plugins/bar/widgets/Tray.qml +++ b/shell/plugins/bar/widgets/Tray.qml @@ -216,7 +216,7 @@ BarWidget { implicitHeight: root.vertical ? trayContent.implicitHeight : root.barSize Behavior on revealProgress { - NumberAnimation { duration: root.animationDuration; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(root.animationDuration); easing.type: Easing.OutCubic } } Loader { diff --git a/shell/plugins/notifications/components/NotificationCard.qml b/shell/plugins/notifications/components/NotificationCard.qml index 64e3870b..d7df8d83 100644 --- a/shell/plugins/notifications/components/NotificationCard.qml +++ b/shell/plugins/notifications/components/NotificationCard.qml @@ -207,7 +207,7 @@ BorderSurface { visible: opacity > 0 opacity: root.hovered ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(100) } } Text { anchors.centerIn: parent diff --git a/shell/plugins/osd/Osd.qml b/shell/plugins/osd/Osd.qml index 0daf676a..69ddc25b 100644 --- a/shell/plugins/osd/Osd.qml +++ b/shell/plugins/osd/Osd.qml @@ -178,7 +178,7 @@ Item { Behavior on width { enabled: root.opened - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } } } diff --git a/shell/plugins/panels/audio/Panel.qml b/shell/plugins/panels/audio/Panel.qml index 26d0c58d..96047e0f 100644 --- a/shell/plugins/panels/audio/Panel.qml +++ b/shell/plugins/panels/audio/Panel.qml @@ -944,7 +944,7 @@ Panel { height: parent.height width: parent.width * Math.max(0, Math.min(1, inputPeakMonitor.peak)) color: root.bar.foreground - Behavior on width { NumberAnimation { duration: 70 } } + Behavior on width { NumberAnimation { duration: Style.duration(70) } } } } } diff --git a/shell/plugins/panels/bluetooth/Panel.qml b/shell/plugins/panels/bluetooth/Panel.qml index 5362d36b..11b0ce0c 100644 --- a/shell/plugins/panels/bluetooth/Panel.qml +++ b/shell/plugins/panels/bluetooth/Panel.qml @@ -603,14 +603,14 @@ Panel { id: phraseSwap PropertyAnimation { target: heroStatus; property: "opacity" - to: 0.0; duration: 180; easing.type: Easing.OutQuad + to: 0.0; duration: Style.duration(180); easing.type: Easing.OutQuad } ScriptAction { script: root.phraseIndex = (root.phraseIndex + 1) % root.activePhrases.length } PropertyAnimation { target: heroStatus; property: "opacity" - to: 1.0; duration: 260; easing.type: Easing.InQuad + to: 1.0; duration: Style.duration(260); easing.type: Easing.InQuad } } diff --git a/shell/plugins/panels/clock/Panel.qml b/shell/plugins/panels/clock/Panel.qml index ec3990e2..ec55afd3 100644 --- a/shell/plugins/panels/clock/Panel.qml +++ b/shell/plugins/panels/clock/Panel.qml @@ -457,7 +457,7 @@ Panel { radius: parent.radius color: Style.selectedStateColor(root.contentForeground, Color.accent) - Behavior on width { NumberAnimation { duration: 160; easing.type: Easing.OutCubic } } + Behavior on width { NumberAnimation { duration: Style.duration(160); easing.type: Easing.OutCubic } } } } } @@ -515,7 +515,7 @@ Panel { radius: parent.radius color: Style.selectedStateColor(root.contentForeground, Color.accent) - Behavior on width { NumberAnimation { duration: 160; easing.type: Easing.OutCubic } } + Behavior on width { NumberAnimation { duration: Style.duration(160); easing.type: Easing.OutCubic } } } } diff --git a/shell/plugins/panels/dropbox/Panel.qml b/shell/plugins/panels/dropbox/Panel.qml index 9dec2473..1a415846 100644 --- a/shell/plugins/panels/dropbox/Panel.qml +++ b/shell/plugins/panels/dropbox/Panel.qml @@ -369,14 +369,14 @@ Panel { id: phraseSwap PropertyAnimation { target: hero; property: "metaOpacity" - to: 0.0; duration: 180; easing.type: Easing.OutQuad + to: 0.0; duration: Style.duration(180); easing.type: Easing.OutQuad } ScriptAction { script: root.phraseIndex = (root.phraseIndex + 1) % root.activePhrases.length } PropertyAnimation { target: hero; property: "metaOpacity" - to: 1.0; duration: 260; easing.type: Easing.InQuad + to: 1.0; duration: Style.duration(260); easing.type: Easing.InQuad } } diff --git a/shell/plugins/panels/elsewhen/CityRow.qml b/shell/plugins/panels/elsewhen/CityRow.qml index 48e19ef7..1eaeddfd 100644 --- a/shell/plugins/panels/elsewhen/CityRow.qml +++ b/shell/plugins/panels/elsewhen/CityRow.qml @@ -79,13 +79,13 @@ Rectangle { // The dragged row follows the pointer one-to-one; the others ease aside. Behavior on y { enabled: !row.dragged - NumberAnimation { duration: 130; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(130); easing.type: Easing.OutCubic } } } ] z: dragged ? 2 : 0 opacity: dragged ? 0.9 : 1 - Behavior on opacity { NumberAnimation { duration: 120 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(120) } } HoverHandler { id: rowHover } @@ -219,7 +219,7 @@ Rectangle { spacing: Style.spacing.sm opacity: parent.greeting ? 0 : 1 visible: opacity > 0 - Behavior on opacity { NumberAnimation { duration: 110 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(110) } } Caption { text: row.ready ? row.rowData.date : "\u2026" @@ -242,7 +242,7 @@ Rectangle { spacing: Style.space(5) opacity: parent.greeting ? 1 : 0 visible: opacity > 0 - Behavior on opacity { NumberAnimation { duration: 110 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(110) } } // Non-Latin scripts fall back per character through fontconfig. Caption { @@ -304,7 +304,7 @@ Rectangle { // Shown on the keyboard's pick too, so Delete's target is plain. opacity: row.removable && row.lit ? 1 : 0 visible: opacity > 0 - Behavior on opacity { NumberAnimation { duration: 120 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(120) } } onClicked: row.panel.removeCityAt(row.index) } diff --git a/shell/plugins/panels/elsewhen/DaylightStrip.qml b/shell/plugins/panels/elsewhen/DaylightStrip.qml index 576817ce..4c171c98 100644 --- a/shell/plugins/panels/elsewhen/DaylightStrip.qml +++ b/shell/plugins/panels/elsewhen/DaylightStrip.qml @@ -115,7 +115,7 @@ Item { color: strip.foreground } - Behavior on x { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } + Behavior on x { NumberAnimation { duration: Style.duration(400); easing.type: Easing.OutCubic } } } } @@ -182,7 +182,7 @@ Item { // Gone rather than faded, so a hidden arrow is also untappable. opacity: strip.hovered && !arrow.covered ? 1 : 0 visible: opacity > 0 - Behavior on opacity { NumberAnimation { duration: 160 } } + Behavior on opacity { NumberAnimation { duration: Style.duration(160) } } // Sunrise sits a little high and sunset a little low: a cue that needs no reading. transform: Translate { y: arrow.rising ? -Style.space(2) : Style.space(2) } diff --git a/shell/plugins/panels/elsewhen/Globe.qml b/shell/plugins/panels/elsewhen/Globe.qml index f14c7f23..335312ec 100644 --- a/shell/plugins/panels/elsewhen/Globe.qml +++ b/shell/plugins/panels/elsewhen/Globe.qml @@ -338,9 +338,9 @@ Item { ParallelAnimation { id: flight NumberAnimation { id: flightSpin; target: root; property: "spin" - duration: 800; easing.type: Easing.OutCubic } + duration: Style.duration(800); easing.type: Easing.OutCubic } NumberAnimation { id: flightViewLat; target: root; property: "viewLat" - duration: 800; easing.type: Easing.OutCubic } + duration: Style.duration(800); easing.type: Easing.OutCubic } } // The throw: spin keeps going after the drag and eases to a stop. diff --git a/shell/plugins/panels/elsewhen/HeroTitle.qml b/shell/plugins/panels/elsewhen/HeroTitle.qml index d868ae4d..9629408f 100644 --- a/shell/plugins/panels/elsewhen/HeroTitle.qml +++ b/shell/plugins/panels/elsewhen/HeroTitle.qml @@ -137,7 +137,7 @@ Column { id: globeSpin target: heroIcon property: "spin" - duration: 1250 + duration: Style.duration(1250) easing.type: Easing.OutQuart } @@ -145,7 +145,7 @@ Column { id: focusSpin target: heroIcon property: "spin" - duration: 700 + duration: Style.duration(700) easing.type: Easing.OutCubic } } diff --git a/shell/plugins/panels/elsewhen/Panel.qml b/shell/plugins/panels/elsewhen/Panel.qml index b36df90d..238b50d8 100644 --- a/shell/plugins/panels/elsewhen/Panel.qml +++ b/shell/plugins/panels/elsewhen/Panel.qml @@ -631,7 +631,7 @@ Panel { // Heavy on the way out, brisk on the way back. Behavior on zoom { NumberAnimation { - duration: root.zoomDuration + duration: Style.duration(root.zoomDuration) easing.type: root.zoomEasing } } @@ -649,14 +649,14 @@ Panel { Behavior on moonDemo { enabled: root.moonShowing - NumberAnimation { duration: 460; easing.type: Easing.InOutSine } + NumberAnimation { duration: Style.duration(460); easing.type: Easing.InOutSine } } NumberAnimation { id: dropAnimation target: root property: "dragOffset" - duration: 150 + duration: Style.duration(150) easing.type: Easing.OutCubic onFinished: root.commitRowDrag() } @@ -805,7 +805,7 @@ Panel { id: scrollAnim target: scroller property: "contentY" - duration: 160 + duration: Style.duration(160) easing.type: Easing.OutCubic } diff --git a/shell/plugins/panels/network/Panel.qml b/shell/plugins/panels/network/Panel.qml index 5976a485..87f160c1 100644 --- a/shell/plugins/panels/network/Panel.qml +++ b/shell/plugins/panels/network/Panel.qml @@ -965,14 +965,14 @@ Panel { id: connectionPhraseSwap PropertyAnimation { target: heroMeta; property: "opacity" - to: 0.0; duration: 180; easing.type: Easing.OutQuad + to: 0.0; duration: Style.duration(180); easing.type: Easing.OutQuad } ScriptAction { script: root.connectionPhraseIndex = (root.connectionPhraseIndex + 1) % root.connectionPhrases.length } PropertyAnimation { target: heroMeta; property: "opacity" - to: 1.0; duration: 260; easing.type: Easing.InQuad + to: 1.0; duration: Style.duration(260); easing.type: Easing.InQuad } } @@ -1472,10 +1472,10 @@ Panel { opacity: root.bandPillsVisible ? 1 : 0 Behavior on height { - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } Behavior on opacity { - NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + NumberAnimation { duration: Style.duration(140); easing.type: Easing.OutCubic } } Row { diff --git a/shell/plugins/panels/power/Panel.qml b/shell/plugins/panels/power/Panel.qml index 9e251fc4..80b8ff67 100644 --- a/shell/plugins/panels/power/Panel.qml +++ b/shell/plugins/panels/power/Panel.qml @@ -246,7 +246,7 @@ Panel { id: phraseSwap PropertyAnimation { target: heroStatus; property: "opacity" - to: 0.0; duration: 180; easing.type: Easing.OutQuad + to: 0.0; duration: Style.duration(180); easing.type: Easing.OutQuad } ScriptAction { script: { @@ -256,7 +256,7 @@ Panel { } PropertyAnimation { target: heroStatus; property: "opacity" - to: 1.0; duration: 260; easing.type: Easing.InQuad + to: 1.0; duration: Style.duration(260); easing.type: Easing.InQuad } } @@ -333,7 +333,7 @@ Panel { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - Behavior on color { ColorAnimation { duration: 200 } } + Behavior on color { ColorAnimation { duration: Style.duration(200) } } } Column { @@ -380,7 +380,7 @@ Panel { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - Behavior on color { ColorAnimation { duration: 200 } } + Behavior on color { ColorAnimation { duration: Style.duration(200) } } } } @@ -405,12 +405,12 @@ Panel { color: root.batteryFillColor width: Math.max(barTrack.height, barTrack.width * root.batteryFraction) - Behavior on width { NumberAnimation { duration: 320; easing.type: Easing.OutCubic } } - Behavior on color { ColorAnimation { duration: 220 } } + Behavior on width { NumberAnimation { duration: Style.duration(320); easing.type: Easing.OutCubic } } + Behavior on color { ColorAnimation { duration: Style.duration(220) } } // Subtle pulse while charging — visible signal that energy is flowing in. SequentialAnimation on opacity { - running: root.charging && !root.fullyCharged && root.opened + running: root.charging && !root.fullyCharged && root.opened && !Style.reduceMotion loops: Animation.Infinite alwaysRunToEnd: true NumberAnimation { from: 1.0; to: 0.55; duration: 950; easing.type: Easing.InOutSine } diff --git a/shell/plugins/panels/tailscale/Panel.qml b/shell/plugins/panels/tailscale/Panel.qml index 9d9c7021..8d0a84cb 100644 --- a/shell/plugins/panels/tailscale/Panel.qml +++ b/shell/plugins/panels/tailscale/Panel.qml @@ -726,14 +726,14 @@ Panel { id: phraseSwap PropertyAnimation { target: hero; property: "metaOpacity" - to: 0.0; duration: 180; easing.type: Easing.OutQuad + to: 0.0; duration: Style.duration(180); easing.type: Easing.OutQuad } ScriptAction { script: root.phraseIndex = (root.phraseIndex + 1) % root.activePhrases.length } PropertyAnimation { target: hero; property: "metaOpacity" - to: 1.0; duration: 260; easing.type: Easing.InQuad + to: 1.0; duration: Style.duration(260); easing.type: Easing.InQuad } } @@ -834,7 +834,7 @@ Panel { opacity: accountRow.switchingAccount ? 0.45 : 1.0 SequentialAnimation on opacity { - running: accountRow.switchingAccount + running: accountRow.switchingAccount && !Style.reduceMotion NumberAnimation { to: 1.0; duration: 420; easing.type: Easing.InOutQuad } NumberAnimation { to: 0.45; duration: 420; easing.type: Easing.InOutQuad } loops: Animation.Infinite @@ -1150,7 +1150,7 @@ Panel { anchors.verticalCenter: parent.verticalCenter NumberAnimation on rotation { - running: exitNodeRow.settingExitNode + running: exitNodeRow.settingExitNode && !Style.reduceMotion from: 0 to: 360 duration: 900 diff --git a/shell/plugins/panels/weather/Panel.qml b/shell/plugins/panels/weather/Panel.qml index c237e223..e9fa5c31 100644 --- a/shell/plugins/panels/weather/Panel.qml +++ b/shell/plugins/panels/weather/Panel.qml @@ -657,7 +657,7 @@ Panel { font.pixelSize: Style.font.bodySmall RotationAnimator on rotation { - running: root.savingLocation + running: root.savingLocation && !Style.reduceMotion from: 0; to: 360 duration: 800 loops: Animation.Infinite diff --git a/shell/plugins/polkit/PolkitAgent.qml b/shell/plugins/polkit/PolkitAgent.qml index 8786eeeb..98d6a0ac 100644 --- a/shell/plugins/polkit/PolkitAgent.qml +++ b/shell/plugins/polkit/PolkitAgent.qml @@ -153,9 +153,9 @@ Item { SequentialAnimation { id: shakeAnimation - NumberAnimation { target: root; property: "shakeOffset"; to: -8; duration: 35; easing.type: Easing.OutQuad } - NumberAnimation { target: root; property: "shakeOffset"; to: 8; duration: 50; easing.type: Easing.InOutQuad } - NumberAnimation { target: root; property: "shakeOffset"; to: 0; duration: 55; easing.type: Easing.OutQuad } + NumberAnimation { target: root; property: "shakeOffset"; to: -8; duration: Style.duration(35); easing.type: Easing.OutQuad } + NumberAnimation { target: root; property: "shakeOffset"; to: 8; duration: Style.duration(50); easing.type: Easing.InOutQuad } + NumberAnimation { target: root; property: "shakeOffset"; to: 0; duration: Style.duration(55); easing.type: Easing.OutQuad } } FileView { path: "/etc/pam.d/polkit-1" diff --git a/shell/plugins/services/media/BarWidget.qml b/shell/plugins/services/media/BarWidget.qml index 02650efc..d8052be3 100644 --- a/shell/plugins/services/media/BarWidget.qml +++ b/shell/plugins/services/media/BarWidget.qml @@ -40,7 +40,7 @@ BarWidget { font.pixelSize: Style.font.body Behavior on color { enabled: !root.bar || root.bar.foregroundAnimationEnabled - ColorAnimation { duration: 160 } + ColorAnimation { duration: Style.duration(160) } } } @@ -65,7 +65,9 @@ BarWidget { NumberAnimation on x { id: scrollAnim - running: labelText.needsScroll && !root.popupOpen && !root.bar.vertical + running: labelText.needsScroll && !root.popupOpen && !root.bar.vertical && !Style.reduceMotion + // Stopped for reduced motion, the title reads from its start again. + onRunningChanged: if (!running && Style.reduceMotion) labelText.x = 0 loops: Animation.Infinite duration: Math.max(6000, labelText.implicitWidth * 25) from: scrollClip.width diff --git a/test/shell.d/toggle-test.sh b/test/shell.d/toggle-test.sh index 0fdf76a2..a4169209 100644 --- a/test/shell.d/toggle-test.sh +++ b/test/shell.d/toggle-test.sh @@ -84,3 +84,30 @@ pass "fullscreen off leaves full screen" HOME="$test_home" omarchy-toggle-fullscreen-desktop on [[ -f $bar_flag && -f $gaps_flag ]] || fail "fullscreen on enters full screen" pass "fullscreen on enters full screen" + +# The flag records animations being off, so on and off are the other way round. +printf '#!/bin/bash\nexit 0\n' >"$stub_bin/omarchy-notification-send" +chmod +x "$stub_bin/omarchy-notification-send" +animations_flag="$test_home/.local/state/omarchy/toggles/hypr/no-animations.lua" + +HOME="$test_home" omarchy-toggle-animations off +[[ -f $animations_flag ]] || fail "animations off places the no-animations flag" +HOME="$test_home" omarchy-toggle-animations on +[[ ! -f $animations_flag ]] || fail "animations on removes the no-animations flag" +HOME="$test_home" omarchy-toggle-animations +[[ -f $animations_flag ]] || fail "toggling animations flips the flag" +pass "animations toggle turns the no-animations flag the right way round" + +# A VM install starts without animations; other machines keep them. +for virt in vm none; do + rm -rf "$test_home/.local/state/omarchy/toggles/hypr" + printf '#!/bin/bash\n[[ %s == vm ]]\n' "$virt" >"$stub_bin/omarchy-hw-vm" + chmod +x "$stub_bin/omarchy-hw-vm" + HOME="$test_home" OMARCHY_PATH="$ROOT" bash "$ROOT/install/user/hardware/vm-no-animations.sh" >/dev/null + if [[ $virt == "vm" ]]; then + [[ -f $animations_flag ]] || fail "a VM install turns animations off" + else + [[ ! -f $animations_flag ]] || fail "a machine that is not a VM keeps its animations" + fi +done +pass "a VM install starts without animations"