diff --git a/quickshell/Modals/Clipboard/ClipboardKeyboardHints.qml b/quickshell/Modals/Clipboard/ClipboardKeyboardHints.qml index 1b6725c3..0f189fa7 100644 --- a/quickshell/Modals/Clipboard/ClipboardKeyboardHints.qml +++ b/quickshell/Modals/Clipboard/ClipboardKeyboardHints.qml @@ -26,9 +26,7 @@ Rectangle { spacing: 2 StyledText { - text: keyboardHints.enterToPaste - ? I18n.tr("↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help", "Keyboard hints when enter-to-paste is enabled") - : I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • F10: Help") + text: keyboardHints.enterToPaste ? I18n.tr("↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help", "Keyboard hints when enter-to-paste is enabled") : I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • F10: Help") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceText anchors.horizontalCenter: parent.horizontalCenter @@ -43,7 +41,7 @@ Rectangle { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modals/Common/DankModalConnected.qml b/quickshell/Modals/Common/DankModalConnected.qml index 42023cf0..27d755f5 100644 --- a/quickshell/Modals/Common/DankModalConnected.qml +++ b/quickshell/Modals/Common/DankModalConnected.qml @@ -154,6 +154,36 @@ Item { }); } + property bool _animSyncQueued: false + function _queueAnimSync() { + if (_animSyncQueued) + return; + _animSyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushAnimSync === "function") + root._flushAnimSync(); + }); + } + function _flushAnimSync() { + _animSyncQueued = false; + _syncModalAnim(); + } + + property bool _bodySyncQueued: false + function _queueBodySync() { + if (_bodySyncQueued) + return; + _bodySyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushBodySync === "function") + root._flushBodySync(); + }); + } + function _flushBodySync() { + _bodySyncQueued = false; + _syncModalBody(); + } + function _syncModalAnim() { if (!frameOwnsConnectedChrome || !_chromeClaimId) return; @@ -185,10 +215,10 @@ Item { onFrameOwnsConnectedChromeChanged: _syncModalChromeState() onResolvedConnectedBarSideChanged: _queueFullSync() onShouldBeVisibleChanged: _queueFullSync() - onAlignedXChanged: _syncModalBody() - onAlignedYChanged: _syncModalBody() - onAlignedWidthChanged: _syncModalBody() - onAlignedHeightChanged: _syncModalBody() + onAlignedXChanged: _queueBodySync() + onAlignedYChanged: _queueBodySync() + onAlignedWidthChanged: _queueBodySync() + onAlignedHeightChanged: _queueBodySync() Component.onDestruction: _releaseModalChrome() @@ -456,8 +486,8 @@ Item { readonly property real s: Math.min(1, modalContainer.scaleValue) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr) blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr) - blurWidth: (root.shouldBeVisible && animatedContent.opacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0 - blurHeight: (root.shouldBeVisible && animatedContent.opacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0 + blurWidth: (root.shouldBeVisible && animatedContent.publishedOpacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0 + blurHeight: (root.shouldBeVisible && animatedContent.publishedOpacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0 blurRadius: root.effectiveCornerRadius } @@ -666,9 +696,9 @@ Item { property real animY: root.shouldBeVisible ? 0 : root.frozenMotionOffsetY onAnimXChanged: if (root.frameOwnsConnectedChrome) - root._syncModalAnim() + root._queueAnimSync() onAnimYChanged: if (root.frameOwnsConnectedChrome) - root._syncModalAnim() + root._queueAnimSync() readonly property real computedScaleCollapsed: root.animationScaleCollapsed property real scaleValue: root.shouldBeVisible ? 1.0 : computedScaleCollapsed @@ -711,11 +741,23 @@ Item { id: animatedContent anchors.fill: parent clip: false + + property real publishedOpacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (root.shouldBeVisible ? 1 : 0) + opacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (root.shouldBeVisible ? 1 : 0) scale: modalContainer.scaleValue transformOrigin: Item.Center Behavior on opacity { + enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect) + OpacityAnimator { + duration: Math.round(Theme.variantDuration(animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) + easing.type: Easing.BezierSpline + easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + } + } + + Behavior on publishedOpacity { enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect) NumberAnimation { duration: Math.round(Theme.variantDuration(animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) diff --git a/quickshell/Modals/Common/DankModalStandalone.qml b/quickshell/Modals/Common/DankModalStandalone.qml index 84d439dc..a035eb98 100644 --- a/quickshell/Modals/Common/DankModalStandalone.qml +++ b/quickshell/Modals/Common/DankModalStandalone.qml @@ -241,8 +241,8 @@ Item { readonly property real s: Math.min(1, modalContainer.scaleValue) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr) blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr) - blurWidth: (shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.width * s : 0 - blurHeight: (shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.height * s : 0 + blurWidth: (shouldBeVisible && animatedContent.publishedOpacity > 0) ? modalContainer.width * s : 0 + blurHeight: (shouldBeVisible && animatedContent.publishedOpacity > 0) ? modalContainer.height * s : 0 blurRadius: root.cornerRadius } @@ -318,7 +318,8 @@ Item { Behavior on opacity { enabled: root.animationsEnabled - DankAnim { + OpacityAnimator { + easing.type: Easing.BezierSpline duration: root.animationDuration easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve } @@ -398,12 +399,24 @@ Item { id: animatedContent anchors.fill: parent clip: false + + property real publishedOpacity: root.shouldBeVisible ? 1 : 0 + opacity: root.shouldBeVisible ? 1 : 0 scale: modalContainer.scaleValue x: Theme.snap(modalContainer.animX, root.dpr) + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5 y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5 Behavior on opacity { + enabled: root.animationsEnabled + OpacityAnimator { + duration: animationDuration + easing.type: Easing.BezierSpline + easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + } + } + + Behavior on publishedOpacity { enabled: root.animationsEnabled NumberAnimation { duration: animationDuration diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml index d6dbb897..859e9398 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml @@ -250,6 +250,36 @@ Item { }); } + property bool _animSyncQueued: false + function _queueAnimSync() { + if (_animSyncQueued) + return; + _animSyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushAnimSync === "function") + root._flushAnimSync(); + }); + } + function _flushAnimSync() { + _animSyncQueued = false; + _syncModalAnim(); + } + + property bool _bodySyncQueued: false + function _queueBodySync() { + if (_bodySyncQueued) + return; + _bodySyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushBodySync === "function") + root._flushBodySync(); + }); + } + function _flushBodySync() { + _bodySyncQueued = false; + _syncModalBody(); + } + function _syncModalAnim() { if (!frameOwnsConnectedChrome || !_chromeClaimId) return; @@ -281,10 +311,10 @@ Item { onFrameOwnsConnectedChromeChanged: _syncModalChromeState() onResolvedConnectedBarSideChanged: _queueFullSync() onSpotlightOpenChanged: _queueFullSync() - onAlignedXChanged: _syncModalBody() - onAlignedYChanged: _syncModalBody() - onAlignedWidthChanged: _syncModalBody() - onAlignedHeightChanged: _syncModalBody() + onAlignedXChanged: _queueBodySync() + onAlignedYChanged: _queueBodySync() + onAlignedWidthChanged: _queueBodySync() + onAlignedHeightChanged: _queueBodySync() Component.onDestruction: _releaseModalChrome() @@ -571,7 +601,8 @@ Item { Behavior on opacity { enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect) - DankAnim { + NumberAnimation { + easing.type: Easing.BezierSpline duration: Math.round(Theme.variantDuration(root.launcherAnimationDuration, launcherMotionVisible) * Theme.variantOpacityDurationScale) easing.bezierCurve: launcherMotionVisible ? root.launcherEnterCurve : root.launcherExitCurve } @@ -597,8 +628,8 @@ Item { readonly property real s: Math.min(1, contentContainer.scaleValue) blurX: root._ccX + root.alignedWidth * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) blurY: root._ccY + root.alignedHeight * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) - blurWidth: (root.spotlightOpen || root.isClosing) && contentWrapper.opacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedWidth * s : 0 - blurHeight: (root.spotlightOpen || root.isClosing) && contentWrapper.opacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedHeight * s : 0 + blurWidth: (root.spotlightOpen || root.isClosing) && contentWrapper.publishedOpacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedWidth * s : 0 + blurHeight: (root.spotlightOpen || root.isClosing) && contentWrapper.publishedOpacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedHeight * s : 0 blurRadius: root.cornerRadius } @@ -716,9 +747,9 @@ Item { property real scaleValue: root._motionActive ? 1.0 : Theme.effectScaleCollapsed onAnimXChanged: if (root.frameOwnsConnectedChrome) - root._syncModalAnim() + root._queueAnimSync() onAnimYChanged: if (root.frameOwnsConnectedChrome) - root._syncModalAnim() + root._queueAnimSync() Behavior on animX { enabled: root.animationsEnabled @@ -769,7 +800,7 @@ Item { id: launcherShadowLayer width: parent.width height: parent.height - opacity: contentWrapper.opacity + opacity: contentWrapper.publishedOpacity scale: contentWrapper.scale x: contentWrapper.x y: contentWrapper.y @@ -787,20 +818,44 @@ Item { id: contentWrapper width: parent.width height: parent.height + + property bool _renderActive: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) || launcherMotionVisible + property real publishedOpacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (launcherMotionVisible ? 1 : 0) + opacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (launcherMotionVisible ? 1 : 0) - visible: opacity > 0 + visible: _renderActive scale: contentContainer.scaleValue x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr) y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr) Behavior on opacity { enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect) - DankAnim { + OpacityAnimator { + easing.type: Easing.BezierSpline duration: Math.round(Theme.variantDuration(root.launcherAnimationDuration, launcherMotionVisible) * Theme.variantOpacityDurationScale) easing.bezierCurve: launcherMotionVisible ? root.launcherEnterCurve : root.launcherExitCurve } } + Behavior on publishedOpacity { + enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect) + NumberAnimation { + easing.type: Easing.BezierSpline + duration: Math.round(Theme.variantDuration(root.launcherAnimationDuration, launcherMotionVisible) * Theme.variantOpacityDurationScale) + easing.bezierCurve: launcherMotionVisible ? root.launcherEnterCurve : root.launcherExitCurve + onRunningChanged: if (!running && contentWrapper.publishedOpacity === 0) + contentWrapper._renderActive = false + } + } + + Connections { + target: root + function onLauncherMotionVisibleChanged() { + if (root.launcherMotionVisible) + contentWrapper._renderActive = true; + } + } + MouseArea { anchors.fill: parent onPressed: mouse => mouse.accepted = true diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml index 17a8031a..a8a2dff6 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml @@ -350,11 +350,11 @@ Item { WindowBlur { targetWindow: launcherWindow - readonly property real s: Math.min(1, modalContainer.scale) + readonly property real s: Math.min(1, modalContainer.publishedScale) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 - blurWidth: (contentVisible && modalContainer.opacity > 0) ? modalContainer.width * s : 0 - blurHeight: (contentVisible && modalContainer.opacity > 0) ? modalContainer.height * s : 0 + blurWidth: (contentVisible && modalContainer.publishedOpacity > 0) ? modalContainer.width * s : 0 + blurHeight: (contentVisible && modalContainer.publishedOpacity > 0) ? modalContainer.height * s : 0 blurRadius: root.cornerRadius } @@ -411,23 +411,55 @@ Item { y: root.contentY width: root.alignedWidth height: root.alignedHeight - visible: contentVisible || opacity > 0 + visible: _renderActive + + property bool _renderActive: contentVisible + property real publishedOpacity: contentVisible ? 1 : 0 + property real publishedScale: contentVisible ? 1 : 0.96 opacity: contentVisible ? 1 : 0 scale: contentVisible ? 1 : 0.96 transformOrigin: Item.Center Behavior on opacity { - DankAnim { + OpacityAnimator { + easing.type: Easing.BezierSpline duration: Theme.modalAnimationDuration easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized } } - Behavior on scale { - DankAnim { + Behavior on publishedOpacity { + NumberAnimation { + easing.type: Easing.BezierSpline duration: Theme.modalAnimationDuration easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized + onRunningChanged: if (!running && modalContainer.publishedOpacity === 0) + modalContainer._renderActive = false + } + } + + Behavior on scale { + ScaleAnimator { + easing.type: Easing.BezierSpline + duration: Theme.modalAnimationDuration + easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized + } + } + + Behavior on publishedScale { + NumberAnimation { + easing.type: Easing.BezierSpline + duration: Theme.modalAnimationDuration + easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized + } + } + + Connections { + target: root + function onContentVisibleChanged() { + if (root.contentVisible) + modalContainer._renderActive = true; } } diff --git a/quickshell/Modals/FileBrowser/FileInfo.qml b/quickshell/Modals/FileBrowser/FileInfo.qml index c41f039b..378a8128 100644 --- a/quickshell/Modals/FileBrowser/FileInfo.qml +++ b/quickshell/Modals/FileBrowser/FileInfo.qml @@ -1,5 +1,4 @@ import QtQuick -import QtCore import Quickshell.Io import qs.Common import qs.Widgets @@ -22,9 +21,9 @@ Rectangle { onShowFileInfoChanged: { if (showFileInfo && currentFileName && currentPath) { - const fullPath = currentPath + "/" + currentFileName - fileStatProcess.selectedFilePath = fullPath - fileStatProcess.running = true + const fullPath = currentPath + "/" + currentFileName; + fileStatProcess.selectedFilePath = fullPath; + fileStatProcess.running = true; } } @@ -38,14 +37,14 @@ Rectangle { stdout: StdioCollector { onStreamFinished: { if (text && text.trim()) { - const parts = text.trim().split('|') + const parts = text.trim().split('|'); if (parts.length >= 4) { fileStatProcess.fileStats = { "modifiedTime": parts[0], "permissions": parts[1], "size": parseInt(parts[2]) || 0, "fullPath": parts[3] - } + }; } } } @@ -60,31 +59,31 @@ Rectangle { onCurrentFileNameChanged: { if (showFileInfo && currentFileName && currentPath) { - const fullPath = currentPath + "/" + currentFileName + const fullPath = currentPath + "/" + currentFileName; if (fullPath !== fileStatProcess.selectedFilePath) { - fileStatProcess.selectedFilePath = fullPath - fileStatProcess.running = true + fileStatProcess.selectedFilePath = fullPath; + fileStatProcess.running = true; } } } function updateFileInfo(filePath, fileName, isDirectory) { if (filePath && filePath !== fileStatProcess.selectedFilePath) { - fileStatProcess.selectedFilePath = filePath - currentFileName = fileName || "" - currentFileIsDir = isDirectory || false + fileStatProcess.selectedFilePath = filePath; + currentFileName = fileName || ""; + currentFileIsDir = isDirectory || false; - let ext = "" + let ext = ""; if (!isDirectory && fileName) { - const lastDot = fileName.lastIndexOf('.') + const lastDot = fileName.lastIndexOf('.'); if (lastDot > 0) { - ext = fileName.substring(lastDot + 1).toLowerCase() + ext = fileName.substring(lastDot + 1).toLowerCase(); } } - currentFileExtension = ext + currentFileExtension = ext; if (showFileInfo) { - fileStatProcess.running = true + fileStatProcess.running = true; } } } @@ -100,10 +99,10 @@ Rectangle { "permissions": "", "extension": "", "position": "N/A" - } + }; } - const hasValidFile = currentFileName !== "" + const hasValidFile = currentFileName !== ""; return { "exists": hasValidFile, "name": hasValidFile ? currentFileName : "Loading...", @@ -113,7 +112,7 @@ Rectangle { "permissions": fileStatProcess.fileStats ? fileStatProcess.fileStats.permissions : "Loading...", "extension": currentFileExtension, "position": sourceFolderModel ? ((selectedIndex + 1) + " of " + sourceFolderModel.count) : "N/A" - } + }; } Column { @@ -209,27 +208,27 @@ Rectangle { function formatFileSize(bytes) { if (bytes === 0 || !bytes) { - return "0 B" + return "0 B"; } - const k = 1024 - const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] - const i = Math.floor(Math.log(bytes) / Math.log(k)) - return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } function formatDateTime(dateTimeString) { if (!dateTimeString) { - return "Unknown" + return "Unknown"; } - const parts = dateTimeString.split(' ') + const parts = dateTimeString.split(' '); if (parts.length >= 2) { - return parts[0] + " " + parts[1].split('.')[0] + return parts[0] + " " + parts[1].split('.')[0]; } - return dateTimeString + return dateTimeString; } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modals/FileBrowser/KeyboardHints.qml b/quickshell/Modals/FileBrowser/KeyboardHints.qml index 1910810d..06d3c778 100644 --- a/quickshell/Modals/FileBrowser/KeyboardHints.qml +++ b/quickshell/Modals/FileBrowser/KeyboardHints.qml @@ -42,7 +42,7 @@ Rectangle { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modals/Greeter/GreeterDoctorPage.qml b/quickshell/Modals/Greeter/GreeterDoctorPage.qml index 5d9b904e..9e403a7a 100644 --- a/quickshell/Modals/Greeter/GreeterDoctorPage.qml +++ b/quickshell/Modals/Greeter/GreeterDoctorPage.qml @@ -180,7 +180,7 @@ Item { opacity: (root.hasRun && !root.isRunning) ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modals/Greeter/GreeterStatusCard.qml b/quickshell/Modals/Greeter/GreeterStatusCard.qml index af0d5d67..15b82d58 100644 --- a/quickshell/Modals/Greeter/GreeterStatusCard.qml +++ b/quickshell/Modals/Greeter/GreeterStatusCard.qml @@ -22,7 +22,7 @@ Rectangle { scale: mouseArea.pressed ? 0.97 : 1 Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modals/PowerMenuModal.qml b/quickshell/Modals/PowerMenuModal.qml index c59037f0..2da5031f 100644 --- a/quickshell/Modals/PowerMenuModal.qml +++ b/quickshell/Modals/PowerMenuModal.qml @@ -772,7 +772,7 @@ DankModal { opacity: root.showHoldHint ? 1 : 0.5 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: 150 } } diff --git a/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml b/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml index 4bde60fa..0f73f478 100644 --- a/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml +++ b/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml @@ -214,13 +214,13 @@ Item { color: root.accentColor Behavior on x { - NumberAnimation { + XAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } } Behavior on y { - NumberAnimation { + YAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -278,13 +278,13 @@ Item { color: root.accentColor Behavior on x { - NumberAnimation { + XAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on y { - NumberAnimation { + YAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/ControlCenter/Components/ActionTile.qml b/quickshell/Modules/ControlCenter/Components/ActionTile.qml index 1b1adb0f..4c126a97 100644 --- a/quickshell/Modules/ControlCenter/Components/ActionTile.qml +++ b/quickshell/Modules/ControlCenter/Components/ActionTile.qml @@ -47,7 +47,7 @@ Rectangle { opacity: mouseArea.containsMouse ? 0.08 : 0.0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration } } diff --git a/quickshell/Modules/ControlCenter/Components/DragDropWidgetWrapper.qml b/quickshell/Modules/ControlCenter/Components/DragDropWidgetWrapper.qml index 7cb0e2cc..1b93a9ae 100644 --- a/quickshell/Modules/ControlCenter/Components/DragDropWidgetWrapper.qml +++ b/quickshell/Modules/ControlCenter/Components/DragDropWidgetWrapper.qml @@ -23,11 +23,15 @@ Item { signal toggleWidgetSize(int index) width: { - const widgetWidth = widgetData?.width || 50 - if (widgetWidth <= 25) return gridCellWidth - else if (widgetWidth <= 50) return gridCellWidth * 2 - else if (widgetWidth <= 75) return gridCellWidth * 3 - else return gridCellWidth * 4 + const widgetWidth = widgetData?.width || 50; + if (widgetWidth <= 25) + return gridCellWidth; + else if (widgetWidth <= 50) + return gridCellWidth * 2; + else if (widgetWidth <= 75) + return gridCellWidth * 3; + else + return gridCellWidth * 4; } height: isSlider ? 16 : gridCellHeight @@ -42,10 +46,14 @@ Item { z: dragArea.drag.active ? 10000 : 1 Behavior on border.width { - NumberAnimation { duration: 150 } + NumberAnimation { + duration: 150 + } } Behavior on opacity { - NumberAnimation { duration: 150 } + OpacityAnimator { + duration: 150 + } } } @@ -58,14 +66,17 @@ Item { property int globalWidgetIndex: root.widgetIndex property int widgetWidth: root.widgetData?.width || 50 - MouseArea { id: editModeBlocker anchors.fill: parent enabled: root.editMode acceptedButtons: Qt.AllButtons - onPressed: function(mouse) { mouse.accepted = true } - onWheel: function(wheel) { wheel.accepted = true } + onPressed: function (mouse) { + mouse.accepted = true; + } + onWheel: function (wheel) { + wheel.accepted = true; + } z: 100 } } @@ -79,19 +90,19 @@ Item { drag.axis: Drag.XAndYAxis drag.smoothed: true - onPressed: function(mouse) { + onPressed: function (mouse) { if (editMode) { - cursorShape = Qt.ClosedHandCursor + cursorShape = Qt.ClosedHandCursor; if (root.gridLayout && root.gridLayout.moveToTop) { - root.gridLayout.moveToTop(root) + root.gridLayout.moveToTop(root); } } } - onReleased: function(mouse) { + onReleased: function (mouse) { if (editMode) { - cursorShape = Qt.OpenHandCursor - root.snapToGrid() + cursorShape = Qt.OpenHandCursor; + root.snapToGrid(); } } } @@ -101,9 +112,11 @@ Item { Drag.hotSpot.y: height / 2 function swapIndices(i, j) { - if (i === j) return; + if (i === j) + return; const arr = SettingsData.controlCenterWidgets; - if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return; + if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) + return; const copy = arr.slice(); const tmp = copy[i]; @@ -114,37 +127,41 @@ Item { } function snapToGrid() { - if (!editMode || !gridLayout) return + if (!editMode || !gridLayout) + return; + const globalPos = root.mapToItem(gridLayout, 0, 0); + const cellWidth = gridLayout.width / gridColumns; + const cellHeight = gridCellHeight + Theme.spacingS; - const globalPos = root.mapToItem(gridLayout, 0, 0) - const cellWidth = gridLayout.width / gridColumns - const cellHeight = gridCellHeight + Theme.spacingS + const centerX = globalPos.x + (root.width / 2); + const centerY = globalPos.y + (root.height / 2); - const centerX = globalPos.x + (root.width / 2) - const centerY = globalPos.y + (root.height / 2) + let targetCol = Math.max(0, Math.floor(centerX / cellWidth)); + let targetRow = Math.max(0, Math.floor(centerY / cellHeight)); - let targetCol = Math.max(0, Math.floor(centerX / cellWidth)) - let targetRow = Math.max(0, Math.floor(centerY / cellHeight)) + targetCol = Math.min(targetCol, gridColumns - 1); - targetCol = Math.min(targetCol, gridColumns - 1) - - const newIndex = findBestInsertionIndex(targetRow, targetCol) + const newIndex = findBestInsertionIndex(targetRow, targetCol); if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) { - swapIndices(widgetIndex, newIndex) + swapIndices(widgetIndex, newIndex); } } function findBestInsertionIndex(targetRow, targetCol) { const widgets = SettingsData.controlCenterWidgets || []; const n = widgets.length; - if (!n || widgetIndex < 0 || widgetIndex >= n) return -1; + if (!n || widgetIndex < 0 || widgetIndex >= n) + return -1; function spanFor(width) { const w = width ?? 50; - if (w <= 25) return 1; - if (w <= 50) return 2; - if (w <= 75) return 3; + if (w <= 25) + return 1; + if (w <= 50) + return 2; + if (w <= 75) + return 3; return 4; } @@ -169,7 +186,13 @@ Item { if (i === widgetIndex) { draggedOrigKey = centerKey; } else { - pos.push({ index: i, row, startCol, span, centerKey }); + pos.push({ + index: i, + row, + startCol, + span, + centerKey + }); } col += span; @@ -179,7 +202,8 @@ Item { } } - if (pos.length === 0) return -1; + if (pos.length === 0) + return -1; const centerColCoord = targetCol + 0.5; const targetKey = targetRow * cols + centerColCoord; @@ -192,15 +216,20 @@ Item { } let lo = 0, hi = pos.length - 1; - if (targetKey <= pos[0].centerKey) return pos[0].index; - if (targetKey >= pos[hi].centerKey) return pos[hi].index; + if (targetKey <= pos[0].centerKey) + return pos[0].index; + if (targetKey >= pos[hi].centerKey) + return pos[hi].index; while (lo <= hi) { const mid = (lo + hi) >> 1; const mk = pos[mid].centerKey; - if (targetKey < mk) hi = mid - 1; - else if (targetKey > mk) lo = mid + 1; - else return pos[mid].index; + if (targetKey < mk) + hi = mid - 1; + else if (targetKey > mk) + lo = mid + 1; + else + return pos[mid].index; } const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false; return (movingUp ? pos[lo].index : pos[hi].index); @@ -240,11 +269,11 @@ Item { currentSize: root.widgetData?.width || 50 isSlider: root.isSlider widgetIndex: root.widgetIndex - onSizeChanged: (newSize) => { - var widgets = SettingsData.controlCenterWidgets.slice() + onSizeChanged: newSize => { + var widgets = SettingsData.controlCenterWidgets.slice(); if (widgetIndex >= 0 && widgetIndex < widgets.length) { - widgets[widgetIndex].width = newSize - SettingsData.set("controlCenterWidgets", widgets) + widgets[widgetIndex].width = newSize; + SettingsData.set("controlCenterWidgets", widgets); } } } @@ -270,7 +299,9 @@ Item { } Behavior on opacity { - NumberAnimation { duration: 150 } + OpacityAnimator { + duration: 150 + } } } @@ -283,7 +314,9 @@ Item { z: -1 Behavior on color { - ColorAnimation { duration: Theme.shortDuration } + ColorAnimation { + duration: Theme.shortDuration + } } } } diff --git a/quickshell/Modules/ControlCenter/ControlCenterPopout.qml b/quickshell/Modules/ControlCenter/ControlCenterPopout.qml index 5898406a..e556b231 100644 --- a/quickshell/Modules/ControlCenter/ControlCenterPopout.qml +++ b/quickshell/Modules/ControlCenter/ControlCenterPopout.qml @@ -195,7 +195,7 @@ DankPopout { Behavior on opacity { enabled: !Theme.isDirectionalEffect - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve diff --git a/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml b/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml index 5d9e4313..96f92c4e 100644 --- a/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml +++ b/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml @@ -127,7 +127,7 @@ Item { opacity: modalVisible ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } @@ -316,14 +316,14 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml b/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml index 74679cc8..cc78435b 100644 --- a/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml +++ b/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml @@ -64,7 +64,7 @@ Rectangle { opacity: 0.08 antialiasing: true Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration } } diff --git a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml index fbd8709a..c6ce36bd 100644 --- a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml @@ -53,7 +53,7 @@ Rectangle { opacity: mouseArea.containsMouse ? 0.08 : 0.0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration } } diff --git a/quickshell/Modules/DankBar/Widgets/AppsDock.qml b/quickshell/Modules/DankBar/Widgets/AppsDock.qml index a8df08ba..8d3eb916 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDock.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDock.qml @@ -474,14 +474,14 @@ BasePill { height: (isInOverflow && !root.overflowExpanded) ? 0 : visualHeight Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } @@ -666,7 +666,7 @@ BasePill { transformOrigin: Item.Center scale: appItem.enlargeScale Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: 120 easing.type: Easing.OutCubic } @@ -711,7 +711,7 @@ BasePill { transformOrigin: Item.Center scale: appItem.enlargeScale Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: 120 easing.type: Easing.OutCubic } @@ -734,7 +734,7 @@ BasePill { transformOrigin: Item.Center scale: appItem.enlargeScale Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: 120 easing.type: Easing.OutCubic } @@ -758,7 +758,7 @@ BasePill { transformOrigin: Item.Center scale: appItem.enlargeScale Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: 120 easing.type: Easing.OutCubic } diff --git a/quickshell/Modules/DankBar/Widgets/AppsDockOverflowButton.qml b/quickshell/Modules/DankBar/Widgets/AppsDockOverflowButton.qml index 82c78d06..8be4aaa2 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDockOverflowButton.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDockOverflowButton.qml @@ -36,7 +36,7 @@ Item { rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90) Behavior on rotation { - NumberAnimation { + RotationAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } diff --git a/quickshell/Modules/DankBar/Widgets/CapsLockIndicator.qml b/quickshell/Modules/DankBar/Widgets/CapsLockIndicator.qml index ada41dd8..4c64c9fb 100644 --- a/quickshell/Modules/DankBar/Widgets/CapsLockIndicator.qml +++ b/quickshell/Modules/DankBar/Widgets/CapsLockIndicator.qml @@ -39,7 +39,7 @@ BasePill { ] Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/DankBar/Widgets/Media.qml b/quickshell/Modules/DankBar/Widgets/Media.qml index a1f7f05e..5af22c83 100644 --- a/quickshell/Modules/DankBar/Widgets/Media.qml +++ b/quickshell/Modules/DankBar/Widgets/Media.qml @@ -173,7 +173,7 @@ BasePill { opacity: root.playerAvailable ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml b/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml index a09aa55f..dab9d96a 100644 --- a/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml +++ b/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml @@ -50,7 +50,7 @@ BasePill { ] Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -238,7 +238,7 @@ BasePill { Behavior on opacity { enabled: hasActivePrivacy && root.visible - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml index 63ea0fee..7e96f862 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml @@ -271,7 +271,7 @@ BasePill { ] Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -625,7 +625,7 @@ BasePill { opacity: root.inlineExpanded ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1214,14 +1214,14 @@ BasePill { scale: root.menuOpen ? 1 : 0.85 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } @@ -1666,14 +1666,14 @@ BasePill { scale: menuRoot.showMenu ? 1 : 0.85 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml index ac53c3fe..0684f9cb 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml @@ -36,7 +36,7 @@ BasePill { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/DankBar/Widgets/Vpn.qml b/quickshell/Modules/DankBar/Widgets/Vpn.qml index fec33d0f..3c74cd3c 100644 --- a/quickshell/Modules/DankBar/Widgets/Vpn.qml +++ b/quickshell/Modules/DankBar/Widgets/Vpn.qml @@ -47,7 +47,7 @@ BasePill { anchors.centerIn: parent Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Easing.InOutQuad } diff --git a/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml b/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml index 3721e350..989a013a 100644 --- a/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml +++ b/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml @@ -1471,7 +1471,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml index 4f9e3fbe..5524e24d 100644 --- a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml +++ b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml @@ -97,7 +97,8 @@ Item { Behavior on opacity { enabled: !Theme.isDirectionalEffect - DankAnim { + NumberAnimation { + easing.type: Easing.BezierSpline duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1) * Theme.variantOpacityDurationScale) easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve } @@ -238,7 +239,8 @@ Item { Behavior on opacity { enabled: !Theme.isDirectionalEffect - DankAnim { + NumberAnimation { + easing.type: Easing.BezierSpline duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2) * Theme.variantOpacityDurationScale) easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve } @@ -393,7 +395,8 @@ Item { Behavior on opacity { enabled: !Theme.isDirectionalEffect - DankAnim { + NumberAnimation { + easing.type: Easing.BezierSpline duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3) * Theme.variantOpacityDurationScale) easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve } diff --git a/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml b/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml index 5d2407c1..b867af68 100644 --- a/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml +++ b/quickshell/Modules/DankDash/Overview/CalendarOverviewCard.qml @@ -402,7 +402,7 @@ Rectangle { opacity: isToday ? 0.9 : 0.7 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Dock/DockApps.qml b/quickshell/Modules/Dock/DockApps.qml index e11296c8..e8670745 100644 --- a/quickshell/Modules/Dock/DockApps.qml +++ b/quickshell/Modules/Dock/DockApps.qml @@ -514,14 +514,14 @@ Item { height: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)) Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } diff --git a/quickshell/Modules/Dock/DockOverflowButton.qml b/quickshell/Modules/Dock/DockOverflowButton.qml index 0d6b439e..08940b15 100644 --- a/quickshell/Modules/Dock/DockOverflowButton.qml +++ b/quickshell/Modules/Dock/DockOverflowButton.qml @@ -35,7 +35,7 @@ Item { rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90) Behavior on rotation { - NumberAnimation { + RotationAnimator { duration: Theme.shortDuration easing.type: Easing.OutCubic } diff --git a/quickshell/Modules/Greetd/GreeterContent.qml b/quickshell/Modules/Greetd/GreeterContent.qml index c20c9bb4..647bc49c 100644 --- a/quickshell/Modules/Greetd/GreeterContent.qml +++ b/quickshell/Modules/Greetd/GreeterContent.qml @@ -1,4 +1,3 @@ -import QtCore import QtQuick import QtQuick.Effects import QtQuick.Layouts @@ -119,15 +118,7 @@ Item { function greeterPamStackHasModule(moduleName) { if (pamModuleEnabled(greetdPamText, moduleName)) return true; - const includedPamStacks = [ - ["system-auth", systemAuthPamText], - ["common-auth", commonAuthPamText], - ["password-auth", passwordAuthPamText], - ["system-login", systemLoginPamText], - ["system-local-login", systemLocalLoginPamText], - ["common-auth-pc", commonAuthPcPamText], - ["login", loginPamText] - ]; + const includedPamStacks = [["system-auth", systemAuthPamText], ["common-auth", commonAuthPamText], ["password-auth", passwordAuthPamText], ["system-login", systemLoginPamText], ["system-local-login", systemLocalLoginPamText], ["common-auth-pc", commonAuthPcPamText], ["login", loginPamText]]; for (let i = 0; i < includedPamStacks.length; i++) { const stack = includedPamStacks[i]; if (pamTextIncludesFile(greetdPamText, stack[0]) && pamModuleEnabled(stack[1], moduleName)) @@ -609,13 +600,7 @@ Item { running: false // sh wrapper: emits PROBE_UNAVAILABLE if gdbus is absent or fprintd unreachable, // keeping the PAM-only fallback active in those cases. - command: ["sh", "-c", - "command -v gdbus >/dev/null 2>&1 || { echo PROBE_UNAVAILABLE; exit 0; }; " + - "gdbus call --system " + - "--dest net.reactivated.Fprint " + - "--object-path /net/reactivated/Fprint/Manager " + - "--method net.reactivated.Fprint.Manager.GetDevices 2>/dev/null " + - "|| echo PROBE_UNAVAILABLE"] + command: ["sh", "-c", "command -v gdbus >/dev/null 2>&1 || { echo PROBE_UNAVAILABLE; exit 0; }; " + "gdbus call --system " + "--dest net.reactivated.Fprint " + "--object-path /net/reactivated/Fprint/Manager " + "--method net.reactivated.Fprint.Manager.GetDevices 2>/dev/null " + "|| echo PROBE_UNAVAILABLE"] stdout: StdioCollector { onStreamFinished: { if (text.includes("PROBE_UNAVAILABLE")) @@ -625,7 +610,7 @@ Item { root.maybeAutoStartExternalAuth(); } } - onExited: function(exitCode, exitStatus) { + onExited: function (exitCode, exitStatus) { if (!root.fprintdProbeComplete) root.maybeAutoStartExternalAuth(); // PAM-only fallback stays active } @@ -729,7 +714,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -1027,7 +1012,7 @@ Item { opacity: (GreeterState.showPasswordInput ? GreeterState.passwordBuffer.length === 0 : GreeterState.usernameInput.length === 0) ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -1064,7 +1049,7 @@ Item { horizontalAlignment: implicitWidth > width ? Text.AlignRight : Text.AlignLeft Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -1136,7 +1121,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1166,7 +1151,7 @@ Item { opacity: root.authFeedbackMessage !== "" ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1184,7 +1169,7 @@ Item { enabled: GreeterState.showPasswordInput Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -1754,7 +1739,7 @@ Item { authTimeout.interval = defaultAuthTimeoutMs; authTimeout.stop(); if (resumePasswordSubmit) { - Qt.callLater(function() { + Qt.callLater(function () { root.startAuthSession(true); }); return; diff --git a/quickshell/Modules/Lock/LockPowerMenu.qml b/quickshell/Modules/Lock/LockPowerMenu.qml index 0a0ee849..04bbc919 100644 --- a/quickshell/Modules/Lock/LockPowerMenu.qml +++ b/quickshell/Modules/Lock/LockPowerMenu.qml @@ -779,7 +779,7 @@ Rectangle { opacity: root.showHoldHint ? 1 : 0.5 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: 150 } } diff --git a/quickshell/Modules/Lock/LockScreenContent.qml b/quickshell/Modules/Lock/LockScreenContent.qml index 1a5bb5d7..4bcfe1cd 100644 --- a/quickshell/Modules/Lock/LockScreenContent.qml +++ b/quickshell/Modules/Lock/LockScreenContent.qml @@ -214,7 +214,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -740,7 +740,7 @@ Item { opacity: pam.passwd.active ? 0 : 1 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -879,7 +879,7 @@ Item { opacity: (demoMode || root.passwordBuffer.length === 0) ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -916,7 +916,7 @@ Item { horizontalAlignment: implicitWidth > width ? Text.AlignRight : Text.AlignLeft Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } @@ -1053,7 +1053,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1084,7 +1084,7 @@ Item { opacity: text.length > 0 ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1114,7 +1114,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Lock/LockSurface.qml b/quickshell/Modules/Lock/LockSurface.qml index 91057ce0..357980d3 100644 --- a/quickshell/Modules/Lock/LockSurface.qml +++ b/quickshell/Modules/Lock/LockSurface.qml @@ -47,7 +47,7 @@ FocusScope { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: 200 } } diff --git a/quickshell/Modules/Notepad/NotepadTextEditor.qml b/quickshell/Modules/Notepad/NotepadTextEditor.qml index 7ea0b27d..2a7902ad 100644 --- a/quickshell/Modules/Notepad/NotepadTextEditor.qml +++ b/quickshell/Modules/Notepad/NotepadTextEditor.qml @@ -1,19 +1,18 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls import QtQuick.Layouts -import Quickshell.Io import qs.Common import qs.Services import qs.Widgets -pragma ComponentBehavior: Bound - Column { id: root Component.onCompleted: { if (PluginService.isPluginLoaded("dankNotepadModule")) { - pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "") + pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", ""); } } @@ -33,65 +32,57 @@ Column { property string lastPluginContent: "" property int loadRequestId: 0 - signal saveRequested() - signal openRequested() - signal newRequested() - signal previewRequested() - signal escapePressed() - signal contentChanged() - signal settingsRequested() + signal saveRequested + signal openRequested + signal newRequested + signal previewRequested + signal escapePressed + signal contentChanged + signal settingsRequested function hasUnsavedChanges() { if (!currentTab || !contentLoaded) { - return false + return false; } if (currentTab.isTemporary) { - return textArea.text.length > 0 + return textArea.text.length > 0; } - return textArea.text !== lastSavedContent + return textArea.text !== lastSavedContent; } function loadCurrentTabContent() { - if (!currentTab) return - - const requestedTabId = currentTab.id - const requestId = ++loadRequestId - contentLoaded = false - NotepadStorageService.loadTabContent( - NotepadStorageService.currentTabIndex, - (content) => { - const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex - ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] - : null - if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId) - return - - lastSavedContent = content - textArea.text = content - contentLoaded = true - syncContentToPlugin() - } - ) + if (!currentTab) + return; + const requestedTabId = currentTab.id; + const requestId = ++loadRequestId; + contentLoaded = false; + NotepadStorageService.loadTabContent(NotepadStorageService.currentTabIndex, content => { + const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null; + if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId) + return; + lastSavedContent = content; + textArea.text = content; + contentLoaded = true; + syncContentToPlugin(); + }); } function saveCurrentTabContent() { - if (!currentTab || !contentLoaded) return - - NotepadStorageService.saveTabContent( - NotepadStorageService.currentTabIndex, - textArea.text - ) - lastSavedContent = textArea.text + if (!currentTab || !contentLoaded) + return; + NotepadStorageService.saveTabContent(NotepadStorageService.currentTabIndex, textArea.text); + lastSavedContent = textArea.text; } function autoSaveToSession() { - if (!currentTab || !contentLoaded) return - saveCurrentTabContent() + if (!currentTab || !contentLoaded) + return; + saveCurrentTabContent(); } function setTextDocumentLineHeight() { - return + return; } property string lastTextForLineModel: "" @@ -99,147 +90,146 @@ Column { function updateLineModel() { if (!SettingsData.notepadShowLineNumbers) { - lineModel = [] - lastTextForLineModel = "" - return + lineModel = []; + lastTextForLineModel = ""; + return; } if (textArea.text !== lastTextForLineModel || lineModel.length === 0) { - lastTextForLineModel = textArea.text - lineModel = textArea.text.split('\n') + lastTextForLineModel = textArea.text; + lineModel = textArea.text.split('\n'); } } function performSearch() { - let matches = [] - currentMatchIndex = -1 + let matches = []; + currentMatchIndex = -1; if (!searchQuery || searchQuery.length === 0) { - searchMatches = [] - matchCount = 0 - textArea.select(0, 0) - return + searchMatches = []; + matchCount = 0; + textArea.select(0, 0); + return; } - const text = textArea.text - const query = searchQuery.toLowerCase() - let index = 0 + const text = textArea.text; + const query = searchQuery.toLowerCase(); + let index = 0; while (index < text.length) { - const foundIndex = text.toLowerCase().indexOf(query, index) - if (foundIndex === -1) break - + const foundIndex = text.toLowerCase().indexOf(query, index); + if (foundIndex === -1) + break; matches.push({ start: foundIndex, end: foundIndex + searchQuery.length - }) - index = foundIndex + 1 + }); + index = foundIndex + 1; } - searchMatches = matches - matchCount = matches.length + searchMatches = matches; + matchCount = matches.length; if (matchCount > 0) { - currentMatchIndex = 0 - highlightCurrentMatch() + currentMatchIndex = 0; + highlightCurrentMatch(); } else { - textArea.select(0, 0) + textArea.select(0, 0); } } function highlightCurrentMatch() { if (currentMatchIndex >= 0 && currentMatchIndex < searchMatches.length) { - const match = searchMatches[currentMatchIndex] + const match = searchMatches[currentMatchIndex]; - textArea.cursorPosition = match.start - textArea.moveCursorSelection(match.end, TextEdit.SelectCharacters) + textArea.cursorPosition = match.start; + textArea.moveCursorSelection(match.end, TextEdit.SelectCharacters); - const flickable = textArea.parent + const flickable = textArea.parent; if (flickable && flickable.contentY !== undefined) { - const lineHeight = textArea.font.pixelSize * 1.5 - const approxLine = textArea.text.substring(0, match.start).split('\n').length - const targetY = approxLine * lineHeight - flickable.height / 2 - flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height)) + const lineHeight = textArea.font.pixelSize * 1.5; + const approxLine = textArea.text.substring(0, match.start).split('\n').length; + const targetY = approxLine * lineHeight - flickable.height / 2; + flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height)); } } } function findNext() { - if (matchCount === 0 || searchMatches.length === 0) return - - currentMatchIndex = (currentMatchIndex + 1) % matchCount - highlightCurrentMatch() + if (matchCount === 0 || searchMatches.length === 0) + return; + currentMatchIndex = (currentMatchIndex + 1) % matchCount; + highlightCurrentMatch(); } function findPrevious() { - if (matchCount === 0 || searchMatches.length === 0) return - - currentMatchIndex = currentMatchIndex <= 0 ? matchCount - 1 : currentMatchIndex - 1 - highlightCurrentMatch() + if (matchCount === 0 || searchMatches.length === 0) + return; + currentMatchIndex = currentMatchIndex <= 0 ? matchCount - 1 : currentMatchIndex - 1; + highlightCurrentMatch(); } function showSearch() { - searchVisible = true + searchVisible = true; Qt.callLater(() => { - searchField.forceActiveFocus() - }) + searchField.forceActiveFocus(); + }); } function togglePreview() { if (!inlinePreviewVisible) { - inlinePreviewVisible = true - previewMode = "split" + inlinePreviewVisible = true; + previewMode = "split"; } else if (previewMode === "split") { - previewMode = "full" + previewMode = "full"; } else { - inlinePreviewVisible = false - previewMode = "split" + inlinePreviewVisible = false; + previewMode = "split"; } - syncContentToPlugin() + syncContentToPlugin(); } function renderPreviewHtml() { - if (!inlinePreviewVisible) return "" - return pluginHighlightedHtml.length > 0 ? pluginHighlightedHtml : "

Rendering preview…

" + if (!inlinePreviewVisible) + return ""; + return pluginHighlightedHtml.length > 0 ? pluginHighlightedHtml : "

Rendering preview…

"; } function syncContentToPlugin() { if (!PluginService.isPluginLoaded("dankNotepadModule")) - return - + return; if (!currentTab) - return - - const filePath = currentTab?.filePath || "" - const ext = filePath.split('.').pop().toLowerCase() - const content = textArea.text + return; + const filePath = currentTab?.filePath || ""; + const ext = filePath.split('.').pop().toLowerCase(); + const content = textArea.text; if (content === lastPluginContent && SettingsData.getBuiltInPluginSetting("dankNotepadModule", "previewActive", false) === inlinePreviewVisible) { - return + return; } - lastPluginContent = content - SettingsData.setBuiltInPluginSetting("dankNotepadModule", "previewActive", inlinePreviewVisible) - SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFilePath", filePath) - SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFileExtension", ext) - SettingsData.setBuiltInPluginSetting("dankNotepadModule", "sourceContent", content) - SettingsData.setBuiltInPluginSetting("dankNotepadModule", "updatedAt", Date.now()) + lastPluginContent = content; + SettingsData.setBuiltInPluginSetting("dankNotepadModule", "previewActive", inlinePreviewVisible); + SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFilePath", filePath); + SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFileExtension", ext); + SettingsData.setBuiltInPluginSetting("dankNotepadModule", "sourceContent", content); + SettingsData.setBuiltInPluginSetting("dankNotepadModule", "updatedAt", Date.now()); } function hideSearch() { - searchVisible = false - searchQuery = "" - searchMatches = [] - matchCount = 0 - currentMatchIndex = -1 - textArea.select(0, 0) - textArea.forceActiveFocus() + searchVisible = false; + searchQuery = ""; + searchMatches = []; + matchCount = 0; + currentMatchIndex = -1; + textArea.select(0, 0); + textArea.forceActiveFocus(); } function copyPlainTextToClipboard() { - if (!inlinePreviewVisible || !textArea.text) return - - const content = textArea.text + if (!inlinePreviewVisible || !textArea.text) + return; + const content = textArea.text; if (content.length > 0) { const proc = Qt.createQmlObject(` import QtQuick @@ -249,22 +239,19 @@ Column { command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"] environment: { "CONTENT": content } running: false - }`, - root, - "copyProc" - ) - proc.content = content - proc.running = true + }`, root, "copyProc"); + proc.content = content; + proc.running = true; proc.exited.connect(() => { - ToastService.showInfo(I18n.tr("Copied to clipboard")) - proc.destroy() - }) + ToastService.showInfo(I18n.tr("Copied to clipboard")); + proc.destroy(); + }); } } function copyHtmlToClipboard() { - if (!inlinePreviewVisible || !pluginHighlightedHtml) return - + if (!inlinePreviewVisible || !pluginHighlightedHtml) + return; if (pluginHighlightedHtml.length > 0) { const proc = Qt.createQmlObject(` import QtQuick @@ -274,16 +261,13 @@ Column { command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"] environment: { "CONTENT": content } running: false - }`, - root, - "copyProcHtml" - ) - proc.content = pluginHighlightedHtml - proc.running = true + }`, root, "copyProcHtml"); + proc.content = pluginHighlightedHtml; + proc.running = true; proc.exited.connect(() => { - ToastService.showInfo(I18n.tr("HTML copied to clipboard")) - proc.destroy() - }) + ToastService.showInfo(I18n.tr("HTML copied to clipboard")); + proc.destroy(); + }); } } @@ -301,7 +285,7 @@ Column { radius: Theme.cornerRadius Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -334,43 +318,43 @@ Column { clip: true Component.onCompleted: { - text = root.searchQuery + text = root.searchQuery; } Connections { target: root function onSearchQueryChanged() { if (searchField.text !== root.searchQuery) { - searchField.text = root.searchQuery + searchField.text = root.searchQuery; } } } onTextChanged: { if (root.searchQuery !== text) { - root.searchQuery = text - root.performSearch() + root.searchQuery = text; + root.performSearch(); } } Keys.onEscapePressed: event => { - root.hideSearch() - event.accepted = true + root.hideSearch(); + event.accepted = true; } Keys.onReturnPressed: event => { if (event.modifiers & Qt.ShiftModifier) { - root.findPrevious() + root.findPrevious(); } else { - root.findNext() + root.findNext(); } - event.accepted = true + event.accepted = true; } Keys.onEnterPressed: event => { if (event.modifiers & Qt.ShiftModifier) { - root.findPrevious() + root.findPrevious(); } else { - root.findNext() + root.findNext(); } - event.accepted = true + event.accepted = true; } } @@ -541,31 +525,41 @@ Column { SequentialAnimation on opacity { running: textArea.activeFocus loops: Animation.Infinite - PropertyAnimation { from: 1.0; to: 0.0; duration: 650; easing.type: Easing.InOutQuad } - PropertyAnimation { from: 0.0; to: 1.0; duration: 650; easing.type: Easing.InOutQuad } + PropertyAnimation { + from: 1.0 + to: 0.0 + duration: 650 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + from: 0.0 + to: 1.0 + duration: 650 + easing.type: Easing.InOutQuad + } } } Component.onCompleted: { - loadCurrentTabContent() - setTextDocumentLineHeight() - root.updateLineModel() + loadCurrentTabContent(); + setTextDocumentLineHeight(); + root.updateLineModel(); Qt.callLater(() => { - textArea.forceActiveFocus() - }) + textArea.forceActiveFocus(); + }); } Connections { target: NotepadStorageService function onCurrentTabIndexChanged() { - loadCurrentTabContent() + loadCurrentTabContent(); Qt.callLater(() => { - textArea.forceActiveFocus() - }) + textArea.forceActiveFocus(); + }); } function onTabsChanged() { if (NotepadStorageService.tabs.length > 0 && !contentLoaded) { - loadCurrentTabContent() + loadCurrentTabContent(); } } } @@ -573,53 +567,53 @@ Column { Connections { target: SettingsData function onNotepadShowLineNumbersChanged() { - root.updateLineModel() + root.updateLineModel(); } } onTextChanged: { if (contentLoaded && text !== lastSavedContent) { - autoSaveTimer.restart() + autoSaveTimer.restart(); } - root.contentChanged() - root.updateLineModel() - pluginSyncTimer.restart() + root.contentChanged(); + root.updateLineModel(); + pluginSyncTimer.restart(); } - Keys.onEscapePressed: (event) => { - root.escapePressed() - event.accepted = true + Keys.onEscapePressed: event => { + root.escapePressed(); + event.accepted = true; } - Keys.onPressed: (event) => { + Keys.onPressed: event => { if (event.modifiers & Qt.ControlModifier) { switch (event.key) { case Qt.Key_S: - event.accepted = true - root.saveRequested() - break + event.accepted = true; + root.saveRequested(); + break; case Qt.Key_O: - event.accepted = true - root.openRequested() - break + event.accepted = true; + root.openRequested(); + break; case Qt.Key_N: - event.accepted = true - root.newRequested() - break + event.accepted = true; + root.newRequested(); + break; case Qt.Key_A: - event.accepted = true - textArea.selectAll() - break + event.accepted = true; + textArea.selectAll(); + break; case Qt.Key_F: - event.accepted = true - root.showSearch() - break + event.accepted = true; + root.showSearch(); + break; case Qt.Key_P: if (PluginService.isPluginLoaded("dankNotepadModule")) { - event.accepted = true - root.previewRequested() + event.accepted = true; + root.previewRequested(); } - break + break; } } } @@ -845,19 +839,16 @@ Column { StyledText { text: { const len = textArea.text.length; - if (len === 0) return I18n.tr("Empty"); - return len === 1 - ? I18n.tr("%1 character").arg(len) - : I18n.tr("%1 characters").arg(len); + if (len === 0) + return I18n.tr("Empty"); + return len === 1 ? I18n.tr("%1 character").arg(len) : I18n.tr("%1 characters").arg(len); } font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium } StyledText { - text: textArea.lineCount === 1 - ? I18n.tr("Line: %1").arg(textArea.lineCount) - : I18n.tr("Lines: %1").arg(textArea.lineCount) + text: textArea.lineCount === 1 ? I18n.tr("Line: %1").arg(textArea.lineCount) : I18n.tr("Lines: %1").arg(textArea.lineCount) font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceTextMedium visible: textArea.text.length > 0 @@ -867,29 +858,29 @@ Column { StyledText { text: { if (autoSaveTimer.running) { - return I18n.tr("Auto-saving...") + return I18n.tr("Auto-saving..."); } if (hasUnsavedChanges()) { if (currentTab && currentTab.isTemporary) { - return I18n.tr("Unsaved note...") + return I18n.tr("Unsaved note..."); } else { - return I18n.tr("Unsaved changes") + return I18n.tr("Unsaved changes"); } } else { - return I18n.tr("Saved") + return I18n.tr("Saved"); } } font.pixelSize: Theme.fontSizeSmall color: { if (autoSaveTimer.running) { - return Theme.primary + return Theme.primary; } if (hasUnsavedChanges()) { - return Theme.warning + return Theme.warning; } else { - return Theme.success + return Theme.success; } } opacity: textArea.text.length > 0 ? 1.0 : 0.0 @@ -902,7 +893,7 @@ Column { interval: 2000 repeat: false onTriggered: { - autoSaveToSession() + autoSaveToSession(); } } @@ -917,7 +908,7 @@ Column { target: SettingsData function onBuiltInPluginSettingsChanged() { if (PluginService.isPluginLoaded("dankNotepadModule")) { - pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "") + pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", ""); } } } diff --git a/quickshell/Modules/Notifications/Center/HistoryNotificationList.qml b/quickshell/Modules/Notifications/Center/HistoryNotificationList.qml index 69fea418..d95fe1aa 100644 --- a/quickshell/Modules/Notifications/Center/HistoryNotificationList.qml +++ b/quickshell/Modules/Notifications/Center/HistoryNotificationList.qml @@ -283,7 +283,7 @@ Item { Behavior on x { enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized - NumberAnimation { + XAnimator { duration: Theme.notificationExitDuration easing.type: Theme.standardEasing } @@ -291,7 +291,7 @@ Item { Behavior on opacity { enabled: delegateRoot.__delegateInitialized - NumberAnimation { + OpacityAnimator { duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0 } } diff --git a/quickshell/Modules/Notifications/Center/KeyboardNavigatedNotificationList.qml b/quickshell/Modules/Notifications/Center/KeyboardNavigatedNotificationList.qml index 9587e7a6..a88a4ff5 100644 --- a/quickshell/Modules/Notifications/Center/KeyboardNavigatedNotificationList.qml +++ b/quickshell/Modules/Notifications/Center/KeyboardNavigatedNotificationList.qml @@ -226,7 +226,7 @@ DankListView { Behavior on x { enabled: !swipeDragHandler.active && !delegateRoot.isDismissing && (listView.swipingCardIndex === -1 || !delegateRoot.isAdjacentToSwipe) && listView.listInitialized - NumberAnimation { + XAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -234,7 +234,7 @@ DankListView { Behavior on opacity { enabled: listView.listInitialized - NumberAnimation { + OpacityAnimator { duration: listView.listInitialized ? Theme.shortDuration : 0 } } diff --git a/quickshell/Modules/Notifications/Center/NotificationCard.qml b/quickshell/Modules/Notifications/Center/NotificationCard.qml index 788496b5..156253d6 100644 --- a/quickshell/Modules/Notifications/Center/NotificationCard.qml +++ b/quickshell/Modules/Notifications/Center/NotificationCard.qml @@ -75,7 +75,7 @@ Rectangle { Behavior on scale { enabled: listLevelScaleAnimationsEnabled - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -545,7 +545,7 @@ Rectangle { Behavior on x { enabled: !expandedSwipeHandler.active && !expandedDelegateWrapper.isDismissing - NumberAnimation { + XAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -553,7 +553,7 @@ Rectangle { Behavior on scale { enabled: !expandedSwipeHandler.active - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -762,7 +762,7 @@ Rectangle { spacing: contentSpacing Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -822,7 +822,7 @@ Rectangle { color: isHovered ? Theme.withAlpha(Theme.primary, Theme.stateLayerHover) : "transparent" Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Notifications/Center/NotificationKeyboardHints.qml b/quickshell/Modules/Notifications/Center/NotificationKeyboardHints.qml index 78ba2d52..78cbe41b 100644 --- a/quickshell/Modules/Notifications/Center/NotificationKeyboardHints.qml +++ b/quickshell/Modules/Notifications/Center/NotificationKeyboardHints.qml @@ -42,7 +42,7 @@ Rectangle { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Notifications/Center/NotificationSettings.qml b/quickshell/Modules/Notifications/Center/NotificationSettings.qml index ea377527..030fe43e 100644 --- a/quickshell/Modules/Notifications/Center/NotificationSettings.qml +++ b/quickshell/Modules/Notifications/Center/NotificationSettings.qml @@ -27,7 +27,7 @@ Rectangle { opacity: expanded ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } @@ -123,327 +123,327 @@ Rectangle { anchors.margins: Theme.spacingL spacing: Theme.spacingM - StyledText { - text: I18n.tr("Notification Settings") - font.pixelSize: Theme.fontSizeMedium - font.weight: Font.Bold - color: Theme.surfaceText - } - - Item { - width: parent.width - height: Math.max(dndRow.implicitHeight, dndToggle.implicitHeight) + Theme.spacingS - - Row { - id: dndRow - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: SessionData.doNotDisturb ? "notifications_off" : "notifications" - size: Theme.iconSizeSmall - color: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Do Not Disturb") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } + StyledText { + text: I18n.tr("Notification Settings") + font.pixelSize: Theme.fontSizeMedium + font.weight: Font.Bold + color: Theme.surfaceText } - DankToggle { - id: dndToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SessionData.doNotDisturb - onToggled: SessionData.setDoNotDisturb(!SessionData.doNotDisturb) - } - } + Item { + width: parent.width + height: Math.max(dndRow.implicitHeight, dndToggle.implicitHeight) + Theme.spacingS - Rectangle { - width: parent.width - height: 1 - color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) - } + Row { + id: dndRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM - StyledText { - text: I18n.tr("Notification Timeouts") - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.Medium - color: Theme.surfaceVariantText - } - - DankDropdown { - text: I18n.tr("Low Priority") - description: I18n.tr("Timeout for low priority notifications") - currentValue: getTimeoutText(SettingsData.notificationTimeoutLow) - options: timeoutOptions.map(opt => opt.text) - onValueChanged: value => { - for (let i = 0; i < timeoutOptions.length; i++) { - if (timeoutOptions[i].text === value) { - SettingsData.set("notificationTimeoutLow", timeoutOptions[i].value); - break; + DankIcon { + name: SessionData.doNotDisturb ? "notifications_off" : "notifications" + size: Theme.iconSizeSmall + color: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter } - } - } - } - - DankDropdown { - text: I18n.tr("Normal Priority") - description: I18n.tr("Timeout for normal priority notifications") - currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal) - options: timeoutOptions.map(opt => opt.text) - onValueChanged: value => { - for (let i = 0; i < timeoutOptions.length; i++) { - if (timeoutOptions[i].text === value) { - SettingsData.set("notificationTimeoutNormal", timeoutOptions[i].value); - break; - } - } - } - } - - DankDropdown { - text: I18n.tr("Critical Priority") - description: I18n.tr("Timeout for critical priority notifications") - currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical) - options: timeoutOptions.map(opt => opt.text) - onValueChanged: value => { - for (let i = 0; i < timeoutOptions.length; i++) { - if (timeoutOptions[i].text === value) { - SettingsData.set("notificationTimeoutCritical", timeoutOptions[i].value); - break; - } - } - } - } - - Rectangle { - width: parent.width - height: 1 - color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) - } - - Item { - width: parent.width - height: Math.max(overlayRow.implicitHeight, overlayToggle.implicitHeight) + Theme.spacingS - - Row { - id: overlayRow - anchors.left: parent.left - anchors.right: overlayToggle.left - anchors.rightMargin: Theme.spacingM - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: "notifications_active" - size: Theme.iconSizeSmall - color: SettingsData.notificationOverlayEnabled ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - Column { - spacing: 2 - anchors.verticalCenter: parent.verticalCenter - width: overlayRow.width - Theme.iconSizeSmall - Theme.spacingM StyledText { - width: parent.width - text: I18n.tr("Notification Overlay") + text: I18n.tr("Do Not Disturb") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceText - wrapMode: Text.Wrap + anchors.verticalCenter: parent.verticalCenter } + } - StyledText { - width: parent.width - text: I18n.tr("Display all priorities over fullscreen apps") - font.pixelSize: Theme.fontSizeSmall - 1 - color: Theme.surfaceVariantText - wrapMode: Text.Wrap + DankToggle { + id: dndToggle + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + checked: SessionData.doNotDisturb + onToggled: SessionData.setDoNotDisturb(!SessionData.doNotDisturb) + } + } + + Rectangle { + width: parent.width + height: 1 + color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) + } + + StyledText { + text: I18n.tr("Notification Timeouts") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceVariantText + } + + DankDropdown { + text: I18n.tr("Low Priority") + description: I18n.tr("Timeout for low priority notifications") + currentValue: getTimeoutText(SettingsData.notificationTimeoutLow) + options: timeoutOptions.map(opt => opt.text) + onValueChanged: value => { + for (let i = 0; i < timeoutOptions.length; i++) { + if (timeoutOptions[i].text === value) { + SettingsData.set("notificationTimeoutLow", timeoutOptions[i].value); + break; + } } } } - DankToggle { - id: overlayToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SettingsData.notificationOverlayEnabled - onToggled: toggled => SettingsData.set("notificationOverlayEnabled", toggled) + DankDropdown { + text: I18n.tr("Normal Priority") + description: I18n.tr("Timeout for normal priority notifications") + currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal) + options: timeoutOptions.map(opt => opt.text) + onValueChanged: value => { + for (let i = 0; i < timeoutOptions.length; i++) { + if (timeoutOptions[i].text === value) { + SettingsData.set("notificationTimeoutNormal", timeoutOptions[i].value); + break; + } + } + } } - } - Item { - width: parent.width - height: Math.max(privacyRow.implicitHeight, privacyToggle.implicitHeight) + Theme.spacingS + DankDropdown { + text: I18n.tr("Critical Priority") + description: I18n.tr("Timeout for critical priority notifications") + currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical) + options: timeoutOptions.map(opt => opt.text) + onValueChanged: value => { + for (let i = 0; i < timeoutOptions.length; i++) { + if (timeoutOptions[i].text === value) { + SettingsData.set("notificationTimeoutCritical", timeoutOptions[i].value); + break; + } + } + } + } - Row { - id: privacyRow - anchors.left: parent.left - anchors.right: privacyToggle.left - anchors.rightMargin: Theme.spacingM - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM + Rectangle { + width: parent.width + height: 1 + color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) + } - DankIcon { - name: "privacy_tip" - size: Theme.iconSizeSmall - color: SettingsData.notificationPopupPrivacyMode ? Theme.primary : Theme.surfaceText + Item { + width: parent.width + height: Math.max(overlayRow.implicitHeight, overlayToggle.implicitHeight) + Theme.spacingS + + Row { + id: overlayRow + anchors.left: parent.left + anchors.right: overlayToggle.left + anchors.rightMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + DankIcon { + name: "notifications_active" + size: Theme.iconSizeSmall + color: SettingsData.notificationOverlayEnabled ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + Column { + spacing: 2 + anchors.verticalCenter: parent.verticalCenter + width: overlayRow.width - Theme.iconSizeSmall - Theme.spacingM + + StyledText { + width: parent.width + text: I18n.tr("Notification Overlay") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + wrapMode: Text.Wrap + } + + StyledText { + width: parent.width + text: I18n.tr("Display all priorities over fullscreen apps") + font.pixelSize: Theme.fontSizeSmall - 1 + color: Theme.surfaceVariantText + wrapMode: Text.Wrap + } + } } - Column { - spacing: 2 + DankToggle { + id: overlayToggle + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - width: privacyRow.width - Theme.iconSizeSmall - Theme.spacingM + checked: SettingsData.notificationOverlayEnabled + onToggled: toggled => SettingsData.set("notificationOverlayEnabled", toggled) + } + } + + Item { + width: parent.width + height: Math.max(privacyRow.implicitHeight, privacyToggle.implicitHeight) + Theme.spacingS + + Row { + id: privacyRow + anchors.left: parent.left + anchors.right: privacyToggle.left + anchors.rightMargin: Theme.spacingM + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + DankIcon { + name: "privacy_tip" + size: Theme.iconSizeSmall + color: SettingsData.notificationPopupPrivacyMode ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + Column { + spacing: 2 + anchors.verticalCenter: parent.verticalCenter + width: privacyRow.width - Theme.iconSizeSmall - Theme.spacingM + + StyledText { + width: parent.width + text: I18n.tr("Privacy Mode") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + wrapMode: Text.Wrap + } + + StyledText { + width: parent.width + text: I18n.tr("Hide notification content until expanded") + font.pixelSize: Theme.fontSizeSmall - 1 + color: Theme.surfaceVariantText + wrapMode: Text.Wrap + } + } + } + + DankToggle { + id: privacyToggle + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + checked: SettingsData.notificationPopupPrivacyMode + onToggled: toggled => SettingsData.set("notificationPopupPrivacyMode", toggled) + } + } + + Rectangle { + width: parent.width + height: 1 + color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) + } + + StyledText { + text: I18n.tr("History Settings") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceVariantText + } + + Item { + width: parent.width + height: Math.max(lowRow.implicitHeight, lowToggle.implicitHeight) + Theme.spacingS + + Row { + id: lowRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + DankIcon { + name: "low_priority" + size: Theme.iconSizeSmall + color: SettingsData.notificationHistorySaveLow ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } StyledText { - width: parent.width - text: I18n.tr("Privacy Mode") + text: I18n.tr("Low Priority") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceText - wrapMode: Text.Wrap + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: lowToggle + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + checked: SettingsData.notificationHistorySaveLow + onToggled: toggled => SettingsData.set("notificationHistorySaveLow", toggled) + } + } + + Item { + width: parent.width + height: Math.max(normalRow.implicitHeight, normalToggle.implicitHeight) + Theme.spacingS + + Row { + id: normalRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + DankIcon { + name: "notifications" + size: Theme.iconSizeSmall + color: SettingsData.notificationHistorySaveNormal ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter } StyledText { - width: parent.width - text: I18n.tr("Hide notification content until expanded") - font.pixelSize: Theme.fontSizeSmall - 1 - color: Theme.surfaceVariantText - wrapMode: Text.Wrap + text: I18n.tr("Normal Priority") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter } } - } - DankToggle { - id: privacyToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SettingsData.notificationPopupPrivacyMode - onToggled: toggled => SettingsData.set("notificationPopupPrivacyMode", toggled) - } - } - - Rectangle { - width: parent.width - height: 1 - color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1) - } - - StyledText { - text: I18n.tr("History Settings") - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.Medium - color: Theme.surfaceVariantText - } - - Item { - width: parent.width - height: Math.max(lowRow.implicitHeight, lowToggle.implicitHeight) + Theme.spacingS - - Row { - id: lowRow - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: "low_priority" - size: Theme.iconSizeSmall - color: SettingsData.notificationHistorySaveLow ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Low Priority") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText + DankToggle { + id: normalToggle + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter + checked: SettingsData.notificationHistorySaveNormal + onToggled: toggled => SettingsData.set("notificationHistorySaveNormal", toggled) } } - DankToggle { - id: lowToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SettingsData.notificationHistorySaveLow - onToggled: toggled => SettingsData.set("notificationHistorySaveLow", toggled) + Item { + width: parent.width + height: Math.max(criticalRow.implicitHeight, criticalToggle.implicitHeight) + Theme.spacingS + + Row { + id: criticalRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + DankIcon { + name: "priority_high" + size: Theme.iconSizeSmall + color: SettingsData.notificationHistorySaveCritical ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: I18n.tr("Critical Priority") + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + } + + DankToggle { + id: criticalToggle + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + checked: SettingsData.notificationHistorySaveCritical + onToggled: toggled => SettingsData.set("notificationHistorySaveCritical", toggled) + } } } - - Item { - width: parent.width - height: Math.max(normalRow.implicitHeight, normalToggle.implicitHeight) + Theme.spacingS - - Row { - id: normalRow - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: "notifications" - size: Theme.iconSizeSmall - color: SettingsData.notificationHistorySaveNormal ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Normal Priority") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } - - DankToggle { - id: normalToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SettingsData.notificationHistorySaveNormal - onToggled: toggled => SettingsData.set("notificationHistorySaveNormal", toggled) - } - } - - Item { - width: parent.width - height: Math.max(criticalRow.implicitHeight, criticalToggle.implicitHeight) + Theme.spacingS - - Row { - id: criticalRow - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: "priority_high" - size: Theme.iconSizeSmall - color: SettingsData.notificationHistorySaveCritical ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: I18n.tr("Critical Priority") - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } - - DankToggle { - id: criticalToggle - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - checked: SettingsData.notificationHistorySaveCritical - onToggled: toggled => SettingsData.set("notificationHistorySaveCritical", toggled) - } - } - } } } diff --git a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml index 4eefe1ad..00067406 100644 --- a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml +++ b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml @@ -36,10 +36,13 @@ PanelWindow { WindowBlur { targetWindow: win - blurX: content.x + content.cardInset + swipeTx.x + tx.x - blurY: content.y + content.cardInset + swipeTx.y + tx.y - blurWidth: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.width - content.cardInset * 2) : 0 - blurHeight: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.height - content.cardInset * 2) : 0 + readonly property real s: Math.min(1, content.scale) * Math.max(0, content.opacity) + readonly property real innerW: Math.max(0, content.width - content.cardInset * 2) + readonly property real innerH: Math.max(0, content.height - content.cardInset * 2) + blurX: content.x + content.cardInset + swipeTx.x + tx.x + innerW * (1 - s) * 0.5 + blurY: content.y + content.cardInset + swipeTx.y + tx.y + innerH * (1 - s) * 0.5 + blurWidth: !win._finalized && !win.connectedFrameMode ? innerW * s : 0 + blurHeight: !win._finalized && !win.connectedFrameMode ? innerH * s : 0 blurRadius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius } @@ -993,7 +996,7 @@ PanelWindow { z: 20 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1048,7 +1051,7 @@ PanelWindow { visible: actionCount < 3 && cardHoverHandler.hovered opacity: visible ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -1136,7 +1139,7 @@ PanelWindow { } onTranslationChanged: { - if (win.exiting) + if (win.exiting || content.swipeDismissing) return; content.swipeOffset = translation.x; @@ -1152,7 +1155,7 @@ PanelWindow { } Behavior on opacity { - enabled: !content.swipeActive + enabled: !content.swipeActive && !content.swipeDismissing NumberAnimation { duration: Theme.shortDuration } @@ -1269,7 +1272,6 @@ PanelWindow { NumberAnimation { target: content property: "opacity" - from: 1 to: Theme.isDirectionalEffect ? 1 : 0 duration: Theme.notificationExitDuration easing.type: Easing.BezierSpline @@ -1279,7 +1281,6 @@ PanelWindow { NumberAnimation { target: content property: "scale" - from: 1 to: Theme.isDirectionalEffect ? 1 : Theme.effectScaleCollapsed duration: Theme.notificationExitDuration easing.type: Easing.BezierSpline diff --git a/quickshell/Modules/Settings/AudioTab.qml b/quickshell/Modules/Settings/AudioTab.qml index c054791c..62e8cd9f 100644 --- a/quickshell/Modules/Settings/AudioTab.qml +++ b/quickshell/Modules/Settings/AudioTab.qml @@ -519,7 +519,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml b/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml index 4852d6b3..fe980fa6 100644 --- a/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml +++ b/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml @@ -182,7 +182,7 @@ SettingsCard { opacity: visible ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/Settings/MediaPlayerTab.qml b/quickshell/Modules/Settings/MediaPlayerTab.qml index a844a86f..2c89d500 100644 --- a/quickshell/Modules/Settings/MediaPlayerTab.qml +++ b/quickshell/Modules/Settings/MediaPlayerTab.qml @@ -107,7 +107,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/Settings/PluginBrowser.qml b/quickshell/Modules/Settings/PluginBrowser.qml index b1f0a6fb..68641d06 100644 --- a/quickshell/Modules/Settings/PluginBrowser.qml +++ b/quickshell/Modules/Settings/PluginBrowser.qml @@ -587,7 +587,7 @@ FloatingWindow { border.color: buttonState === "incompatible" ? Theme.warning : Theme.outline Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Settings/ThemeBrowser.qml b/quickshell/Modules/Settings/ThemeBrowser.qml index 1df32c75..6d3622bd 100644 --- a/quickshell/Modules/Settings/ThemeBrowser.qml +++ b/quickshell/Modules/Settings/ThemeBrowser.qml @@ -619,7 +619,7 @@ FloatingWindow { border.color: isInstalled ? (uninstallMouseArea.containsMouse ? Theme.error : Theme.outline) : "transparent" Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Settings/ThemeColorsTab.qml b/quickshell/Modules/Settings/ThemeColorsTab.qml index e88408b2..2a4551e1 100644 --- a/quickshell/Modules/Settings/ThemeColorsTab.qml +++ b/quickshell/Modules/Settings/ThemeColorsTab.qml @@ -380,7 +380,7 @@ Item { } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } @@ -633,7 +633,7 @@ Item { scale: isActive ? 1.03 : 1 Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } @@ -989,7 +989,7 @@ Item { } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/Settings/WidgetsTabSection.qml b/quickshell/Modules/Settings/WidgetsTabSection.qml index e02245e4..c89ef949 100644 --- a/quickshell/Modules/Settings/WidgetsTabSection.qml +++ b/quickshell/Modules/Settings/WidgetsTabSection.qml @@ -308,7 +308,7 @@ Column { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -620,7 +620,7 @@ Column { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Modules/Settings/WorkspacesTab.qml b/quickshell/Modules/Settings/WorkspacesTab.qml index 99955fd6..4886e603 100644 --- a/quickshell/Modules/Settings/WorkspacesTab.qml +++ b/quickshell/Modules/Settings/WorkspacesTab.qml @@ -97,7 +97,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/Toast.qml b/quickshell/Modules/Toast.qml index 55c57e3b..aaeaeabc 100644 --- a/quickshell/Modules/Toast.qml +++ b/quickshell/Modules/Toast.qml @@ -416,7 +416,7 @@ PanelWindow { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml b/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml index bc32e853..d0f3efc9 100644 --- a/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml +++ b/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml @@ -48,268 +48,271 @@ Scope { bottom: true } - HyprlandFocusGrab { - id: grab - windows: [root] - active: false - property bool hasBeenActivated: false - onActiveChanged: { - if (active) { - hasBeenActivated = true - } - } - onCleared: () => { - if (hasBeenActivated && overviewScope.overviewOpen) { - overviewScope.overviewOpen = false - } - } - } - - Connections { - target: overviewScope - function onOverviewOpenChanged() { - if (overviewScope.overviewOpen) { - grab.hasBeenActivated = false - if (CompositorService.useHyprlandFocusGrab) - delayedGrabTimer.start() - } else { - delayedGrabTimer.stop() - grab.active = false - grab.hasBeenActivated = false - } - } - } - - Connections { - target: root - function onMonitorIsFocusedChanged() { - if (!CompositorService.useHyprlandFocusGrab) - return; - if (overviewScope.overviewOpen && root.monitorIsFocused && !grab.active) { - grab.hasBeenActivated = false - grab.active = true - } else if (overviewScope.overviewOpen && !root.monitorIsFocused && grab.active) { - grab.active = false - } - } - } - - Timer { - id: delayedGrabTimer - interval: 150 - repeat: false - onTriggered: { - if (CompositorService.useHyprlandFocusGrab && overviewScope.overviewOpen && root.monitorIsFocused) { - grab.active = true - } - } - } - - Timer { - id: closeTimer - interval: Theme.expressiveDurations.expressiveDefaultSpatial + 120 - onTriggered: { - root.visible = false - } - } - - Rectangle { - id: background - anchors.fill: parent - color: "black" - opacity: overviewScope.overviewOpen ? 0.5 : 0 - - Behavior on opacity { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - MouseArea { - anchors.fill: parent - onClicked: mouse => { - const localPos = mapToItem(contentContainer, mouse.x, mouse.y) - if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) { - overviewScope.overviewOpen = false - closeTimer.restart() + HyprlandFocusGrab { + id: grab + windows: [root] + active: false + property bool hasBeenActivated: false + onActiveChanged: { + if (active) { + hasBeenActivated = true; } } - } - } - - Item { - id: contentContainer - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: 100 - width: childrenRect.width - height: childrenRect.height - - opacity: overviewScope.overviewOpen ? 1 : 0 - transform: [scaleTransform, motionTransform] - - Scale { - id: scaleTransform - origin.x: contentContainer.width / 2 - origin.y: contentContainer.height / 2 - xScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed - yScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed - - Behavior on xScale { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - Behavior on yScale { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + onCleared: () => { + if (hasBeenActivated && overviewScope.overviewOpen) { + overviewScope.overviewOpen = false; } } } - Translate { - id: motionTransform - x: { - if (overviewScope.overviewOpen) - return 0; - if (Theme.isDirectionalEffect) - return 0; - if (Theme.isDepthEffect) - return Theme.effectAnimOffset * 0.25; - return 0; - } - y: { - if (overviewScope.overviewOpen) - return 0; - if (Theme.isDirectionalEffect) - return -Math.max(contentContainer.height * 0.8, Theme.effectAnimOffset * 1.1); - if (Theme.isDepthEffect) - return Math.max(Theme.effectAnimOffset * 0.85, 28); - return Theme.effectAnimOffset; - } - - Behavior on x { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - Behavior on y { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - } - - Behavior on opacity { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) - easing.type: Easing.BezierSpline - easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - Loader { - id: overviewLoader - active: overviewScope.overviewOpen - asynchronous: false - - sourceComponent: OverviewWidget { - panelWindow: root - overviewOpen: overviewScope.overviewOpen - } - } - } - - FocusScope { - id: focusScope - anchors.fill: parent - visible: overviewScope.overviewOpen - focus: overviewScope.overviewOpen && root.monitorIsFocused - - Keys.onEscapePressed: event => { - if (!root.monitorIsFocused) return - overviewScope.overviewOpen = false - closeTimer.restart() - event.accepted = true - } - - Keys.onPressed: event => { - if (!root.monitorIsFocused) return - - if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) { - if (!overviewLoader.item) return - - const thisMonitorWorkspaceIds = overviewLoader.item.thisMonitorWorkspaceIds - if (thisMonitorWorkspaceIds.length === 0) return - - const currentId = root.monitor.activeWorkspace?.id ?? thisMonitorWorkspaceIds[0] - const currentIndex = thisMonitorWorkspaceIds.indexOf(currentId) - - let targetIndex - if (event.key === Qt.Key_Left) { - targetIndex = currentIndex - 1 - if (targetIndex < 0) targetIndex = thisMonitorWorkspaceIds.length - 1 + Connections { + target: overviewScope + function onOverviewOpenChanged() { + if (overviewScope.overviewOpen) { + grab.hasBeenActivated = false; + if (CompositorService.useHyprlandFocusGrab) + delayedGrabTimer.start(); } else { - targetIndex = currentIndex + 1 - if (targetIndex >= thisMonitorWorkspaceIds.length) targetIndex = 0 + delayedGrabTimer.stop(); + grab.active = false; + grab.hasBeenActivated = false; } - - const targetId = thisMonitorWorkspaceIds[targetIndex] - Hyprland.dispatch("workspace " + targetId) - event.accepted = true - } - } - - onVisibleChanged: { - if (visible && overviewScope.overviewOpen && root.monitorIsFocused) { - Qt.callLater(() => focusScope.forceActiveFocus()) } } Connections { target: root function onMonitorIsFocusedChanged() { - if (root.monitorIsFocused && overviewScope.overviewOpen) { - Qt.callLater(() => focusScope.forceActiveFocus()) + if (!CompositorService.useHyprlandFocusGrab) + return; + if (overviewScope.overviewOpen && root.monitorIsFocused && !grab.active) { + grab.hasBeenActivated = false; + grab.active = true; + } else if (overviewScope.overviewOpen && !root.monitorIsFocused && grab.active) { + grab.active = false; + } + } + } + + Timer { + id: delayedGrabTimer + interval: 150 + repeat: false + onTriggered: { + if (CompositorService.useHyprlandFocusGrab && overviewScope.overviewOpen && root.monitorIsFocused) { + grab.active = true; + } + } + } + + Timer { + id: closeTimer + interval: Theme.expressiveDurations.expressiveDefaultSpatial + 120 + onTriggered: { + root.visible = false; + } + } + + Rectangle { + id: background + anchors.fill: parent + color: "black" + opacity: overviewScope.overviewOpen ? 0.5 : 0 + + Behavior on opacity { + OpacityAnimator { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + + MouseArea { + anchors.fill: parent + onClicked: mouse => { + const localPos = mapToItem(contentContainer, mouse.x, mouse.y); + if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) { + overviewScope.overviewOpen = false; + closeTimer.restart(); + } + } + } + } + + Item { + id: contentContainer + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 100 + width: childrenRect.width + height: childrenRect.height + + opacity: overviewScope.overviewOpen ? 1 : 0 + transform: [scaleTransform, motionTransform] + + Scale { + id: scaleTransform + origin.x: contentContainer.width / 2 + origin.y: contentContainer.height / 2 + xScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed + yScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed + + Behavior on xScale { + NumberAnimation { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + + Behavior on yScale { + NumberAnimation { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + } + + Translate { + id: motionTransform + x: { + if (overviewScope.overviewOpen) + return 0; + if (Theme.isDirectionalEffect) + return 0; + if (Theme.isDepthEffect) + return Theme.effectAnimOffset * 0.25; + return 0; + } + y: { + if (overviewScope.overviewOpen) + return 0; + if (Theme.isDirectionalEffect) + return -Math.max(contentContainer.height * 0.8, Theme.effectAnimOffset * 1.1); + if (Theme.isDepthEffect) + return Math.max(Theme.effectAnimOffset * 0.85, 28); + return Theme.effectAnimOffset; + } + + Behavior on x { + NumberAnimation { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + + Behavior on y { + NumberAnimation { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + } + + Behavior on opacity { + OpacityAnimator { + duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen) + easing.type: Easing.BezierSpline + easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + } + } + + Loader { + id: overviewLoader + active: overviewScope.overviewOpen + asynchronous: false + + sourceComponent: OverviewWidget { + panelWindow: root + overviewOpen: overviewScope.overviewOpen + } + } + } + + FocusScope { + id: focusScope + anchors.fill: parent + visible: overviewScope.overviewOpen + focus: overviewScope.overviewOpen && root.monitorIsFocused + + Keys.onEscapePressed: event => { + if (!root.monitorIsFocused) + return; + overviewScope.overviewOpen = false; + closeTimer.restart(); + event.accepted = true; + } + + Keys.onPressed: event => { + if (!root.monitorIsFocused) + return; + if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) { + if (!overviewLoader.item) + return; + const thisMonitorWorkspaceIds = overviewLoader.item.thisMonitorWorkspaceIds; + if (thisMonitorWorkspaceIds.length === 0) + return; + const currentId = root.monitor.activeWorkspace?.id ?? thisMonitorWorkspaceIds[0]; + const currentIndex = thisMonitorWorkspaceIds.indexOf(currentId); + + let targetIndex; + if (event.key === Qt.Key_Left) { + targetIndex = currentIndex - 1; + if (targetIndex < 0) + targetIndex = thisMonitorWorkspaceIds.length - 1; + } else { + targetIndex = currentIndex + 1; + if (targetIndex >= thisMonitorWorkspaceIds.length) + targetIndex = 0; + } + + const targetId = thisMonitorWorkspaceIds[targetIndex]; + Hyprland.dispatch("workspace " + targetId); + event.accepted = true; + } + } + + onVisibleChanged: { + if (visible && overviewScope.overviewOpen && root.monitorIsFocused) { + Qt.callLater(() => focusScope.forceActiveFocus()); + } + } + + Connections { + target: root + function onMonitorIsFocusedChanged() { + if (root.monitorIsFocused && overviewScope.overviewOpen) { + Qt.callLater(() => focusScope.forceActiveFocus()); + } + } + } + } + + onVisibleChanged: { + if (visible && overviewScope.overviewOpen) { + Qt.callLater(() => focusScope.forceActiveFocus()); + } else if (!visible) { + grab.active = false; + } + } + + Connections { + target: overviewScope + function onOverviewOpenChanged() { + if (overviewScope.overviewOpen) { + closeTimer.stop(); + root.visible = true; + Qt.callLater(() => focusScope.forceActiveFocus()); + } else { + closeTimer.restart(); + grab.active = false; } } } } - - onVisibleChanged: { - if (visible && overviewScope.overviewOpen) { - Qt.callLater(() => focusScope.forceActiveFocus()) - } else if (!visible) { - grab.active = false - } - } - - Connections { - target: overviewScope - function onOverviewOpenChanged() { - if (overviewScope.overviewOpen) { - closeTimer.stop() - root.visible = true - Qt.callLater(() => focusScope.forceActiveFocus()) - } else { - closeTimer.restart() - grab.active = false - } - } - } - } } } } diff --git a/quickshell/Widgets/DankButton.qml b/quickshell/Widgets/DankButton.qml index 6de3a8d4..93031a46 100644 --- a/quickshell/Widgets/DankButton.qml +++ b/quickshell/Widgets/DankButton.qml @@ -28,7 +28,8 @@ Rectangle { Behavior on scale { enabled: enableScaleAnimation && Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None - DankAnim { + ScaleAnimator { + easing.type: Easing.BezierSpline duration: 100 easing.bezierCurve: Theme.expressiveCurves.standard } diff --git a/quickshell/Widgets/DankButtonGroup.qml b/quickshell/Widgets/DankButtonGroup.qml index 02e145ed..2a7cae27 100644 --- a/quickshell/Widgets/DankButtonGroup.qml +++ b/quickshell/Widgets/DankButtonGroup.qml @@ -197,7 +197,7 @@ Row { Behavior on opacity { enabled: root.userInteracted - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -205,7 +205,7 @@ Row { Behavior on scale { enabled: root.userInteracted - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.emphasizedEasing } diff --git a/quickshell/Widgets/DankCollapsibleSection.qml b/quickshell/Widgets/DankCollapsibleSection.qml index 0ed0e59c..9c15c453 100644 --- a/quickshell/Widgets/DankCollapsibleSection.qml +++ b/quickshell/Widgets/DankCollapsibleSection.qml @@ -45,7 +45,8 @@ ColumnLayout { Behavior on rotation { enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None - DankAnim { + RotationAnimator { + easing.type: Easing.BezierSpline duration: Theme.shortDuration easing.bezierCurve: Theme.expressiveCurves.standard } @@ -88,7 +89,8 @@ ColumnLayout { Behavior on opacity { enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None - DankAnim { + OpacityAnimator { + easing.type: Easing.BezierSpline duration: Theme.shortDuration easing.bezierCurve: Theme.expressiveCurves.standard } @@ -108,7 +110,8 @@ ColumnLayout { Behavior on opacity { enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None - DankAnim { + OpacityAnimator { + easing.type: Easing.BezierSpline duration: Theme.shortDuration easing.bezierCurve: Theme.expressiveCurves.standard } diff --git a/quickshell/Widgets/DankDropdown.qml b/quickshell/Widgets/DankDropdown.qml index 05b962a0..d8f2ad7e 100644 --- a/quickshell/Widgets/DankDropdown.qml +++ b/quickshell/Widgets/DankDropdown.qml @@ -187,7 +187,7 @@ Item { anchors.rightMargin: Theme.spacingS Behavior on rotation { - NumberAnimation { + RotationAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Widgets/DankLocationSearch.qml b/quickshell/Widgets/DankLocationSearch.qml index d58538a8..a14829c9 100644 --- a/quickshell/Widgets/DankLocationSearch.qml +++ b/quickshell/Widgets/DankLocationSearch.qml @@ -157,7 +157,7 @@ Item { opacity: (locationInput.getActiveFocus() && locationInput.text.length > 2) ? 1 : 0 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Widgets/DankOSD.qml b/quickshell/Widgets/DankOSD.qml index ad5625b8..d4db5d18 100644 --- a/quickshell/Widgets/DankOSD.qml +++ b/quickshell/Widgets/DankOSD.qml @@ -315,14 +315,14 @@ PanelWindow { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: animationDuration easing.type: animationEasing } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: animationDuration easing.type: animationEasing } diff --git a/quickshell/Widgets/DankPopoutConnected.qml b/quickshell/Widgets/DankPopoutConnected.qml index a478940a..b61b42c5 100644 --- a/quickshell/Widgets/DankPopoutConnected.qml +++ b/quickshell/Widgets/DankPopoutConnected.qml @@ -273,13 +273,44 @@ Item { }); } + property bool _animSyncQueued: false + function _queueAnimSync() { + if (_animSyncQueued) + return; + _animSyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushAnimSync === "function") + root._flushAnimSync(); + }); + } + function _flushAnimSync() { + _animSyncQueued = false; + _syncPopoutAnim("x"); + _syncPopoutAnim("y"); + } + + property bool _bodySyncQueued: false + function _queueBodySync() { + if (_bodySyncQueued) + return; + _bodySyncQueued = true; + Qt.callLater(() => { + if (root && typeof root._flushBodySync === "function") + root._flushBodySync(); + }); + } + function _flushBodySync() { + _bodySyncQueued = false; + _syncPopoutBody(); + } + onAlignedXChanged: _queueFullSync() onAlignedYChanged: _queueFullSync() onAlignedWidthChanged: _queueFullSync() - onContentAnimXChanged: _syncPopoutAnim("x") - onContentAnimYChanged: _syncPopoutAnim("y") - onRenderedAlignedYChanged: _syncPopoutBody() - onRenderedAlignedHeightChanged: _syncPopoutBody() + onContentAnimXChanged: _queueAnimSync() + onContentAnimYChanged: _queueAnimSync() + onRenderedAlignedYChanged: _queueBodySync() + onRenderedAlignedHeightChanged: _queueBodySync() onScreenChanged: _queueFullSync() onEffectiveBarPositionChanged: _queueFullSync() @@ -676,8 +707,8 @@ Item { blurX: trackBlurFromBarEdge ? contentContainer.x + (contentContainer.barRight ? _dxClamp : 0) : contentContainer.x + contentContainer.width * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) - contentContainer.horizontalConnectorExtent * s blurY: trackBlurFromBarEdge ? contentContainer.y + (contentContainer.barBottom ? _dyClamp : 0) : contentContainer.y + contentContainer.height * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) - contentContainer.verticalConnectorExtent * s - blurWidth: (shouldBeVisible && contentWrapper.opacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.width - Math.abs(_dxClamp)) : (contentContainer.width + contentContainer.horizontalConnectorExtent * 2) * s) : 0 - blurHeight: (shouldBeVisible && contentWrapper.opacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.height - Math.abs(_dyClamp)) : (contentContainer.height + contentContainer.verticalConnectorExtent * 2) * s) : 0 + blurWidth: (shouldBeVisible && contentWrapper.publishedOpacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.width - Math.abs(_dxClamp)) : (contentContainer.width + contentContainer.horizontalConnectorExtent * 2) * s) : 0 + blurHeight: (shouldBeVisible && contentWrapper.publishedOpacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.height - Math.abs(_dyClamp)) : (contentContainer.height + contentContainer.verticalConnectorExtent * 2) * s) : 0 blurRadius: Theme.isConnectedEffect ? Theme.connectedCornerRadius : Theme.connectedSurfaceRadius } @@ -959,7 +990,7 @@ Item { width: rollOutAdjuster.baseWidth + extraLeft + extraRight height: rollOutAdjuster.baseHeight + extraTop + extraBottom - opacity: contentWrapper.opacity + opacity: contentWrapper.publishedOpacity scale: contentWrapper.scale x: contentWrapper.x - extraLeft y: contentWrapper.y - extraTop @@ -1024,23 +1055,48 @@ Item { id: contentWrapper width: rollOutAdjuster.baseWidth height: rollOutAdjuster.baseHeight + + property bool _renderActive: Theme.isDirectionalEffect || shouldBeVisible + property bool _animating: false + property real publishedOpacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0) + opacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0) - visible: opacity > 0 + visible: _renderActive scale: contentContainer.scaleValue x: Theme.snap(contentContainer.animX + (rollOutAdjuster.baseWidth - width) * (1 - scale) * 0.5, root.dpr) y: Theme.snap(contentContainer.animY + (rollOutAdjuster.baseHeight - height) * (1 - scale) * 0.5, root.dpr) - layer.enabled: contentWrapper.opacity < 1 + layer.enabled: _animating || (!Theme.isDirectionalEffect && publishedOpacity < 1) layer.smooth: false layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0) Behavior on opacity { + enabled: !Theme.isDirectionalEffect + OpacityAnimator { + duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale) + easing.type: Easing.BezierSpline + easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + onRunningChanged: contentWrapper._animating = running + } + } + + Behavior on publishedOpacity { enabled: !Theme.isDirectionalEffect NumberAnimation { duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + onRunningChanged: if (!running && contentWrapper.publishedOpacity === 0) + contentWrapper._renderActive = false + } + } + + Connections { + target: root + function onShouldBeVisibleChanged() { + if (root.shouldBeVisible) + contentWrapper._renderActive = true; } } diff --git a/quickshell/Widgets/DankPopoutStandalone.qml b/quickshell/Widgets/DankPopoutStandalone.qml index 29d5b4c3..d0cbd9f7 100644 --- a/quickshell/Widgets/DankPopoutStandalone.qml +++ b/quickshell/Widgets/DankPopoutStandalone.qml @@ -527,7 +527,7 @@ Item { targetWindow: contentWindow readonly property real s: Math.min(1, contentContainer.scaleValue) readonly property bool trackBlurFromBarEdge: root.fluidStandaloneActive - readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : (root.shouldBeVisible && contentWrapper.opacity > 0) + readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : (root.shouldBeVisible && contentWrapper.publishedOpacity > 0) blurX: trackBlurFromBarEdge ? contentContainer.x + contentContainer.revealX : contentContainer.x + contentContainer.width * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) blurY: trackBlurFromBarEdge ? contentContainer.y + contentContainer.revealY : contentContainer.y + contentContainer.height * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) @@ -595,9 +595,7 @@ Item { Item { id: contentContainer x: shadowBuffer + root.alignedX - root._surfaceBodyX - y: root._fullHeight - ? (root.fluidStandaloneActive ? root.renderedAlignedY : root.alignedY) - : shadowBuffer + (root.fluidStandaloneActive ? root.renderedAlignedY : root.alignedY) - root._surfaceBodyY + y: root._fullHeight ? (root.fluidStandaloneActive ? root.renderedAlignedY : root.alignedY) : shadowBuffer + (root.fluidStandaloneActive ? root.renderedAlignedY : root.alignedY) - root._surfaceBodyY width: root.alignedWidth height: root.fluidStandaloneActive ? root.renderedAlignedHeight : root.alignedHeight @@ -759,7 +757,7 @@ Item { id: shadowSource width: rollOutAdjuster.baseWidth height: rollOutAdjuster.baseHeight - opacity: contentWrapper.opacity + opacity: contentWrapper.publishedOpacity scale: root.fluidStandaloneActive ? 1 : contentWrapper.scale x: root.fluidStandaloneActive ? 0 : contentWrapper.x y: root.fluidStandaloneActive ? 0 : contentWrapper.y @@ -775,23 +773,52 @@ Item { id: contentWrapper width: rollOutAdjuster.baseWidth height: rollOutAdjuster.baseHeight + + // _renderActive pins visibility/layer for the full transition; flipped true on shouldBeVisible rising, + // false only after the close animation completes. publishedOpacity tracks Item.opacity but on the GUI + // thread so consumers (WindowBlur, ElevationShadow, sibling rect) see interpolated values while the + // visual runs on the render thread via OpacityAnimator. + property bool _renderActive: Theme.isDirectionalEffect || shouldBeVisible + property bool _animating: false + property real publishedOpacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0) + opacity: Theme.isDirectionalEffect ? 1 : (shouldBeVisible ? 1 : 0) - visible: opacity > 0 + visible: _renderActive scale: contentContainer.scaleValue transformOrigin: Item.Center x: Theme.snap(contentContainer.animX + (rollOutAdjuster.baseWidth - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr) y: Theme.snap(contentContainer.animY + (rollOutAdjuster.baseHeight - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr) - layer.enabled: contentWrapper.opacity < 1 + layer.enabled: _animating || (!Theme.isDirectionalEffect && publishedOpacity < 1) layer.smooth: false layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0) Behavior on opacity { + enabled: !Theme.isDirectionalEffect + OpacityAnimator { + duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) + easing.type: Easing.BezierSpline + easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + onRunningChanged: contentWrapper._animating = running + } + } + + Behavior on publishedOpacity { enabled: !Theme.isDirectionalEffect NumberAnimation { duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve + onRunningChanged: if (!running && contentWrapper.publishedOpacity === 0) + contentWrapper._renderActive = false + } + } + + Connections { + target: root + function onShouldBeVisibleChanged() { + if (root.shouldBeVisible) + contentWrapper._renderActive = true; } } @@ -808,7 +835,7 @@ Item { height: rollOutAdjuster.baseHeight x: root.fluidStandaloneActive ? 0 : contentWrapper.x y: root.fluidStandaloneActive ? 0 : contentWrapper.y - opacity: contentWrapper.opacity + opacity: contentWrapper.publishedOpacity scale: root.fluidStandaloneActive ? 1 : contentWrapper.scale visible: contentWrapper.visible radius: Theme.cornerRadius diff --git a/quickshell/Widgets/DankScrollbar.qml b/quickshell/Widgets/DankScrollbar.qml index c5d2d70a..5e3eba8d 100644 --- a/quickshell/Widgets/DankScrollbar.qml +++ b/quickshell/Widgets/DankScrollbar.qml @@ -23,7 +23,7 @@ ScrollBar { visible: policy !== ScrollBar.AlwaysOff Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: 160 easing.type: Easing.OutQuad } diff --git a/quickshell/Widgets/DankSeekbar.qml b/quickshell/Widgets/DankSeekbar.qml index 85f13a50..80c2aaf3 100644 --- a/quickshell/Widgets/DankSeekbar.qml +++ b/quickshell/Widgets/DankSeekbar.qml @@ -225,7 +225,7 @@ Item { y: parent.midY - height / 2 z: 3 Behavior on x { - NumberAnimation { + XAnimator { duration: 80 } } diff --git a/quickshell/Widgets/DankSlider.qml b/quickshell/Widgets/DankSlider.qml index 70926448..6ce6fd67 100644 --- a/quickshell/Widgets/DankSlider.qml +++ b/quickshell/Widgets/DankSlider.qml @@ -171,7 +171,7 @@ Item { scale: active ? 1.05 : 1.0 Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } @@ -278,7 +278,7 @@ Item { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Theme.shortDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Widgets/DankTabBar.qml b/quickshell/Widgets/DankTabBar.qml index d3b72f12..2aafb33b 100644 --- a/quickshell/Widgets/DankTabBar.qml +++ b/quickshell/Widgets/DankTabBar.qml @@ -201,7 +201,7 @@ FocusScope { Behavior on x { enabled: indicator.animationEnabled - NumberAnimation { + XAnimator { duration: Theme.mediumDuration easing.type: Theme.standardEasing } diff --git a/quickshell/Widgets/DankToggle.qml b/quickshell/Widgets/DankToggle.qml index 76c21caa..5a18ae51 100644 --- a/quickshell/Widgets/DankToggle.qml +++ b/quickshell/Widgets/DankToggle.qml @@ -168,14 +168,14 @@ Item { scale: (toggle.checked && toggle.enabled) ? 1 : 0.6 Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: Anims.durShort easing.type: Easing.BezierSpline easing.bezierCurve: Anims.emphasized } } Behavior on scale { - NumberAnimation { + ScaleAnimator { duration: Anims.durShort easing.type: Easing.BezierSpline easing.bezierCurve: Anims.emphasized diff --git a/quickshell/Widgets/StyledRect.qml b/quickshell/Widgets/StyledRect.qml index 03ab84b2..1382c64d 100644 --- a/quickshell/Widgets/StyledRect.qml +++ b/quickshell/Widgets/StyledRect.qml @@ -20,7 +20,7 @@ Rectangle { } Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: standardAnimation.duration easing.type: standardAnimation["easing.type"] easing.bezierCurve: standardAnimation["easing.bezierCurve"] diff --git a/quickshell/Widgets/StyledText.qml b/quickshell/Widgets/StyledText.qml index 3c07ab1f..25a2d9c1 100644 --- a/quickshell/Widgets/StyledText.qml +++ b/quickshell/Widgets/StyledText.qml @@ -40,7 +40,7 @@ Text { //renderType: Text.NativeRendering Behavior on opacity { - NumberAnimation { + OpacityAnimator { duration: standardAnimation.duration easing.type: standardAnimation["easing.type"] easing.bezierCurve: standardAnimation["easing.bezierCurve"] diff --git a/scripts/format-staged.py b/scripts/format-staged.py new file mode 100755 index 00000000..523b6161 --- /dev/null +++ b/scripts/format-staged.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +"""Format staged .qml files using qmlls (the Qt QML language server). + +Per file: + 1. Speak LSP over stdio to qmlls: initialize -> didOpen -> formatting, + apply returned edits, save, `git add`. + 2. Run qmllint on the formatted file and warn about unused imports + (informational only — never modifies files). + +Refuses to run if any staged file also has unstaged changes, since `git add` +would silently absorb those into the commit. +""" + +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + + +TAB_SIZE = 4 +QMLLS_CANDIDATES = ["qmlls6", "qmlls"] +QMLLINT_CANDIDATES = ["/usr/lib/qt6/bin/qmllint", "qmllint6", "qmllint"] + + +def git(*args, cwd=None): + return subprocess.run( + ["git", *args], + cwd=cwd, + capture_output=True, + text=True, + check=True, + ).stdout + + +def repo_root(): + return Path(git("rev-parse", "--show-toplevel").strip()) + + +def staged_qml_files(root): + out = git("diff", "--cached", "--name-only", "--diff-filter=ACMR", cwd=root) + return [root / line for line in out.splitlines() if line.endswith(".qml")] + + +def has_unstaged_changes(root, file): + rel = str(file.relative_to(root)) + return git("diff", "--name-only", "--", rel, cwd=root).strip() != "" + + +def find_qmlls(): + for name in QMLLS_CANDIDATES: + path = shutil.which(name) + if path: + return path + return None + + +def find_qmllint(): + for candidate in QMLLINT_CANDIDATES: + path = candidate if "/" in candidate and Path(candidate).is_file() else shutil.which(candidate) + if not path: + continue + try: + result = subprocess.run([path, "--help"], capture_output=True, text=True, timeout=5) + except (subprocess.TimeoutExpired, OSError): + continue + if "--json" in result.stdout: + return path + return None + + +def lint_unused_imports(qmllint, file): + """Return a list of (line, message, suspect) for unused-import warnings. + + `suspect` is True when the same line also has an import-resolution failure, + which often means the warning is a false positive (qmllint couldn't find + the module, so its 'unused' verdict is unreliable). + """ + result = subprocess.run( + [qmllint, "--unused-imports", "warning", "--json", "-", str(file)], + capture_output=True, text=True, + ) + try: + data = json.loads(result.stdout) + except json.JSONDecodeError: + return [] + + files = data.get("files", []) + if not files: + return [] + warnings = files[0].get("warnings", []) + + failed_lines = {w["line"] for w in warnings if w.get("id") == "import" and "line" in w} + findings = [] + for w in warnings: + if w.get("id") != "unused-imports" or "line" not in w: + continue + line = w["line"] + findings.append((line, w.get("message", "Unused import"), line in failed_lines)) + findings.sort(key=lambda x: x[0]) + return findings + + +class LspClient: + def __init__(self, command): + self.proc = subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + ) + self._next_id = 1 + + def _send(self, msg): + body = json.dumps(msg).encode("utf-8") + header = f"Content-Length: {len(body)}\r\n\r\n".encode("ascii") + self.proc.stdin.write(header + body) + self.proc.stdin.flush() + + def _read(self): + headers = {} + while True: + line = self.proc.stdout.readline() + if not line: + raise RuntimeError("qmlls closed unexpectedly") + line = line.decode("ascii").rstrip("\r\n") + if line == "": + break + key, _, value = line.partition(":") + headers[key.strip().lower()] = value.strip() + length = int(headers["content-length"]) + body = b"" + while len(body) < length: + chunk = self.proc.stdout.read(length - len(body)) + if not chunk: + raise RuntimeError("qmlls closed mid-message") + body += chunk + return json.loads(body) + + def request(self, method, params): + req_id = self._next_id + self._next_id += 1 + self._send({"jsonrpc": "2.0", "id": req_id, "method": method, "params": params}) + while True: + msg = self._read() + if msg.get("id") == req_id and ("result" in msg or "error" in msg): + if "error" in msg: + raise RuntimeError(f"LSP {method} error: {msg['error']}") + return msg.get("result") + if "id" in msg and "method" in msg: + # Server-to-client request — reply with null so it doesn't stall. + self._send({"jsonrpc": "2.0", "id": msg["id"], "result": None}) + + def notify(self, method, params): + self._send({"jsonrpc": "2.0", "method": method, "params": params}) + + def shutdown(self): + try: + self.request("shutdown", None) + self.notify("exit", None) + except Exception: + pass + try: + self.proc.wait(timeout=2) + except subprocess.TimeoutExpired: + self.proc.kill() + + +def apply_edits(text, edits): + """Apply LSP TextEdits (non-overlapping) to text, end-first.""" + if not edits: + return text + + lines = text.splitlines(keepends=True) + line_starts = [0] + for line in lines: + line_starts.append(line_starts[-1] + len(line)) + + def offset(pos): + line = pos["line"] + if line >= len(line_starts): + return len(text) + return min(line_starts[line] + pos["character"], len(text)) + + sorted_edits = sorted( + edits, + key=lambda e: (e["range"]["start"]["line"], e["range"]["start"]["character"]), + reverse=True, + ) + for edit in sorted_edits: + start = offset(edit["range"]["start"]) + end = offset(edit["range"]["end"]) + text = text[:start] + edit["newText"] + text[end:] + return text + + +def main(): + root = repo_root() + files = staged_qml_files(root) + if not files: + print("No staged .qml files.") + return 0 + + dirty = [f for f in files if has_unstaged_changes(root, f)] + if dirty: + print("Refusing to format: staged files have unstaged changes:", file=sys.stderr) + for f in dirty: + print(f" {f.relative_to(root)}", file=sys.stderr) + print("\nStash or stage those changes first.", file=sys.stderr) + return 1 + + qmlls = find_qmlls() + if not qmlls: + print(f"qmlls not found (tried: {', '.join(QMLLS_CANDIDATES)})", file=sys.stderr) + return 1 + + qmllint = find_qmllint() + if not qmllint: + print("warning: qmllint with --json not found; skipping unused-import checks", file=sys.stderr) + + client = LspClient([qmlls]) + changed = 0 + unused_by_file = {} + try: + client.request("initialize", { + "processId": os.getpid(), + "rootUri": root.as_uri(), + "workspaceFolders": [{"uri": root.as_uri(), "name": root.name}], + "capabilities": { + "textDocument": { + "formatting": {"dynamicRegistration": False}, + "synchronization": {"dynamicRegistration": False}, + }, + }, + }) + client.notify("initialized", {}) + + for file in files: + rel = file.relative_to(root) + print(f" {rel} ... ", end="", flush=True) + + original = file.read_text() + uri = file.as_uri() + + client.notify("textDocument/didOpen", { + "textDocument": { + "uri": uri, + "languageId": "qml", + "version": 1, + "text": original, + }, + }) + + edits = client.request("textDocument/formatting", { + "textDocument": {"uri": uri}, + "options": {"tabSize": TAB_SIZE, "insertSpaces": True}, + }) + + client.notify("textDocument/didClose", {"textDocument": {"uri": uri}}) + + new_text = apply_edits(original, edits or []) + if new_text == original: + print("unchanged") + continue + + file.write_text(new_text) + git("add", "--", str(rel), cwd=root) + changed += 1 + print("formatted & staged") + + if qmllint: + for file in files: + findings = lint_unused_imports(qmllint, file) + if findings: + unused_by_file[file] = findings + + print(f"\n{changed} of {len(files)} file(s) changed.") + + if unused_by_file: + print("\nUnused import warnings (informational, not auto-removed):") + for file, findings in unused_by_file.items(): + rel = file.relative_to(root) + for line, message, suspect in findings: + suffix = " [suspect: import didn't resolve]" if suspect else "" + print(f" {rel}:{line} {message}{suffix}") + + return 0 + finally: + client.shutdown() + + +if __name__ == "__main__": + sys.exit(main())