Launcher: clipboard, prevent unecessary refresh while browsing

This commit is contained in:
LemmyCook 2025-09-03 10:25:44 -04:00
parent 20b29f98a7
commit 7b2d490ba7
6 changed files with 91 additions and 37 deletions

View file

@ -11,8 +11,18 @@ NPanel {
id: root id: root
// Panel configuration // Panel configuration
panelWidth: Math.min(700 * scaling, screen?.width * 0.75) panelWidth: {
panelHeight: Math.min(600 * scaling, screen?.height * 0.8) var w = Math.round(Math.max(screen?.width * 0.3, 500) * scaling)
w = Math.min(w, screen?.width - Style.marginL * 2)
return w
}
panelHeight: {
var h = Math.round(Math.max(screen?.height * 0.5, 600) * scaling)
h = Math.min(h, screen?.height - Style.barHeight * scaling - Style.marginL * 2)
return h
}
panelKeyboardFocus: true panelKeyboardFocus: true
panelBackgroundColor: Qt.rgba(Color.mSurface.r, Color.mSurface.g, Color.mSurface.b, panelBackgroundColor: Qt.rgba(Color.mSurface.r, Color.mSurface.g, Color.mSurface.b,
Settings.data.appLauncher.backgroundOpacity) Settings.data.appLauncher.backgroundOpacity)
@ -246,7 +256,7 @@ NPanel {
id: entry id: entry
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex) property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
property int badgeSize: Style.baseWidgetSize * 1.75 * scaling property int badgeSize: Style.baseWidgetSize * 1.6 * scaling
// Property to reliably track the current item's ID. // Property to reliably track the current item's ID.
// This changes whenever the delegate is recycled for a new item. // This changes whenever the delegate is recycled for a new item.

View file

@ -5,7 +5,7 @@ import qs.Commons
import qs.Services import qs.Services
import "../../../Helpers/FuzzySort.js" as Fuzzysort import "../../../Helpers/FuzzySort.js" as Fuzzysort
QtObject { Item {
property var launcher: null property var launcher: null
property string name: "Applications" property string name: "Applications"
property bool handleSearch: true property bool handleSearch: true

View file

@ -2,7 +2,7 @@ import QtQuick
import qs.Services import qs.Services
import "../../../Helpers/AdvancedMath.js" as AdvancedMath import "../../../Helpers/AdvancedMath.js" as AdvancedMath
QtObject { Item {
property var launcher: null property var launcher: null
property string name: "Calculator" property string name: "Calculator"

View file

@ -3,7 +3,7 @@ import Quickshell
import qs.Commons import qs.Commons
import qs.Services import qs.Services
QtObject { Item {
id: root id: root
// Plugin metadata // Plugin metadata
@ -13,26 +13,47 @@ QtObject {
// Plugin capabilities // Plugin capabilities
property bool handleSearch: false // Don't handle regular search property bool handleSearch: false // Don't handle regular search
// Internal state
property bool isWaitingForData: false
property bool gotResults: false
property string lastSearchText: ""
// Listen for clipboard data updates // Listen for clipboard data updates
// Connections { Connections {
// target: ClipboardService target: ClipboardService
// function onListCompleted() { function onListCompleted() {
// // Only refresh if the clipboard plugin is active if (gotResults) {
// if (launcher && launcher.activePlugin === root) { // Do not update results after the first fetch.
// launcher.updateResults() // This will avoid the list resetting every 2seconds when the service updates.
// } return
// } }
// } // Refresh results if we're waiting for data or if clipboard plugin is active
if (isWaitingForData || (launcher && launcher.searchText.startsWith(">clip"))) {
isWaitingForData = false
gotResults = true
if (launcher) {
launcher.updateResults()
}
}
}
}
// Initialize plugin // Initialize plugin
function init() { function init() {
Logger.log("ClipboardPlugin", "Initialized") Logger.log("ClipboardPlugin", "Initialized")
// Pre-load clipboard data if service is active
if (ClipboardService.active) {
ClipboardService.list(100)
}
} }
// Called when launcher opens // Called when launcher opens
function onOpened() { function onOpened() {
// Refresh clipboard history when launcher opens // Refresh clipboard history when launcher opens
ClipboardService.list(100) if (ClipboardService.active) {
isWaitingForData = true
ClipboardService.list(100)
}
} }
// Check if this plugin handles the command // Check if this plugin handles the command
@ -45,7 +66,7 @@ QtObject {
return [{ return [{
"name": ">clip", "name": ">clip",
"description": "Search clipboard history", "description": "Search clipboard history",
"icon": "content_paste", "icon": "text-x-generic",
"isImage": false, "isImage": false,
"onActivate": function () { "onActivate": function () {
launcher.setSearchText(">clip ") launcher.setSearchText(">clip ")
@ -53,7 +74,7 @@ QtObject {
}, { }, {
"name": ">clip clear", "name": ">clip clear",
"description": "Clear all clipboard history", "description": "Clear all clipboard history",
"icon": "delete_sweep", "icon": "text-x-generic",
"isImage": false, "isImage": false,
"onActivate": function () { "onActivate": function () {
ClipboardService.wipeAll() ClipboardService.wipeAll()
@ -68,15 +89,28 @@ QtObject {
return [] return []
} }
lastSearchText = searchText
const results = [] const results = []
const query = searchText.slice(5).trim() const query = searchText.slice(5).trim()
// Check if clipboard service is not active
if (!ClipboardService.active) {
return [{
"name": "Clipboard History Disabled",
"description": "Enable clipboard history in settings or install cliphist",
"icon": "view-refresh",
"isImage": false,
"onActivate": function () {}
}]
}
// Special command: clear // Special command: clear
if (query === "clear") { if (query === "clear") {
return [{ return [{
"name": "Clear Clipboard History", "name": "Clear Clipboard History",
"description": "Remove all items from clipboard history", "description": "Remove all items from clipboard history",
"icon": "delete_sweep", "icon": "delete_sweep",
"isImage": false,
"onActivate": function () { "onActivate": function () {
ClipboardService.wipeAll() ClipboardService.wipeAll()
launcher.close() launcher.close()
@ -84,8 +118,24 @@ QtObject {
}] }]
} }
// Show loading state if data isn't ready yet // Show loading state if data is being loaded
if (ClipboardService.loading) { if (ClipboardService.loading || isWaitingForData) {
return [{
"name": "Loading clipboard history...",
"description": "Please wait",
"icon": "view-refresh",
"isImage": false,
"onActivate": function () {}
}]
}
// Get clipboard items
const items = ClipboardService.items || []
// If no items and we haven't tried loading yet, trigger a load
if (items.length === 0 && !ClipboardService.loading) {
isWaitingForData = true
ClipboardService.list(100)
return [{ return [{
"name": "Loading clipboard history...", "name": "Loading clipboard history...",
"description": "Please wait", "description": "Please wait",
@ -98,10 +148,6 @@ QtObject {
// Search clipboard items // Search clipboard items
const searchTerm = query.toLowerCase() const searchTerm = query.toLowerCase()
// Force dependency update
const _rev = ClipboardService.revision
const items = ClipboardService.items || []
// Filter and format results // Filter and format results
items.forEach(function (item) { items.forEach(function (item) {
const preview = (item.preview || "").toLowerCase() const preview = (item.preview || "").toLowerCase()
@ -140,7 +186,7 @@ QtObject {
}) })
} }
Logger.log("ClipboardPlugin", `Returning ${results.length} results`) //Logger.log("ClipboardPlugin", `Returning ${results.length} results for query: "${query}"`)
return results return results
} }
@ -216,4 +262,4 @@ QtObject {
function getImageForItem(clipboardId) { function getImageForItem(clipboardId) {
return ClipboardService.getImageData ? ClipboardService.getImageData(clipboardId) : null return ClipboardService.getImageData ? ClipboardService.getImageData(clipboardId) : null
} }
} }

View file

@ -10,10 +10,9 @@ Singleton {
id: root id: root
// Public API // Public API
property var items: [] // [{id, preview, mime, isImage}] property bool active: Settings.isLoaded && Settings.data.appLauncher.enableClipboardHistory && cliphistAvailable
property bool loading: false property bool loading: false
// Active only when feature is enabled, settings have finished initial load, and cliphist is available property var items: [] // [{id, preview, mime, isImage}]
property bool active: Settings.data.appLauncher.enableClipboardHistory && Settings.isLoaded && cliphistAvailable
// Check if cliphist is available on the system // Check if cliphist is available on the system
property bool cliphistAvailable: false property bool cliphistAvailable: false
@ -39,7 +38,7 @@ Singleton {
property string _b64CurrentMime: "" property string _b64CurrentMime: ""
property string _b64CurrentId: "" property string _b64CurrentId: ""
signal listCompleted() signal listCompleted
// Check if cliphist is available // Check if cliphist is available
Component.onCompleted: { Component.onCompleted: {
@ -82,7 +81,7 @@ Singleton {
// Start/stop watchers when enabled changes // Start/stop watchers when enabled changes
onActiveChanged: { onActiveChanged: {
if (root.active && root.cliphistAvailable) { if (root.active) {
startWatchers() startWatchers()
} else { } else {
stopWatchers() stopWatchers()
@ -96,7 +95,7 @@ Singleton {
Timer { Timer {
interval: 5000 interval: 5000
repeat: true repeat: true
running: root.active && root.cliphistAvailable running: root.active
onTriggered: list() onTriggered: list()
} }
@ -185,9 +184,9 @@ Singleton {
const url = `data:${root._b64CurrentMime};base64,${b64}` const url = `data:${root._b64CurrentMime};base64,${b64}`
try { try {
root._b64CurrentCb(url) root._b64CurrentCb(url)
} finally { } catch (e) {
/* noop */ } }
} }
if (root._b64CurrentId !== "") { if (root._b64CurrentId !== "") {
root.imageDataById[root._b64CurrentId] = `data:${root._b64CurrentMime};base64,${b64}` root.imageDataById[root._b64CurrentId] = `data:${root._b64CurrentMime};base64,${b64}`
@ -321,6 +320,7 @@ Singleton {
return return
} }
Quickshell.execDetached(["cliphist", "delete", id]) Quickshell.execDetached(["cliphist", "delete", id])
revision++
Qt.callLater(() => list()) Qt.callLater(() => list())
} }
@ -330,9 +330,7 @@ Singleton {
} }
Quickshell.execDetached(["cliphist", "wipe"]) Quickshell.execDetached(["cliphist", "wipe"])
revision++ revision++
Qt.callLater(() => list()) Qt.callLater(() => list())
} }
} }

View file

@ -7,7 +7,7 @@ import qs.Commons
import qs.Services import qs.Services
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
QtObject { Singleton {
id: root id: root
// Notification server instance // Notification server instance