1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-03 20:32:07 -04:00

launcher v2: general performance improvements

This commit is contained in:
bbedward
2026-02-05 13:22:49 -05:00
parent 4349d68f87
commit a3ae95df09
3 changed files with 182 additions and 104 deletions

View File

@@ -257,13 +257,22 @@ Item {
}
property int _searchVersion: 0
property bool _pluginPhasePending: false
property bool _pluginPhaseForceFirst: false
property var _phase1Items: []
Timer {
id: searchDebounce
interval: searchMode === "all" && searchQuery.length > 0 ? 90 : 60
interval: 60
onTriggered: root.performSearch()
}
Timer {
id: pluginPhaseTimer
interval: 1
onTriggered: root._performPluginPhase()
}
Timer {
id: fileSearchDebounce
interval: 200
@@ -277,6 +286,9 @@ Item {
function setSearchQuery(query) {
_searchVersion++;
_queryDrivenSearch = true;
_pluginPhasePending = false;
_phase1Items = [];
pluginPhaseTimer.stop();
searchQuery = query;
searchDebounce.restart();
@@ -337,6 +349,10 @@ Item {
collapsedSections = {};
_clearModeCache();
_queryDrivenSearch = false;
_pluginPhasePending = false;
_pluginPhaseForceFirst = false;
_phase1Items = [];
pluginPhaseTimer.stop();
}
function loadPluginCategories(pluginId) {
@@ -418,7 +434,7 @@ Item {
sections = modeCache.sections;
flatModel = modeCache.flatModel;
} else {
sections = cachedSections.map(function (s) {
var newSections = cachedSections.map(function (s) {
var copy = Object.assign({}, s, {
items: s.items ? s.items.slice() : []
});
@@ -426,7 +442,8 @@ Item {
copy.collapsed = collapsedSections[s.id];
return copy;
});
flatModel = Scorer.flattenSections(sections);
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
_setCachedModeData("all", sections, flatModel);
}
selectedFlatIndex = restoreSelection(flatModel);
@@ -449,7 +466,8 @@ Item {
loadPluginCategories(triggerMatch.pluginId);
var pluginItems = getPluginItems(triggerMatch.pluginId, triggerMatch.query);
allItems = allItems.concat(pluginItems);
for (var k = 0; k < pluginItems.length; k++)
allItems.push(pluginItems[k]);
if (triggerMatch.isBuiltIn) {
var builtInItems = AppSearchService.getBuiltInLauncherItems(triggerMatch.pluginId, triggerMatch.query);
@@ -461,17 +479,18 @@ Item {
var dynamicDefs = buildDynamicSectionDefs(allItems);
var scoredItems = Scorer.scoreItems(allItems, triggerMatch.query, getFrecencyForItem);
var sortAlpha = !triggerMatch.query && SettingsData.sortAppsAlphabetically;
sections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
for (var sid in collapsedSections) {
for (var i = 0; i < sections.length; i++) {
if (sections[i].id === sid) {
sections[i].collapsed = collapsedSections[sid];
for (var i = 0; i < newSections.length; i++) {
if (newSections[i].id === sid) {
newSections[i].collapsed = collapsedSections[sid];
}
}
}
flatModel = Scorer.flattenSections(sections);
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem();
@@ -507,7 +526,7 @@ Item {
flatModel = modeCache.flatModel;
} else {
var appSectionIds = ["favorites", "apps"];
sections = cachedSections.filter(function (s) {
var newSections = cachedSections.filter(function (s) {
return appSectionIds.indexOf(s.id) !== -1;
}).map(function (s) {
var copy = Object.assign({}, s, {
@@ -517,7 +536,8 @@ Item {
copy.collapsed = collapsedSections[s.id];
return copy;
});
flatModel = Scorer.flattenSections(sections);
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
_setCachedModeData("apps", sections, flatModel);
}
selectedFlatIndex = restoreSelection(flatModel);
@@ -534,17 +554,18 @@ Item {
var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically;
sections = Scorer.groupBySection(scoredItems, sectionDefinitions, sortAlpha, searchQuery ? 50 : 500);
var newSections = Scorer.groupBySection(scoredItems, sectionDefinitions, sortAlpha, searchQuery ? 50 : 500);
for (var sid in collapsedSections) {
for (var i = 0; i < sections.length; i++) {
if (sections[i].id === sid) {
sections[i].collapsed = collapsedSections[sid];
for (var i = 0; i < newSections.length; i++) {
if (newSections[i].id === sid) {
newSections[i].collapsed = collapsedSections[sid];
}
}
}
flatModel = Scorer.flattenSections(sections);
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem();
@@ -556,13 +577,15 @@ Item {
if (searchMode === "plugins") {
if (!searchQuery && !pluginFilter) {
var browseItems = getPluginBrowseItems();
allItems = allItems.concat(browseItems);
for (var k = 0; k < browseItems.length; k++)
allItems.push(browseItems[k]);
} else if (pluginFilter) {
var isBuiltInFilter = !!AppSearchService.builtInPlugins[pluginFilter];
applyActivePluginViewPreference(pluginFilter, isBuiltInFilter);
var filterItems = getPluginItems(pluginFilter, searchQuery);
allItems = allItems.concat(filterItems);
for (var k = 0; k < filterItems.length; k++)
allItems.push(filterItems[k]);
var builtInItems = AppSearchService.getBuiltInLauncherItems(pluginFilter, searchQuery);
for (var j = 0; j < builtInItems.length; j++) {
@@ -573,7 +596,8 @@ Item {
for (var i = 0; i < emptyTriggerPlugins.length; i++) {
var pluginId = emptyTriggerPlugins[i];
var pItems = getPluginItems(pluginId, searchQuery);
allItems = allItems.concat(pItems);
for (var k = 0; k < pItems.length; k++)
allItems.push(pItems[k]);
}
var builtInLauncherPlugins = getBuiltInEmptyTriggerLaunchers();
@@ -589,17 +613,18 @@ Item {
var dynamicDefs = buildDynamicSectionDefs(allItems);
var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically;
sections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
for (var sid in collapsedSections) {
for (var i = 0; i < sections.length; i++) {
if (sections[i].id === sid) {
sections[i].collapsed = collapsedSections[sid];
for (var i = 0; i < newSections.length; i++) {
if (newSections[i].id === sid) {
newSections[i].collapsed = collapsedSections[sid];
}
}
}
flatModel = Scorer.flattenSections(sections);
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem();
@@ -610,31 +635,26 @@ Item {
var calculatorResult = evaluateCalculator(searchQuery);
if (calculatorResult) {
calculatorResult._preScored = 12000;
allItems.push(calculatorResult);
}
var apps = searchApps(searchQuery);
allItems = allItems.concat(apps);
for (var i = 0; i < apps.length; i++) {
if (searchQuery)
apps[i]._preScored = 11000 - i;
allItems.push(apps[i]);
}
if (searchMode === "all") {
var includePlugins = !searchQuery || searchQuery.length >= 2;
if (searchQuery && includePlugins) {
var allPluginsOrdered = getAllVisiblePluginsOrdered();
var maxPerPlugin = 10;
for (var i = 0; i < allPluginsOrdered.length; i++) {
var plugin = allPluginsOrdered[i];
if (plugin.isBuiltIn) {
var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
var blLimit = Math.min(blItems.length, maxPerPlugin);
for (var j = 0; j < blLimit; j++)
allItems.push(transformBuiltInLauncherItem(blItems[j], plugin.id));
} else {
var pItems = getPluginItems(plugin.id, searchQuery);
if (pItems.length > maxPerPlugin)
pItems = pItems.slice(0, maxPerPlugin);
allItems = allItems.concat(pItems);
}
}
if (searchQuery && searchQuery.length >= 2) {
_pluginPhasePending = true;
_pluginPhaseForceFirst = shouldResetSelection;
_phase1Items = allItems;
pluginPhaseTimer.restart();
isSearching = true;
searchCompleted();
return;
} else if (!searchQuery) {
var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered();
for (var i = 0; i < emptyTriggerOrdered.length; i++) {
@@ -645,12 +665,14 @@ Item {
allItems.push(transformBuiltInLauncherItem(blItems[j], plugin.id));
} else {
var pItems = getPluginItems(plugin.id, searchQuery);
allItems = allItems.concat(pItems);
for (var j = 0; j < pItems.length; j++)
allItems.push(pItems[j]);
}
}
var browseItems = getPluginBrowseItems();
allItems = allItems.concat(browseItems);
for (var i = 0; i < browseItems.length; i++)
allItems.push(browseItems[i]);
}
}
@@ -677,8 +699,8 @@ Item {
}
}
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
flatModel = Scorer.flattenSections(sections);
if (!AppSearchService.isCacheValid() && !searchQuery && searchMode === "all" && !pluginFilter) {
AppSearchService.setCachedDefaultSections(sections, flatModel);
@@ -687,6 +709,63 @@ Item {
selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem();
isSearching = _pluginPhasePending;
searchCompleted();
}
function _performPluginPhase() {
_pluginPhasePending = false;
if (!searchQuery || searchQuery.length < 2 || searchMode !== "all")
return;
var currentVersion = _searchVersion;
var restoreSelection = preserveSelectionAfterUpdate(_pluginPhaseForceFirst);
var allItems = _phase1Items;
_phase1Items = [];
var allPluginsOrdered = getAllVisiblePluginsOrdered();
var maxPerPlugin = 10;
for (var i = 0; i < allPluginsOrdered.length; i++) {
if (currentVersion !== _searchVersion)
return;
var plugin = allPluginsOrdered[i];
if (plugin.isBuiltIn) {
var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
var blLimit = Math.min(blItems.length, maxPerPlugin);
for (var j = 0; j < blLimit; j++) {
var item = transformBuiltInLauncherItem(blItems[j], plugin.id);
item._preScored = 900 - j;
allItems.push(item);
}
} else {
var pItems = getPluginItems(plugin.id, searchQuery, maxPerPlugin);
for (var j = 0; j < pItems.length; j++) {
pItems[j]._preScored = 900 - j;
allItems.push(pItems[j]);
}
}
}
if (currentVersion !== _searchVersion)
return;
var dynamicDefs = buildDynamicSectionDefs(allItems);
var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, false, 50);
if (currentVersion !== _searchVersion)
return;
for (var i = 0; i < newSections.length; i++) {
var sid = newSections[i].id;
if (collapsedSections[sid] !== undefined)
newSections[i].collapsed = collapsedSections[sid];
}
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem();
isSearching = false;
searchCompleted();
}
@@ -756,9 +835,8 @@ Item {
newSections.sort(function (a, b) {
return a.priority - b.priority;
});
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
flatModel = Scorer.flattenSections(sections);
if (selectedFlatIndex >= flatModel.length) {
selectedFlatIndex = getFirstItemIndex();
}
@@ -954,11 +1032,12 @@ Item {
return sortPluginIdsByOrder(visible);
}
function getPluginItems(pluginId, query) {
function getPluginItems(pluginId, query, limit) {
var items = AppSearchService.getPluginItemsForPlugin(pluginId, query);
var count = limit > 0 && limit < items.length ? limit : items.length;
var transformed = [];
for (var i = 0; i < items.length; i++) {
for (var i = 0; i < count; i++) {
transformed.push(transformPluginItem(items[i], pluginId));
}
@@ -1127,8 +1206,14 @@ Item {
return Nav.getGridColumns(getSectionViewMode(sectionId), gridColumns);
}
function _cancelPendingSelectionReset() {
_queryDrivenSearch = false;
_pluginPhaseForceFirst = false;
}
function selectNext() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculateNextIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1138,6 +1223,7 @@ Item {
function selectPrevious() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculatePrevIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1147,6 +1233,7 @@ Item {
function selectRight() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculateRightIndex(flatModel, selectedFlatIndex, getSectionViewMode);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1156,6 +1243,7 @@ Item {
function selectLeft() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculateLeftIndex(flatModel, selectedFlatIndex, getSectionViewMode);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1165,6 +1253,7 @@ Item {
function selectNextSection() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculateNextSectionIndex(flatModel, selectedFlatIndex);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1174,6 +1263,7 @@ Item {
function selectPreviousSection() {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculatePrevSectionIndex(flatModel, selectedFlatIndex);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1183,6 +1273,7 @@ Item {
function selectPageDown(visibleItems) {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculatePageDownIndex(flatModel, selectedFlatIndex, visibleItems);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1192,6 +1283,7 @@ Item {
function selectPageUp(visibleItems) {
keyboardNavigationActive = true;
_cancelPendingSelectionReset();
var newIndex = Nav.calculatePageUpIndex(flatModel, selectedFlatIndex, visibleItems);
if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex;
@@ -1232,10 +1324,9 @@ Item {
});
}
}
flatModel = Scorer.flattenSections(newSections);
sections = newSections;
flatModel = Scorer.flattenSections(sections);
if (selectedFlatIndex >= flatModel.length) {
selectedFlatIndex = getFirstItemIndex();
}

View File

@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
@@ -173,7 +174,10 @@ Item {
width: parent.width
Repeater {
model: root.controller?.sections ?? []
model: ScriptModel {
values: root.controller?.sections ?? []
objectProp: "id"
}
Column {
id: sectionDelegate
@@ -207,33 +211,23 @@ Item {
visible: !sectionDelegate.isGridMode && !sectionDelegate.isCollapsed
Repeater {
model: sectionDelegate.isGridMode || sectionDelegate.isCollapsed ? [] : (sectionDelegate.modelData?.items ?? [])
model: ScriptModel {
values: sectionDelegate.isGridMode || sectionDelegate.isCollapsed ? [] : (sectionDelegate.modelData?.items ?? [])
objectProp: "id"
}
ResultItem {
required property var modelData
required property int index
readonly property int computedFlatIndex: (sectionDelegate.modelData?.flatStartIndex ?? 0) + index
width: listContent.width
height: 52
item: modelData
isSelected: getFlatIndex() === root.controller?.selectedFlatIndex
isSelected: computedFlatIndex === root.controller?.selectedFlatIndex
controller: root.controller
flatIndex: getFlatIndex()
function getFlatIndex() {
if (!sectionDelegate?.sectionId)
return -1;
var flatIdx = 0;
var sections = root.controller?.sections ?? [];
for (var i = 0; i < sections.length; i++) {
flatIdx++;
if (sections[i].id === sectionDelegate.sectionId)
return flatIdx + index;
if (!sections[i].collapsed)
flatIdx += sections[i].items?.length ?? 0;
}
return -1;
}
flatIndex: computedFlatIndex
onClicked: {
if (root.controller) {
@@ -242,7 +236,7 @@ Item {
}
onRightClicked: (mouseX, mouseY) => {
root.itemRightClicked(getFlatIndex(), modelData, mouseX, mouseY);
root.itemRightClicked(computedFlatIndex, modelData, mouseX, mouseY);
}
}
}
@@ -258,7 +252,10 @@ Item {
readonly property real cellHeight: sectionDelegate.currentViewMode === "tile" ? cellWidth * 0.75 : cellWidth + 24
Repeater {
model: sectionDelegate.isGridMode && !sectionDelegate.isCollapsed ? (sectionDelegate.modelData?.items ?? []) : []
model: ScriptModel {
values: sectionDelegate.isGridMode && !sectionDelegate.isCollapsed ? (sectionDelegate.modelData?.items ?? []) : []
objectProp: "id"
}
Item {
id: gridDelegateItem
@@ -268,22 +265,7 @@ Item {
width: gridContent.cellWidth
height: gridContent.cellHeight
function getFlatIndex() {
if (!sectionDelegate?.sectionId)
return -1;
var flatIdx = 0;
var sections = root.controller?.sections ?? [];
for (var i = 0; i < sections.length; i++) {
flatIdx++;
if (sections[i].id === sectionDelegate.sectionId)
return flatIdx + index;
if (!sections[i].collapsed)
flatIdx += sections[i].items?.length ?? 0;
}
return -1;
}
readonly property int cachedFlatIndex: getFlatIndex()
readonly property int cachedFlatIndex: (sectionDelegate.modelData?.flatStartIndex ?? 0) + index
GridItem {
width: parent.width - 4

View File

@@ -42,26 +42,23 @@ function hasWordBoundaryMatch(text, query) {
function levenshteinDistance(s1, s2) {
var len1 = s1.length
var len2 = s2.length
var matrix = []
var prev = new Array(len2 + 1)
var curr = new Array(len2 + 1)
for (var i = 0; i <= len1; i++) {
matrix[i] = [i]
}
for (var j = 0; j <= len2; j++) {
matrix[0][j] = j
}
for (var j = 0; j <= len2; j++)
prev[j] = j
for (var i = 1; i <= len1; i++) {
curr[0] = i
for (var j = 1; j <= len2; j++) {
var cost = s1[i - 1] === s2[j - 1] ? 0 : 1
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
)
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost)
}
var tmp = prev
prev = curr
curr = tmp
}
return matrix[len1][len2]
return prev[len2]
}
function fuzzyScore(text, query) {
@@ -153,8 +150,14 @@ function scoreItems(items, query, getFrecencyFn) {
for (var i = 0; i < items.length; i++) {
var item = items[i]
var frecencyData = getFrecencyFn ? getFrecencyFn(item) : null
var itemScore = score(item, query, frecencyData)
var itemScore
if (item._preScored !== undefined) {
itemScore = item._preScored
} else {
var frecencyData = getFrecencyFn ? getFrecencyFn(item) : null
itemScore = score(item, query, frecencyData)
}
if (itemScore > 0 || !query || query.length === 0) {
scored.push({
@@ -228,6 +231,8 @@ function flattenSections(sections) {
sectionIndex: i
})
section.flatStartIndex = flat.length
if (!section.collapsed) {
for (var j = 0; j < section.items.length; j++) {
flat.push({