diff --git a/quickshell/Common/ListViewTransitions.qml b/quickshell/Common/ListViewTransitions.qml index 57e8f1192..64bef9a1d 100644 --- a/quickshell/Common/ListViewTransitions.qml +++ b/quickshell/Common/ListViewTransitions.qml @@ -14,42 +14,68 @@ Singleton { readonly property bool enabled: Math.floor(Theme.currentAnimationBaseDuration * 0.4) >= 1 readonly property Transition add: enabled ? _add : null - readonly property Transition remove: enabled ? _remove : null + readonly property Transition remove: null readonly property Transition displaced: enabled ? _displaced : null readonly property Transition move: enabled ? _move : null - readonly property Transition _add: Transition { - DankAnim { - property: "opacity" - from: 0 - to: 1 - duration: Theme.expressiveDurations.expressiveEffects - easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel - } - } + readonly property int _staggerMs: Math.round(Theme.currentAnimationBaseDuration * 0.03) + readonly property int _staggerCap: 8 - readonly property Transition _remove: Transition { - DankAnim { - property: "opacity" - to: 0 - duration: Theme.expressiveDurations.fast - easing.bezierCurve: Theme.expressiveCurves.emphasizedAccel + readonly property Transition _add: Transition { + id: addTransition + + SequentialAnimation { + PropertyAction { + property: "opacity" + value: 0 + } + PauseAnimation { + duration: Math.max(0, Math.min(addTransition.ViewTransition.index - (addTransition.ViewTransition.targetIndexes[0] ?? 0), root._staggerCap)) * root._staggerMs + } + ParallelAnimation { + DankAnim { + property: "opacity" + from: 0 + to: 1 + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel + } + DankAnim { + property: "y" + from: addTransition.ViewTransition.destination.y + Theme.spacingS + to: addTransition.ViewTransition.destination.y + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel + } + } } } readonly property Transition _displaced: Transition { DankAnim { property: "y" - duration: Theme.expressiveDurations.normal - easing.bezierCurve: Theme.expressiveCurves.expressiveEffects + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.standard + } + DankAnim { + property: "opacity" + to: 1 + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.standard } } readonly property Transition _move: Transition { DankAnim { property: "y" - duration: Theme.expressiveDurations.normal - easing.bezierCurve: Theme.expressiveCurves.expressiveEffects + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.standard + } + DankAnim { + property: "opacity" + to: 1 + duration: Theme.expressiveDurations.fast + easing.bezierCurve: Theme.expressiveCurves.standard } } } diff --git a/quickshell/DMSShellIPC.qml b/quickshell/DMSShellIPC.qml index 187156813..8359819a2 100644 --- a/quickshell/DMSShellIPC.qml +++ b/quickshell/DMSShellIPC.qml @@ -1499,6 +1499,30 @@ Item { return "SPOTLIGHT_BAR_TOGGLE_SUCCESS"; } + function openWith(mode: string): string { + if (!mode) + return "SPOTLIGHT_BAR_OPEN_FAILED: No mode specified"; + PopoutService.openSpotlightBarWithMode(mode); + return `SPOTLIGHT_BAR_OPEN_SUCCESS: ${mode}`; + } + + function toggleWith(mode: string): string { + if (!mode) + return "SPOTLIGHT_BAR_TOGGLE_FAILED: No mode specified"; + PopoutService.toggleSpotlightBarWithMode(mode); + return `SPOTLIGHT_BAR_TOGGLE_SUCCESS: ${mode}`; + } + + function openQuery(query: string): string { + PopoutService.openSpotlightBarWithQuery(query); + return "SPOTLIGHT_BAR_OPEN_QUERY_SUCCESS"; + } + + function toggleQuery(query: string): string { + PopoutService.toggleSpotlightBarWithQuery(query); + return "SPOTLIGHT_BAR_TOGGLE_QUERY_SUCCESS"; + } + target: "spotlight-bar" } diff --git a/quickshell/Modals/DankLauncherV2/SpotlightLauncherContent.qml b/quickshell/Modals/DankLauncherV2/SpotlightLauncherContent.qml index 50b03a030..83e17c62a 100644 --- a/quickshell/Modals/DankLauncherV2/SpotlightLauncherContent.qml +++ b/quickshell/Modals/DankLauncherV2/SpotlightLauncherContent.qml @@ -21,10 +21,10 @@ FocusScope { readonly property real _rowH: 64 readonly property real _maxResultsH: Math.min(430, (parentModal?.screenHeight ?? 900) * 0.55) readonly property var _resultRows: _buildRows() - readonly property real _resultsContentH: _resultRows.length > 0 ? _resultRows.length * _rowH : _statusH + readonly property real _resultsContentH: _resultRows.length > 0 ? _resultRows.length * _rowH + resultsList.bottomInset : _statusH readonly property real _resultsH: _hasQuery ? Math.min(_resultsContentH, _maxResultsH) : 0 readonly property int _fastDuration: 90 - readonly property int _resizeDuration: 110 + readonly property int _resizeDuration: Theme.expressiveDurations.fast readonly property bool _blurActive: Theme.blurForegroundLayers || Theme.transparentBlurLayers readonly property real _searchSurfaceAlpha: { if (Theme.transparentBlurLayers) @@ -57,13 +57,18 @@ FocusScope { const flat = searchController.flatModel || []; const sections = searchController.sections || []; const rows = []; + const seen = {}; for (let i = 0; i < flat.length; i++) { const entry = flat[i]; if (!entry || entry.isHeader || !entry.item) continue; const section = sections[entry.sectionIndex] || null; + // Plugin item ids embed result content, so key them by slot position instead + const base = entry.item.pluginId ? (entry.sectionId + ":" + entry.indexInSection) : (entry.item.id || (entry.sectionId + ":" + (entry.item.name || entry.indexInSection))); + const bump = seen[base] || 0; + seen[base] = bump + 1; rows.push({ - "_rowId": entry.item.id || (entry.sectionId + ":" + entry.indexInSection + ":" + i), + "_rowId": bump ? base + "#" + bump : base, "item": entry.item, "flatIndex": i, "sectionTitle": section?.title || "", diff --git a/quickshell/Modals/DankLauncherV2/SpotlightResultsList.qml b/quickshell/Modals/DankLauncherV2/SpotlightResultsList.qml index bc004a427..276c29da7 100644 --- a/quickshell/Modals/DankLauncherV2/SpotlightResultsList.qml +++ b/quickshell/Modals/DankLauncherV2/SpotlightResultsList.qml @@ -61,6 +61,7 @@ Item { anchors.bottomMargin: root.bottomInset clip: true visible: root.rows.length > 0 + add: null readonly property int rowHeight: 64 diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index 93aa386d9..3d1c2ecd0 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -671,6 +671,8 @@ Singleton { property bool _spotlightBarWantsOpen: false property bool _spotlightBarWantsToggle: false + property string _spotlightBarPendingQuery: "" + property string _spotlightBarPendingMode: "" function openSpotlightBar() { if (spotlightBarModal) { @@ -682,6 +684,28 @@ Singleton { } } + function openSpotlightBarWithQuery(query: string) { + if (spotlightBarModal) { + spotlightBarModal.showWithQuery(query); + } else if (spotlightBarModalLoader) { + _spotlightBarPendingQuery = query; + _spotlightBarWantsOpen = true; + _spotlightBarWantsToggle = false; + spotlightBarModalLoader.active = true; + } + } + + function openSpotlightBarWithMode(mode: string) { + if (spotlightBarModal) { + spotlightBarModal.showWithMode(mode); + } else if (spotlightBarModalLoader) { + _spotlightBarPendingMode = mode; + _spotlightBarWantsOpen = true; + _spotlightBarWantsToggle = false; + spotlightBarModalLoader.active = true; + } + } + function closeSpotlightBar() { spotlightBarModal?.hide(); } @@ -696,15 +720,50 @@ Singleton { } } + function toggleSpotlightBarWithMode(mode: string) { + if (spotlightBarModal) { + spotlightBarModal.toggleWithMode(mode); + } else if (spotlightBarModalLoader) { + _spotlightBarPendingMode = mode; + _spotlightBarWantsToggle = true; + _spotlightBarWantsOpen = false; + spotlightBarModalLoader.active = true; + } + } + + function toggleSpotlightBarWithQuery(query: string) { + if (spotlightBarModal) { + spotlightBarModal.toggleWithQuery(query); + } else if (spotlightBarModalLoader) { + _spotlightBarPendingQuery = query; + _spotlightBarWantsOpen = true; + _spotlightBarWantsToggle = false; + spotlightBarModalLoader.active = true; + } + } + function _onSpotlightBarModalLoaded() { if (_spotlightBarWantsOpen) { _spotlightBarWantsOpen = false; - spotlightBarModal?.show(); + if (_spotlightBarPendingQuery) { + spotlightBarModal?.showWithQuery(_spotlightBarPendingQuery); + _spotlightBarPendingQuery = ""; + } else if (_spotlightBarPendingMode) { + spotlightBarModal?.showWithMode(_spotlightBarPendingMode); + _spotlightBarPendingMode = ""; + } else { + spotlightBarModal?.show(); + } return; } if (_spotlightBarWantsToggle) { _spotlightBarWantsToggle = false; - spotlightBarModal?.toggle(); + if (_spotlightBarPendingMode) { + spotlightBarModal?.toggleWithMode(_spotlightBarPendingMode); + _spotlightBarPendingMode = ""; + } else { + spotlightBarModal?.toggle(); + } } }