separate search and window overview

This commit is contained in:
Daniel Bulant 2025-06-29 10:43:51 +02:00
parent 91c9ea9df6
commit 9ca8dffa00
No known key found for this signature in database
7 changed files with 191 additions and 88 deletions

View file

@ -10,9 +10,11 @@ Singleton {
id: root id: root
property bool sidebarLeftOpen: false property bool sidebarLeftOpen: false
property bool sidebarRightOpen: false property bool sidebarRightOpen: false
property bool overviewOpen: false property bool overviewWindowsOpen: false
property bool overviewSearchOpen: false
property bool workspaceShowNumbers: false property bool workspaceShowNumbers: false
property bool superReleaseMightTrigger: true property bool superReleaseMightTrigger: true
property bool dontAutoCancelSearch: false
// When user is not reluctant while pressing super, they probably don't need to see workspace numbers // When user is not reluctant while pressing super, they probably don't need to see workspace numbers
onSuperReleaseMightTriggerChanged: { onSuperReleaseMightTriggerChanged: {

View file

@ -122,8 +122,8 @@ Singleton {
property QtObject search: QtObject { property QtObject search: QtObject {
property int nonAppResultDelay: 30 // This prevents lagging when typing property int nonAppResultDelay: 30 // This prevents lagging when typing
property string engineBaseUrl: "https://www.google.com/search?q=" property string engineBaseUrl: "https://kagi.com/search?q="
property list<string> excludedSites: [ "quora.com" ] property list<string> excludedSites: [ ]
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird. property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
property QtObject prefix: QtObject { property QtObject prefix: QtObject {
property string action: "/" property string action: "/"

View file

@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import "root:/" import "root:/"
import "root:/services" import "root:/services"
import "root:/modules/common" import "root:/modules/common"
@ -12,18 +13,21 @@ import Quickshell.Hyprland
Scope { Scope {
id: overviewScope id: overviewScope
property bool dontAutoCancelSearch: false
OverviewSearch {
id: searchPanel
}
Variants { Variants {
id: overviewVariants id: overviewVariants
model: Quickshell.screens model: Quickshell.screens
PanelWindow { PanelWindow {
id: root id: root
required property var modelData required property var modelData
property string searchingText: ""
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id) property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
screen: modelData screen: modelData
visible: GlobalStates.overviewOpen visible: GlobalStates.overviewWindowsOpen
WlrLayershell.namespace: "quickshell:overview" WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.layer: WlrLayer.Overlay
@ -31,10 +35,10 @@ Scope {
color: "transparent" color: "transparent"
mask: Region { mask: Region {
item: GlobalStates.overviewOpen ? columnLayout : null item: GlobalStates.overviewWindowsOpen ? columnLayout : null
} }
HyprlandWindow.visibleMask: Region { HyprlandWindow.visibleMask: Region {
item: GlobalStates.overviewOpen ? columnLayout : null item: GlobalStates.overviewWindowsOpen ? columnLayout : null
} }
@ -49,22 +53,15 @@ Scope {
id: grab id: grab
windows: [ root ] windows: [ root ]
property bool canBeActive: root.monitorIsFocused property bool canBeActive: root.monitorIsFocused
active: false
onCleared: () => { onCleared: () => {
if (!active) GlobalStates.overviewOpen = false if (!active) GlobalStates.overviewWindowsOpen = false
} }
} }
Connections { Connections {
target: GlobalStates target: GlobalStates
function onOverviewOpenChanged() { function onOverviewWindowsOpenChanged() {
if (!GlobalStates.overviewOpen) { if (GlobalStates.overviewWindowsOpen) {
searchWidget.disableExpandAnimation()
overviewScope.dontAutoCancelSearch = false;
} else {
if (!overviewScope.dontAutoCancelSearch) {
searchWidget.cancelSearch()
}
delayedGrabTimer.start() delayedGrabTimer.start()
} }
} }
@ -76,20 +73,16 @@ Scope {
repeat: false repeat: false
onTriggered: { onTriggered: {
if (!grab.canBeActive) return if (!grab.canBeActive) return
grab.active = GlobalStates.overviewOpen grab.active = GlobalStates.overviewWindowsOpen
} }
} }
implicitWidth: columnLayout.implicitWidth implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight implicitHeight: columnLayout.implicitHeight
function setSearchingText(text) {
searchWidget.setSearchingText(text);
}
ColumnLayout { ColumnLayout {
id: columnLayout id: columnLayout
visible: GlobalStates.overviewOpen visible: GlobalStates.overviewWindowsOpen
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
top: !ConfigOptions.bar.bottom ? parent.top : undefined top: !ConfigOptions.bar.bottom ? parent.top : undefined
@ -98,11 +91,11 @@ Scope {
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
GlobalStates.overviewOpen = false; GlobalStates.overviewWindowsOpen = false;
} else if (event.key === Qt.Key_Left) { } else if (event.key === Qt.Key_Left) {
if (!root.searchingText) Hyprland.dispatch("workspace r-1"); Hyprland.dispatch("workspace r-1");
} else if (event.key === Qt.Key_Right) { } else if (event.key === Qt.Key_Right) {
if (!root.searchingText) Hyprland.dispatch("workspace r+1"); Hyprland.dispatch("workspace r+1");
} }
} }
@ -111,20 +104,12 @@ Scope {
width: 1 // Prevent Wayland protocol error width: 1 // Prevent Wayland protocol error
} }
SearchWidget {
id: searchWidget
Layout.alignment: Qt.AlignHCenter
onSearchingTextChanged: (text) => {
root.searchingText = searchingText
}
}
Loader { Loader {
id: overviewLoader id: overviewLoader
active: GlobalStates.overviewOpen active: GlobalStates.overviewWindowsOpen
sourceComponent: OverviewWidget { sourceComponent: OverviewWidget {
panelWindow: root panelWindow: root
visible: (root.searchingText == "") visible: true
} }
} }
} }
@ -132,29 +117,37 @@ Scope {
} }
} }
IpcHandler {
target: "overview"
function toggle() { // IpcHandler {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen // target: "overview"
}
function close() {
GlobalStates.overviewOpen = false
}
function open() {
GlobalStates.overviewOpen = true
}
function toggleReleaseInterrupt() {
GlobalStates.superReleaseMightTrigger = false
}
}
// function toggle() {
// GlobalStates.overviewOpen = !GlobalStates.overviewOpen
// }
// function close() {
// GlobalStates.overviewOpen = false
// }
// function open() {
// GlobalStates.overviewOpen = true
// }
// function toggleReleaseInterrupt() {
// GlobalStates.superReleaseMightTrigger = false
// }
// }
GlobalShortcut {
name: "overviewSearchToggle"
description: qsTr("Toggles overview on press")
onPressed: {
GlobalStates.overviewSearchOpen = !GlobalStates.overviewSearchOpen
}
}
GlobalShortcut { GlobalShortcut {
name: "overviewToggle" name: "overviewToggle"
description: qsTr("Toggles overview on press") description: qsTr("Toggles overview on press")
onPressed: { onPressed: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen GlobalStates.overviewWindowsOpen = !GlobalStates.overviewWindowsOpen
} }
} }
GlobalShortcut { GlobalShortcut {
@ -162,7 +155,8 @@ Scope {
description: qsTr("Closes overview") description: qsTr("Closes overview")
onPressed: { onPressed: {
GlobalStates.overviewOpen = false GlobalStates.overviewSearchOpen = false
GlobalStates.overviewWindowOpen = false
} }
} }
GlobalShortcut { GlobalShortcut {
@ -178,7 +172,7 @@ Scope {
GlobalStates.superReleaseMightTrigger = true GlobalStates.superReleaseMightTrigger = true
return return
} }
GlobalStates.overviewOpen = !GlobalStates.overviewOpen GlobalStates.overviewWindowsOpen = !GlobalStates.overviewWindowsOpen
} }
} }
GlobalShortcut { GlobalShortcut {
@ -196,21 +190,15 @@ Scope {
description: qsTr("Toggle clipboard query on overview widget") description: qsTr("Toggle clipboard query on overview widget")
onPressed: { onPressed: {
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { if (GlobalStates.overviewSearchOpen && GlobalStates.dontAutoCancelSearch) {
GlobalStates.overviewOpen = false; GlobalStates.overviewSearchOpen = false;
return; return;
} }
for (let i = 0; i < overviewVariants.instances.length; i++) { GlobalStates.dontAutoCancelSearch = true;
let panelWindow = overviewVariants.instances[i]; searchPanel.setSearchingText(
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
ConfigOptions.search.prefix.clipboard ConfigOptions.search.prefix.clipboard
); );
GlobalStates.overviewOpen = true; GlobalStates.overviewSearchOpen = true;
return
}
}
} }
} }
@ -219,21 +207,15 @@ Scope {
description: qsTr("Toggle emoji query on overview widget") description: qsTr("Toggle emoji query on overview widget")
onPressed: { onPressed: {
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { if (GlobalStates.overviewSearchOpen && GlobalStates.dontAutoCancelSearch) {
GlobalStates.overviewOpen = false; GlobalStates.overviewSearchOpen = false;
return; return;
} }
for (let i = 0; i < overviewVariants.instances.length; i++) { GlobalStates.dontAutoCancelSearch = true;
let panelWindow = overviewVariants.instances[i]; searchPanel.setSearchingText(
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
ConfigOptions.search.prefix.emojis ConfigOptions.search.prefix.emojis
); );
GlobalStates.overviewOpen = true; GlobalStates.overviewSearchOpen = true;
return
}
}
} }
} }

