quickshell: pin shared ii base and phone overlay
This commit is contained in:
parent
cd2150542e
commit
b0a1304ba1
970 changed files with 88310 additions and 1 deletions
158
surfaces/quickshell/ii-base/services/FileSearch.qml
Normal file
158
surfaces/quickshell/ii-base/services/FileSearch.qml
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property var results: []
|
||||
property string pendingQuery: ""
|
||||
property string activeQuery: ""
|
||||
property int searchId: 0
|
||||
property bool running: false
|
||||
|
||||
function normalizePath(path) {
|
||||
if (!path) return "";
|
||||
let normalized = FileUtils.trimFileProtocol(String(path)).trim();
|
||||
if (normalized.startsWith("~/")) {
|
||||
normalized = FileUtils.trimFileProtocol(Directories.home) + normalized.slice(1);
|
||||
}
|
||||
if (normalized.endsWith("/")) {
|
||||
normalized = normalized.slice(0, -1);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function isExcluded(path) {
|
||||
if (Config.options.search.fileSearch.excludeHiddenDirs) {
|
||||
// Match any hidden dir segment: "/.name/" or ending "/.name"
|
||||
if (path.match(/\/\.[^/]+(\/|$)/)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const exclude = Config.options.search.fileSearch.exclude;
|
||||
if (!exclude || exclude.length === 0) return false;
|
||||
for (let i = 0; i < exclude.length; i++) {
|
||||
const needle = String(exclude[i]).trim();
|
||||
if (needle.length === 0) continue;
|
||||
if (path.includes(`/${needle}/`) || path.endsWith(`/${needle}`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isAllowedByPath(path) {
|
||||
const paths = Config.options.search.fileSearch.paths;
|
||||
if (!paths || paths.length === 0) return true;
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const base = normalizePath(paths[i]);
|
||||
if (!base) continue;
|
||||
if (path === base || path.startsWith(base + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function shouldSearch(query) {
|
||||
if (!Config.options.search.fileSearch.enable) return false;
|
||||
if (!query || query.trim().length < 2) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
results = [];
|
||||
activeQuery = "";
|
||||
if (plocateProc.running) {
|
||||
plocateProc.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
function search(query) {
|
||||
pendingQuery = String(query || "").trim();
|
||||
if (!shouldSearch(pendingQuery)) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
debounceTimer.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: debounceTimer
|
||||
interval: 200
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.runSearch(pendingQuery);
|
||||
}
|
||||
}
|
||||
|
||||
function runSearch(query) {
|
||||
const trimmed = String(query || "").trim();
|
||||
if (!shouldSearch(trimmed)) { reset(); return; }
|
||||
if (plocateProc.running) plocateProc.running = false;
|
||||
|
||||
searchId += 1;
|
||||
activeQuery = trimmed;
|
||||
results = [];
|
||||
|
||||
const maxResults = Config.options.search.fileSearch.maxResults;
|
||||
|
||||
// Tag each line with d: or f: prefix
|
||||
const command = [
|
||||
"bash", "-c",
|
||||
`plocate -i --basename --limit ${maxResults} '${trimmed}' | while IFS= read -r p; do [ -d "$p" ] && printf 'd:%s\n' "$p" || printf 'f:%s\n' "$p"; done`
|
||||
];
|
||||
|
||||
plocateProc.runId = searchId;
|
||||
plocateProc.accepted = 0;
|
||||
plocateProc.command = command;
|
||||
plocateProc.running = true;
|
||||
root.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: plocateProc
|
||||
property int runId: 0
|
||||
property int accepted: 0
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: line => {
|
||||
if (plocateProc.runId !== root.searchId) return;
|
||||
const raw = String(line || "").trim();
|
||||
if (!raw || raw.length < 3) return;
|
||||
|
||||
const isDir = raw.startsWith("d:");
|
||||
const rawPath = raw.slice(2);
|
||||
|
||||
const normalized = root.normalizePath(rawPath);
|
||||
if (!normalized) return;
|
||||
if (!root.isAllowedByPath(normalized)) return;
|
||||
if (root.isExcluded(normalized)) return;
|
||||
if (plocateProc.accepted >= Config.options.search.fileSearch.maxResults) {
|
||||
plocateProc.running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plocateProc.accepted += 1;
|
||||
root.results = root.results.concat([{ path: normalized, isDir: isDir }]);
|
||||
}
|
||||
}
|
||||
|
||||
stderr: SplitParser {
|
||||
onRead: line => {
|
||||
if (plocateProc.runId !== root.searchId) return;
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (plocateProc.runId !== root.searchId) return;
|
||||
root.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue