From 5d94c0a7d44a2255b81815a52a7056a94a39842d Mon Sep 17 00:00:00 2001 From: nippy Date: Sat, 18 Apr 2026 13:49:56 +0200 Subject: update Raveos themes --- .../quickshell/Services/SettingsSearchService.qml | 240 +++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml') diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml new file mode 100644 index 0000000..97e5d11 --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/SettingsSearchService.qml @@ -0,0 +1,240 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Common + +Singleton { + id: root + + property string query: "" + property var results: [] + property string targetSection: "" + property string highlightSection: "" + property var registeredCards: ({}) + property var settingsIndex: [] + property bool indexLoaded: false + property var _translatedCache: [] + + readonly property var conditionMap: ({ + "isNiri": () => CompositorService.isNiri, + "isHyprland": () => CompositorService.isHyprland, + "isDwl": () => CompositorService.isDwl, + "keybindsAvailable": () => KeybindsService.available, + "soundsAvailable": () => AudioService.soundsAvailable, + "cupsAvailable": () => CupsService.cupsAvailable, + "networkNotLegacy": () => !NetworkService.usingLegacy, + "dmsConnected": () => DMSService.isConnected && DMSService.apiVersion >= 23, + "matugenAvailable": () => Theme.matugenAvailable + }) + + Component.onCompleted: indexFile.reload() + + FileView { + id: indexFile + path: Qt.resolvedUrl("../translations/settings_search_index.json") + onLoaded: { + try { + root.settingsIndex = JSON.parse(text()); + root.indexLoaded = true; + root._rebuildTranslationCache(); + } catch (e) { + console.warn("SettingsSearchService: Failed to parse index:", e); + root.settingsIndex = []; + root._translatedCache = []; + } + } + onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error) + } + + function registerCard(settingKey, item, flickable) { + if (!settingKey) + return; + registeredCards[settingKey] = { + item: item, + flickable: flickable + }; + if (targetSection === settingKey) + scrollTimer.restart(); + } + + function unregisterCard(settingKey) { + if (!settingKey) + return; + let cards = registeredCards; + delete cards[settingKey]; + registeredCards = cards; + } + + function navigateToSection(section) { + targetSection = section; + if (registeredCards[section]) + scrollTimer.restart(); + } + + function scrollToTarget() { + if (!targetSection) + return; + const entry = registeredCards[targetSection]; + if (!entry || !entry.item || !entry.flickable) + return; + const flickable = entry.flickable; + const item = entry.item; + const contentItem = flickable.contentItem; + + if (!contentItem) + return; + const mapped = item.mapToItem(contentItem, 0, 0); + const maxY = Math.max(0, flickable.contentHeight - flickable.height); + const targetY = Math.min(maxY, Math.max(0, mapped.y - 16)); + flickable.contentY = targetY; + + highlightSection = targetSection; + targetSection = ""; + highlightTimer.restart(); + } + + function clearHighlight() { + highlightSection = ""; + } + + Timer { + id: scrollTimer + interval: 50 + onTriggered: root.scrollToTarget() + } + + Timer { + id: highlightTimer + interval: 2500 + onTriggered: root.highlightSection = "" + } + + function checkCondition(item) { + if (!item.conditionKey) + return true; + const condFn = conditionMap[item.conditionKey]; + if (!condFn) + return true; + return condFn(); + } + + function translateItem(item) { + return { + section: item.section, + label: I18n.tr(item.label), + tabIndex: item.tabIndex, + category: I18n.tr(item.category), + keywords: item.keywords || [], + icon: item.icon || "settings", + description: item.description ? I18n.tr(item.description) : "", + conditionKey: item.conditionKey + }; + } + + function _rebuildTranslationCache() { + var cache = []; + for (var i = 0; i < settingsIndex.length; i++) { + var item = settingsIndex[i]; + var t = translateItem(item); + cache.push({ + section: t.section, + label: t.label, + tabIndex: t.tabIndex, + category: t.category, + keywords: t.keywords, + icon: t.icon, + description: t.description, + conditionKey: t.conditionKey, + labelLower: t.label.toLowerCase(), + categoryLower: t.category.toLowerCase() + }); + } + _translatedCache = cache; + } + + function search(text) { + query = text; + if (!text) { + results = []; + return; + } + + var queryLower = text.toLowerCase().trim(); + var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0); + var scored = []; + var cache = _translatedCache; + + for (var i = 0; i < cache.length; i++) { + var entry = cache[i]; + if (!checkCondition(entry)) + continue; + + var labelLower = entry.labelLower; + var categoryLower = entry.categoryLower; + var score = 0; + + if (labelLower === queryLower) { + score = 10000; + } else if (labelLower.startsWith(queryLower)) { + score = 5000; + } else if (labelLower.includes(queryLower)) { + score = 1000; + } else if (categoryLower.includes(queryLower)) { + score = 500; + } + + if (score === 0) { + var keywords = entry.keywords; + for (var k = 0; k < keywords.length; k++) { + if (keywords[k].startsWith(queryLower)) { + score = 800; + break; + } + if (keywords[k].includes(queryLower) && score < 400) { + score = 400; + } + } + } + + if (score === 0 && queryWords.length > 1) { + var allMatch = true; + for (var w = 0; w < queryWords.length; w++) { + var word = queryWords[w]; + if (labelLower.includes(word)) + continue; + var inKeywords = false; + for (var k = 0; k < entry.keywords.length; k++) { + if (entry.keywords[k].includes(word)) { + inKeywords = true; + break; + } + } + if (!inKeywords && !categoryLower.includes(word)) { + allMatch = false; + break; + } + } + if (allMatch) + score = 300; + } + + if (score > 0) { + scored.push({ + item: entry, + score: score + }); + } + } + + scored.sort((a, b) => b.score - a.score); + results = scored.slice(0, 15).map(s => s.item); + } + + function clear() { + query = ""; + results = []; + } +} -- cgit v1.3