View file

@ -0,0 +1,118 @@
pragma ComponentBehavior: Bound
import "root:/"
import "root:/services"
import "root:/modules/common"
import "root:/modules/common/widgets"
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
// Scope {
// id: overviewScope
PanelWindow {
id: root
// required property var modelData
property string searchingText: ""
// readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
// property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
// property bool searching: overviewScope.previewWindows
// screen: modelData
visible: GlobalStates.overviewSearchOpen
WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
color: "transparent"
mask: Region {
item: GlobalStates.overviewSearchOpen ? columnLayout : null
}
HyprlandWindow.visibleMask: Region {
item: GlobalStates.overviewSearchOpen ? columnLayout : null
}
anchors {
top: true
left: true
right: true
bottom: true
}
HyprlandFocusGrab {
id: grab
windows: [ root ]
// property bool canBeActive: root.monitorIsFocused
onCleared: () => {
if (!active) GlobalStates.overviewSearchOpen = false
}
}
Connections {
target: GlobalStates
function onOverviewSearchOpenChanged() {
if (!GlobalStates.overviewSearchOpen) {
searchWidget.disableExpandAnimation()
GlobalStates.dontAutoCancelSearch = false;
} else {
if (!GlobalStates.dontAutoCancelSearch) {
searchWidget.cancelSearch()
}
delayedGrabTimer.start()
}
}
}
Timer {
id: delayedGrabTimer
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
repeat: false
onTriggered: {
// if (!grab.canBeActive) return
grab.active = GlobalStates.overviewSearchOpen
}
}
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
function setSearchingText(text) {
searchWidget.setSearchingText(text);
}
ColumnLayout {
id: columnLayout
visible: GlobalStates.overviewSearchOpen
anchors {
horizontalCenter: parent.horizontalCenter
top: !ConfigOptions.bar.bottom ? parent.top : undefined
bottom: ConfigOptions.bar.bottom ? parent.bottom : undefined
}
Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape) {
GlobalStates.overviewSearchOpen = false;
}
}
Item {
height: 1 // Prevent Wayland protocol error
width: 1 // Prevent Wayland protocol error
}
SearchWidget {
id: searchWidget
Layout.alignment: Qt.AlignHCenter
onSearchingTextChanged: (text) => {
root.searchingText = searchingText
// root.searching = (searchingText.length > 0)
}
}
}
}
// }

