1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

launcher: optimize bindings and filters

This commit is contained in:
bbedward
2026-01-04 11:49:05 -05:00
parent 2e1bed5fb5
commit 151d695212
9 changed files with 250 additions and 279 deletions

View File

@@ -52,6 +52,7 @@ DankPopout {
onOpened: {
searchMode = "apps";
appLauncher.ensureInitialized();
appLauncher.searchQuery = "";
appLauncher.selectedIndex = 0;
appLauncher.setCategory(I18n.tr("All"));
@@ -344,7 +345,7 @@ DankPopout {
width: parent.width - Theme.spacingS * 2
height: 40
anchors.horizontalCenter: parent.horizontalCenter
visible: searchField.text.length === 0 && appDrawerPopout.searchMode === "apps"
visible: appDrawerPopout.searchMode === "apps"
Rectangle {
width: 180
@@ -404,7 +405,7 @@ DankPopout {
height: {
let usedHeight = 40 + Theme.spacingS;
usedHeight += 52 + Theme.spacingS;
usedHeight += (searchField.text.length === 0 && appDrawerPopout.searchMode === "apps" ? 40 : 0);
usedHeight += appDrawerPopout.searchMode === "apps" ? 40 : 0;
return parent.height - usedHeight;
}
radius: Theme.cornerRadius

View File

@@ -1,9 +1,7 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
@@ -23,15 +21,15 @@ Item {
property bool keyboardNavigationActive: false
property bool suppressUpdatesWhileLaunching: false
property var categories: {
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
const result = [I18n.tr("All")]
return result.concat(allCategories.filter(cat => cat !== I18n.tr("All")))
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science");
const result = [I18n.tr("All")];
return result.concat(allCategories.filter(cat => cat !== I18n.tr("All")));
}
readonly property var categoryIcons: categories.map(category => AppSearchService.getCategoryIcon(category))
property var appUsageRanking: AppUsageHistoryData.appUsageRanking || {}
property alias model: filteredModel
property var _watchApplications: AppSearchService.applications
property var _uniqueApps: []
property bool _initialized: false
property bool _isTriggered: false
property string _triggeredCategory: ""
property bool _updatingFromTrigger: false
@@ -40,98 +38,109 @@ Item {
signal categorySelected(string category)
signal viewModeSelected(string mode)
function ensureInitialized() {
if (_initialized)
return;
_initialized = true;
updateFilteredModel();
}
function updateCategories() {
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
const result = [I18n.tr("All")]
categories = result.concat(allCategories.filter(cat => cat !== I18n.tr("All")))
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science");
const result = [I18n.tr("All")];
categories = result.concat(allCategories.filter(cat => cat !== I18n.tr("All")));
}
Connections {
target: PluginService
function onPluginLoaded() { updateCategories() }
function onPluginUnloaded() { updateCategories() }
function onPluginListUpdated() { updateCategories() }
function onPluginLoaded() {
updateCategories();
}
function onPluginUnloaded() {
updateCategories();
}
function onPluginListUpdated() {
updateCategories();
}
function onRequestLauncherUpdate(pluginId) {
// Only update if we are actually looking at this plugin or in All category
updateFilteredModel()
updateFilteredModel();
}
}
Connections {
target: SettingsData
function onSortAppsAlphabeticallyChanged() {
updateFilteredModel()
updateFilteredModel();
}
}
function updateFilteredModel() {
if (suppressUpdatesWhileLaunching) {
suppressUpdatesWhileLaunching = false
return
suppressUpdatesWhileLaunching = false;
return;
}
filteredModel.clear()
selectedIndex = 0
keyboardNavigationActive = false
filteredModel.clear();
selectedIndex = 0;
keyboardNavigationActive = false;
const triggerResult = checkPluginTriggers(searchQuery)
const triggerResult = checkPluginTriggers(searchQuery);
if (triggerResult.triggered) {
console.log("AppLauncher: Plugin trigger detected:", triggerResult.trigger, "for plugin:", triggerResult.pluginId)
console.log("AppLauncher: Plugin trigger detected:", triggerResult.trigger, "for plugin:", triggerResult.pluginId);
}
let apps = []
const allCategory = I18n.tr("All")
const emptyTriggerPlugins = typeof PluginService !== "undefined" ? PluginService.getPluginsWithEmptyTrigger() : []
let apps = [];
const allCategory = I18n.tr("All");
const emptyTriggerPlugins = typeof PluginService !== "undefined" ? PluginService.getPluginsWithEmptyTrigger() : [];
if (triggerResult.triggered) {
_isTriggered = true
_triggeredCategory = triggerResult.pluginCategory
_updatingFromTrigger = true
selectedCategory = triggerResult.pluginCategory
_updatingFromTrigger = false
apps = AppSearchService.getPluginItems(triggerResult.pluginCategory, triggerResult.query)
_isTriggered = true;
_triggeredCategory = triggerResult.pluginCategory;
_updatingFromTrigger = true;
selectedCategory = triggerResult.pluginCategory;
_updatingFromTrigger = false;
apps = AppSearchService.getPluginItems(triggerResult.pluginCategory, triggerResult.query);
} else {
if (_isTriggered) {
_updatingFromTrigger = true
selectedCategory = allCategory
_updatingFromTrigger = false
_isTriggered = false
_triggeredCategory = ""
_updatingFromTrigger = true;
selectedCategory = allCategory;
_updatingFromTrigger = false;
_isTriggered = false;
_triggeredCategory = "";
}
if (searchQuery.length === 0) {
if (selectedCategory === allCategory) {
let emptyTriggerItems = []
let emptyTriggerItems = [];
emptyTriggerPlugins.forEach(pluginId => {
const plugin = PluginService.getLauncherPlugin(pluginId)
const pluginCategory = plugin.name || pluginId
const items = AppSearchService.getPluginItems(pluginCategory, "")
emptyTriggerItems = emptyTriggerItems.concat(items)
})
apps = AppSearchService.applications.concat(emptyTriggerItems)
const plugin = PluginService.getLauncherPlugin(pluginId);
const pluginCategory = plugin.name || pluginId;
const items = AppSearchService.getPluginItems(pluginCategory, "");
emptyTriggerItems = emptyTriggerItems.concat(items);
});
apps = AppSearchService.applications.concat(emptyTriggerItems);
} else {
apps = AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults)
apps = AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults);
}
} else {
if (selectedCategory === allCategory) {
apps = AppSearchService.searchApplications(searchQuery)
apps = AppSearchService.searchApplications(searchQuery);
let emptyTriggerItems = []
let emptyTriggerItems = [];
emptyTriggerPlugins.forEach(pluginId => {
const plugin = PluginService.getLauncherPlugin(pluginId)
const pluginCategory = plugin.name || pluginId
const items = AppSearchService.getPluginItems(pluginCategory, searchQuery)
emptyTriggerItems = emptyTriggerItems.concat(items)
})
apps = apps.concat(emptyTriggerItems)
const plugin = PluginService.getLauncherPlugin(pluginId);
const pluginCategory = plugin.name || pluginId;
const items = AppSearchService.getPluginItems(pluginCategory, searchQuery);
emptyTriggerItems = emptyTriggerItems.concat(items);
});
apps = apps.concat(emptyTriggerItems);
} else {
const categoryApps = AppSearchService.getAppsInCategory(selectedCategory)
const categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
if (categoryApps.length > 0) {
const allSearchResults = AppSearchService.searchApplications(searchQuery)
const categoryNames = new Set(categoryApps.map(app => app.name))
apps = allSearchResults.filter(searchApp => categoryNames.has(searchApp.name)).slice(0, maxResults)
const allSearchResults = AppSearchService.searchApplications(searchQuery);
const categoryNames = new Set(categoryApps.map(app => app.name));
apps = allSearchResults.filter(searchApp => categoryNames.has(searchApp.name)).slice(0, maxResults);
} else {
apps = []
apps = [];
}
}
}
@@ -140,138 +149,150 @@ Item {
if (searchQuery.length === 0) {
if (SettingsData.sortAppsAlphabetically) {
apps = apps.sort((a, b) => {
return (a.name || "").localeCompare(b.name || "")
})
return (a.name || "").localeCompare(b.name || "");
});
} else {
apps = apps.sort((a, b) => {
const aId = a.id || a.execString || a.exec || ""
const bId = b.id || b.execString || b.exec || ""
const aUsage = appUsageRanking[aId] ? appUsageRanking[aId].usageCount : 0
const bUsage = appUsageRanking[bId] ? appUsageRanking[bId].usageCount : 0
if (aUsage !== bUsage) {
return bUsage - aUsage
}
return (a.name || "").localeCompare(b.name || "")
})
const aId = a.id || a.execString || a.exec || "";
const bId = b.id || b.execString || b.exec || "";
const aUsage = appUsageRanking[aId] ? appUsageRanking[aId].usageCount : 0;
const bUsage = appUsageRanking[bId] ? appUsageRanking[bId].usageCount : 0;
if (aUsage !== bUsage) {
return bUsage - aUsage;
}
return (a.name || "").localeCompare(b.name || "");
});
}
}
const seenNames = new Set()
const uniqueApps = []
const seenNames = new Set();
const uniqueApps = [];
apps.forEach(app => {
if (app) {
const itemKey = app.name + "|" + (app.execString || app.exec || app.action || "")
if (seenNames.has(itemKey)) {
return
}
seenNames.add(itemKey)
uniqueApps.push(app)
if (app) {
const itemKey = app.name + "|" + (app.execString || app.exec || app.action || "");
if (seenNames.has(itemKey)) {
return;
}
seenNames.add(itemKey);
uniqueApps.push(app);
const isPluginItem = app.action !== undefined
filteredModel.append({
"name": app.name || "",
"exec": app.execString || app.exec || app.action || "",
"icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"),
"comment": app.comment || "",
"categories": app.categories || [],
"isPlugin": isPluginItem,
"appIndex": uniqueApps.length - 1
})
}
})
const isPluginItem = app.action !== undefined;
filteredModel.append({
"name": app.name || "",
"exec": app.execString || app.exec || app.action || "",
"icon": app.icon !== undefined ? app.icon : (isPluginItem ? "" : "application-x-executable"),
"comment": app.comment || "",
"categories": app.categories || [],
"isPlugin": isPluginItem,
"appIndex": uniqueApps.length - 1
});
}
});
root._uniqueApps = uniqueApps
root._uniqueApps = uniqueApps;
}
function selectNext() {
if (filteredModel.count === 0) {
return
return;
}
keyboardNavigationActive = true
selectedIndex = viewMode === "grid" ? Math.min(selectedIndex + gridColumns, filteredModel.count - 1) : Math.min(selectedIndex + 1, filteredModel.count - 1)
keyboardNavigationActive = true;
selectedIndex = viewMode === "grid" ? Math.min(selectedIndex + gridColumns, filteredModel.count - 1) : Math.min(selectedIndex + 1, filteredModel.count - 1);
}
function selectPrevious() {
if (filteredModel.count === 0) {
return
return;
}
keyboardNavigationActive = true
selectedIndex = viewMode === "grid" ? Math.max(selectedIndex - gridColumns, 0) : Math.max(selectedIndex - 1, 0)
keyboardNavigationActive = true;
selectedIndex = viewMode === "grid" ? Math.max(selectedIndex - gridColumns, 0) : Math.max(selectedIndex - 1, 0);
}
function selectNextInRow() {
if (filteredModel.count === 0 || viewMode !== "grid") {
return
return;
}
keyboardNavigationActive = true
selectedIndex = Math.min(selectedIndex + 1, filteredModel.count - 1)
keyboardNavigationActive = true;
selectedIndex = Math.min(selectedIndex + 1, filteredModel.count - 1);
}
function selectPreviousInRow() {
if (filteredModel.count === 0 || viewMode !== "grid") {
return
return;
}
keyboardNavigationActive = true
selectedIndex = Math.max(selectedIndex - 1, 0)
keyboardNavigationActive = true;
selectedIndex = Math.max(selectedIndex - 1, 0);
}
function launchSelected() {
if (filteredModel.count === 0 || selectedIndex < 0 || selectedIndex >= filteredModel.count) {
return
return;
}
const selectedApp = filteredModel.get(selectedIndex)
launchApp(selectedApp)
const selectedApp = filteredModel.get(selectedIndex);
launchApp(selectedApp);
}
function launchApp(appData) {
if (!appData || typeof appData.appIndex === "undefined" || appData.appIndex < 0 || appData.appIndex >= _uniqueApps.length) {
return
return;
}
suppressUpdatesWhileLaunching = true
suppressUpdatesWhileLaunching = true;
const actualApp = _uniqueApps[appData.appIndex]
const actualApp = _uniqueApps[appData.appIndex];
if (appData.isPlugin) {
const pluginId = getPluginIdForItem(actualApp)
const pluginId = getPluginIdForItem(actualApp);
if (pluginId) {
AppSearchService.executePluginItem(actualApp, pluginId)
appLaunched(appData)
return
AppSearchService.executePluginItem(actualApp, pluginId);
appLaunched(appData);
return;
}
} else {
SessionService.launchDesktopEntry(actualApp)
appLaunched(appData)
AppUsageHistoryData.addAppUsage(actualApp)
SessionService.launchDesktopEntry(actualApp);
appLaunched(appData);
AppUsageHistoryData.addAppUsage(actualApp);
}
}
function setCategory(category) {
selectedCategory = category
categorySelected(category)
selectedCategory = category;
categorySelected(category);
}
function setViewMode(mode) {
viewMode = mode
viewModeSelected(mode)
viewMode = mode;
viewModeSelected(mode);
}
onSearchQueryChanged: {
if (!_initialized)
return;
if (debounceSearch) {
searchDebounceTimer.restart()
searchDebounceTimer.restart();
} else {
updateFilteredModel()
updateFilteredModel();
}
}
onSelectedCategoryChanged: {
if (_updatingFromTrigger) {
return
}
updateFilteredModel()
if (_updatingFromTrigger || !_initialized)
return;
updateFilteredModel();
}
onAppUsageRankingChanged: updateFilteredModel()
on_WatchApplicationsChanged: updateFilteredModel()
Component.onCompleted: {
updateFilteredModel()
onAppUsageRankingChanged: {
if (_initialized)
updateFilteredModel();
}
Connections {
target: DesktopEntries
function onApplicationsChanged() {
if (!root._initialized)
return;
root.updateCategories();
root.updateFilteredModel();
}
}
ListModel {
@@ -289,59 +310,67 @@ Item {
// Plugin trigger system functions
function checkPluginTriggers(query) {
if (!query || typeof PluginService === "undefined") {
return { triggered: false, pluginCategory: "", query: "" }
return {
triggered: false,
pluginCategory: "",
query: ""
};
}
const triggers = PluginService.getAllPluginTriggers()
const triggers = PluginService.getAllPluginTriggers();
for (const trigger in triggers) {
if (query.startsWith(trigger)) {
const pluginId = triggers[trigger]
const plugin = PluginService.getLauncherPlugin(pluginId)
const pluginId = triggers[trigger];
const plugin = PluginService.getLauncherPlugin(pluginId);
if (plugin) {
const remainingQuery = query.substring(trigger.length).trim()
const remainingQuery = query.substring(trigger.length).trim();
const result = {
triggered: true,
pluginId: pluginId,
pluginCategory: plugin.name || pluginId,
query: remainingQuery,
trigger: trigger
}
return result
};
return result;
}
}
}
return { triggered: false, pluginCategory: "", query: "" }
return {
triggered: false,
pluginCategory: "",
query: ""
};
}
function getPluginIdForItem(item) {
if (!item || !item.categories || typeof PluginService === "undefined") {
return null
return null;
}
const launchers = PluginService.getLauncherPlugins()
const launchers = PluginService.getLauncherPlugins();
for (const pluginId in launchers) {
const plugin = launchers[pluginId]
const pluginCategory = plugin.name || pluginId
const plugin = launchers[pluginId];
const pluginCategory = plugin.name || pluginId;
let hasCategory = false
let hasCategory = false;
if (Array.isArray(item.categories)) {
hasCategory = item.categories.includes(pluginCategory)
hasCategory = item.categories.includes(pluginCategory);
} else if (item.categories && typeof item.categories.count !== "undefined") {
for (let i = 0; i < item.categories.count; i++) {
if (item.categories.get(i) === pluginCategory) {
hasCategory = true
break
hasCategory = true;
break;
}
}
}
if (hasCategory) {
return pluginId
return pluginId;
}
}
return null
return null;
}
}