diff --git a/Helpers/Searcher.qml b/Helpers/Searcher.qml index 23f15e5..d03bc41 100644 --- a/Helpers/Searcher.qml +++ b/Helpers/Searcher.qml @@ -26,6 +26,7 @@ Singleton { function query(search: string): list { search = transformSearch(search); + console.log(!search, search, ...list); if (!search) return [...list]; diff --git a/Modules/Launcher/AppList.qml b/Modules/Launcher/AppList.qml index 497bbec..4bb7921 100644 --- a/Modules/Launcher/AppList.qml +++ b/Modules/Launcher/AppList.qml @@ -1,28 +1,36 @@ pragma ComponentBehavior: Bound -import Quickshell import QtQuick -import qs.Modules.Launcher.Services -import qs.Modules.Launcher.Items -import qs.Components +import Quickshell import ZShell.Config +import qs.Components import qs.Services +import qs.Modules.Launcher.Items +import qs.Modules.Launcher.Services CustomListView { id: root + readonly property string displayState: stateForText(displayText) + property string displayText + readonly property string requestedState: stateForText(search.text) required property SearchBar search required property PersistentProperties visibilities - highlightFollowsCurrentItem: false - highlightRangeMode: ListView.ApplyRange - implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing - orientation: Qt.Vertical - preferredHighlightBegin: 0 - preferredHighlightEnd: height - spacing: Tokens.spacing.small - state: { - const text = search.text; + function resultsForText(text: string): var { + switch (stateForText(text)) { + case "actions": + return Actions.query(text); + case "calc": + return [0]; + case "variant": + return SchemeVariants.query(text); + default: + return Apps.search(text); + } + } + + function stateForText(text: string): string { const prefix = Config.launcher.actionPrefix; if (text.startsWith(prefix)) { for (const action of ["calc", "scheme", "variant"]) @@ -34,6 +42,20 @@ CustomListView { return "apps"; } + + function syncDisplayText(): void { + if (visibilities.launcher && requestedState === displayState) + displayText = search.text; + } + + highlightFollowsCurrentItem: false + highlightRangeMode: ListView.ApplyRange + implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing + orientation: Qt.Vertical + preferredHighlightBegin: 0 + preferredHighlightEnd: height + spacing: Tokens.spacing.small + state: visibilities.launcher ? requestedState : displayState verticalLayoutDirection: ListView.BottomToTop CustomScrollBar.vertical: CustomScrollBar { @@ -44,19 +66,21 @@ CustomListView { Anim { from: 0 - properties: "opacity,scale" + property: "opacity" to: 1 + type: Anim.DefaultEffects } } addDisplaced: Transition { Anim { - duration: Tokens.anim.durations.small property: "y" + type: Anim.StandardSmall } Anim { - properties: "opacity,scale" + property: "opacity" to: 1 + type: Anim.DefaultEffects } } displaced: Transition { @@ -65,8 +89,9 @@ CustomListView { } Anim { - properties: "opacity,scale" + property: "opacity" to: 1 + type: Anim.DefaultEffects } } highlight: CustomRect { @@ -85,7 +110,7 @@ CustomListView { } } model: ScriptModel { - id: model + values: root.resultsForText(root.displayText) onValuesChanged: root.currentIndex = 0 } @@ -95,8 +120,9 @@ CustomListView { } Anim { - properties: "opacity,scale" + property: "opacity" to: 1 + type: Anim.DefaultEffects } } remove: Transition { @@ -104,8 +130,9 @@ CustomListView { Anim { from: 1 - properties: "opacity,scale" + property: "opacity" to: 0 + type: Anim.DefaultEffects } } states: [ @@ -113,7 +140,6 @@ CustomListView { name: "apps" PropertyChanges { - model.values: Apps.search(search.text) root.delegate: appItem } }, @@ -121,7 +147,6 @@ CustomListView { name: "actions" PropertyChanges { - model.values: Actions.query(search.text) root.delegate: actionItem } }, @@ -129,7 +154,6 @@ CustomListView { name: "calc" PropertyChanges { - model.values: [0] root.delegate: calcItem } }, @@ -137,7 +161,6 @@ CustomListView { name: "variant" PropertyChanges { - model.values: SchemeVariants.query(search.text) root.delegate: variantItem } } @@ -147,7 +170,7 @@ CustomListView { ParallelAnimation { Anim { duration: Tokens.anim.durations.small - easing.bezierCurve: Tokens.anim.curves.expressiveEffects + easing.bezierCurve: Tokens.anim.curves.standardAccel from: 1 property: "opacity" target: root @@ -156,7 +179,7 @@ CustomListView { Anim { duration: Tokens.anim.durations.small - easing.bezierCurve: Tokens.anim.curves.expressiveEffects + easing.bezierCurve: Tokens.anim.curves.standardAccel from: 1 property: "scale" target: root @@ -165,14 +188,24 @@ CustomListView { } PropertyAction { - properties: "values,delegate" - targets: [model, root] + property: "delegate" + target: root + value: null + } + + ScriptAction { + script: root.displayText = root.search.text + } + + PropertyAction { + property: "delegate" + target: root } ParallelAnimation { Anim { duration: Tokens.anim.durations.small - easing.bezierCurve: Tokens.anim.curves.expressiveEffects + easing.bezierCurve: Tokens.anim.curves.standardDecel from: 0 property: "opacity" target: root @@ -181,7 +214,7 @@ CustomListView { Anim { duration: Tokens.anim.durations.small - easing.bezierCurve: Tokens.anim.curves.expressiveEffects + easing.bezierCurve: Tokens.anim.curves.standardDecel from: 0.9 property: "scale" target: root @@ -197,6 +230,8 @@ CustomListView { } } + Component.onCompleted: displayText = search.text + Component { id: appItem @@ -228,4 +263,20 @@ CustomListView { list: root } } + + Connections { + function onTextChanged() { + root.syncDisplayText(); + } + + target: root.search + } + + Connections { + function onLauncherChanged() { + root.syncDisplayText(); + } + + target: root.visibilities + } } diff --git a/Plugins/ZShell/Components/carouselview.cpp b/Plugins/ZShell/Components/carouselview.cpp index 9428c1a..c1341d5 100644 --- a/Plugins/ZShell/Components/carouselview.cpp +++ b/Plugins/ZShell/Components/carouselview.cpp @@ -21,6 +21,7 @@ constexpr int kMaxLayoutCapacity = 64; CarouselView::CarouselView(QQuickItem* parent) : QQuickItem(parent) { setAcceptedMouseButtons(Qt::LeftButton); + setFiltersChildMouseEvents(true); setFlag(QQuickItem::ItemHasContents, false); setClip(false); @@ -80,6 +81,89 @@ void CarouselView::setDelegate(QQmlComponent* c) { relayout(); } +bool CarouselView::childMouseEventFilter(QQuickItem* item, QEvent* event) { + Q_UNUSED(item); + + auto* mouseEvent = dynamic_cast(event); + if (!mouseEvent) return false; + + const QPointF pos = mapFromItem(item, mouseEvent->position()); + + switch (mouseEvent->type()) { + case QEvent::MouseButtonPress: { + if (mouseEvent->button() != Qt::LeftButton) return false; + + m_dragging = true; + m_dragActive = false; + + m_pressPos = pos; + m_pressContentX = m_contentX; + + m_dragTimer.start(); + + m_lastMoveX = pos.x(); + m_lastMoveT = 0; + m_velocity = 0; + + return false; + } + + case QEvent::MouseMove: { + if (!m_dragging) return false; + + const qreal dx = pos.x() - m_pressPos.x(); + + if (!m_dragActive && std::abs(dx) > kDragThreshold) { + m_dragActive = true; + m_pressContentX = m_contentX; + + cancelAnimations(); + grabMouse(); + } + + if (!m_dragActive) return false; + + m_contentX = m_pressContentX - dx; + + wrapContentIfNeeded(); + updateCurrentIndexFromContentX(true); + relayout(); + + const qint64 t = m_dragTimer.elapsed(); + const qint64 dt = t - m_lastMoveT; + + if (dt > 0) { + const qreal instVel = (m_lastMoveX - pos.x()) / (dt / 1000.0); + + m_velocity = m_velocity * 0.7 + instVel * 0.3; + } + + m_lastMoveX = pos.x(); + m_lastMoveT = t; + + mouseEvent->accept(); + return true; + } + + case QEvent::MouseButtonRelease: { + if (!m_dragging) return false; + + if (!m_dragActive) { + m_dragging = false; + return false; + } + + return false; + } + + case QEvent::MouseButtonDblClick: + return false; + + default: + return false; + } +} + void CarouselView::setModel(const QVariantList& m) { m_model = m; diff --git a/Plugins/ZShell/Components/carouselview.hpp b/Plugins/ZShell/Components/carouselview.hpp index d822c79..14dea74 100644 --- a/Plugins/ZShell/Components/carouselview.hpp +++ b/Plugins/ZShell/Components/carouselview.hpp @@ -118,6 +118,8 @@ class CarouselView : public QQuickItem { void previewIndexChanged(int realIndex, QVariant modelData); protected: + bool childMouseEventFilter(QQuickItem* item, QEvent* event) override; + void geometryChange(const QRectF& newGeo, const QRectF& oldGeo) override; void mousePressEvent(QMouseEvent* event) override;