View file

@ -112,7 +112,7 @@ Item {
onClicked: { onClicked: {
if (root.draggingTargetWorkspace === -1) { if (root.draggingTargetWorkspace === -1) {
// Hyprland.dispatch(`exec qs ipc call overview close`) // Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false GlobalStates.overviewWindowsOpen = false
Hyprland.dispatch(`workspace ${workspaceValue}`) Hyprland.dispatch(`workspace ${workspaceValue}`)
} }
} }
@ -225,7 +225,7 @@ Item {
if (!windowData) return; if (!windowData) return;
if (event.button === Qt.LeftButton) { if (event.button === Qt.LeftButton) {
GlobalStates.overviewOpen = false GlobalStates.overviewWindowsOpen = false
Hyprland.dispatch(`focuswindow address:${windowData.address}`) Hyprland.dispatch(`focuswindow address:${windowData.address}`)
event.accepted = true event.accepted = true
} else if (event.button === Qt.MiddleButton) { } else if (event.button === Qt.MiddleButton) {

View file

@ -70,7 +70,7 @@ Item { // Window
ScreencopyView { ScreencopyView {
id: windowPreview id: windowPreview
anchors.fill: parent anchors.fill: parent
captureSource: GlobalStates.overviewOpen ? root.toplevel : null captureSource: GlobalStates.overviewWindowsOpen ? root.toplevel : null
live: true live: true
Rectangle { Rectangle {

View file

@ -25,13 +25,14 @@ Item { // Wrapper
property string mathResult: "" property string mathResult: ""
function disableExpandAnimation() { function disableExpandAnimation() {
searchWidthBehavior.enabled = false; searchWidthBehavior.enabled = false
} }
function cancelSearch() { function cancelSearch() {
searchInput.selectAll() // searchInput.selectAll()
searchInput.text = ""
root.searchingText = "" root.searchingText = ""
searchWidthBehavior.enabled = true; searchWidthBehavior.enabled = true
} }
function setSearchingText(text) { function setSearchingText(text) {
@ -208,7 +209,7 @@ Item { // Wrapper
TextField { // Search box TextField { // Search box
id: searchInput id: searchInput
focus: GlobalStates.overviewOpen focus: GlobalStates.overviewSearchOpen
Layout.rightMargin: 15 Layout.rightMargin: 15
padding: 15 padding: 15
renderType: Text.NativeRendering renderType: Text.NativeRendering