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

Compare commits

...

5 Commits

Author SHA1 Message Date
bbedward
816819bf9f dankinstall: fix xero color typo 2026-01-23 23:10:24 -05:00
bbedward
78f3bb3812 dankinstall: support XeroLinux
fixes #1474
2026-01-23 22:39:14 -05:00
bbedward
01d7ed5dd8 launcher v2: ability to toggle visibility in modal 2026-01-23 22:17:35 -05:00
Lucas
50311db280 nix: add qt-imageformats to DMS qml dependencies (#1479)
* nix: add qt-imageformats to DMS qml dependencies

* nix: update flake.lock
2026-01-23 21:53:35 -05:00
bbedward
01b1a276c5 launcher v2: support ScreenCopy in tiles 2026-01-23 21:29:48 -05:00
11 changed files with 849 additions and 649 deletions

View File

@@ -41,6 +41,9 @@ func init() {
Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan) return NewArchDistribution(config, logChan)
}) })
Register("XeroLinux", "#888fe2", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
} }
type ArchDistribution struct { type ArchDistribution struct {

6
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1766651565, "lastModified": 1769018530,
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=", "narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539", "rev": "88d3861acdd3d2f0e361767018218e51810df8a1",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -47,6 +47,7 @@
kirigami.unwrapped kirigami.unwrapped
sonnet sonnet
qtmultimedia qtmultimedia
qtimageformats
]; ];
in in
{ {

View File

@@ -33,7 +33,8 @@ Rectangle {
result.push(selectedItem.primaryAction); result.push(selectedItem.primaryAction);
} }
if (selectedItem?.type === "plugin") { switch (selectedItem?.type) {
case "plugin":
var pluginActions = getPluginContextMenuActions(); var pluginActions = getPluginContextMenuActions();
for (var i = 0; i < pluginActions.length; i++) { for (var i = 0; i < pluginActions.length; i++) {
var act = pluginActions[i]; var act = pluginActions[i];
@@ -44,7 +45,17 @@ Rectangle {
pluginAction: act.action pluginAction: act.action
}); });
} }
} else if (selectedItem?.type === "app" && !selectedItem?.isCore) { break;
case "plugin_browse":
if (selectedItem?.actions) {
for (var i = 0; i < selectedItem.actions.length; i++) {
result.push(selectedItem.actions[i]);
}
}
break;
case "app":
if (selectedItem?.isCore)
break;
if (selectedItem?.actions) { if (selectedItem?.actions) {
for (var i = 0; i < selectedItem.actions.length; i++) { for (var i = 0; i < selectedItem.actions.length; i++) {
result.push(selectedItem.actions[i]); result.push(selectedItem.actions[i]);
@@ -57,19 +68,23 @@ Rectangle {
action: "launch_dgpu" action: "launch_dgpu"
}); });
} }
break;
} }
return result; return result;
} }
readonly property bool hasActions: { readonly property bool hasActions: {
if (selectedItem?.type === "app" && !selectedItem?.isCore) switch (selectedItem?.type) {
return true; case "app":
if (selectedItem?.type === "plugin") { return !selectedItem?.isCore;
var pluginActions = getPluginContextMenuActions(); case "plugin":
return pluginActions.length > 0; return getPluginContextMenuActions().length > 0;
} case "plugin_browse":
return selectedItem?.actions?.length > 0;
default:
return actions.length > 1; return actions.length > 1;
} }
}
width: parent?.width ?? 200 width: parent?.width ?? 200
height: expanded && hasActions ? 52 : 0 height: expanded && hasActions ? 52 : 0

View File

@@ -6,6 +6,9 @@ import Quickshell.Io
import qs.Common import qs.Common
import qs.Services import qs.Services
import "Scorer.js" as Scorer import "Scorer.js" as Scorer
import "ControllerUtils.js" as Utils
import "NavigationHelpers.js" as Nav
import "ItemTransformers.js" as Transform
Item { Item {
id: root id: root
@@ -147,6 +150,10 @@ Item {
if (sectionDefinitions[i].id === sectionId) if (sectionDefinitions[i].id === sectionId)
return sectionDefinitions[i].defaultViewMode || "list"; return sectionDefinitions[i].defaultViewMode || "list";
} }
if (pluginViewPreferences[sectionId]?.mode)
return pluginViewPreferences[sectionId].mode;
return "list"; return "list";
} }
@@ -313,9 +320,23 @@ Item {
return false; return false;
} }
function preserveSelectionAfterUpdate() {
var previousSelectedId = selectedItem?.id || "";
return function (newFlatModel) {
if (!previousSelectedId)
return getFirstItemIndex();
for (var i = 0; i < newFlatModel.length; i++) {
if (!newFlatModel[i].isHeader && newFlatModel[i].item?.id === previousSelectedId)
return i;
}
return getFirstItemIndex();
};
}
function performSearch() { function performSearch() {
var currentVersion = _searchVersion; var currentVersion = _searchVersion;
isSearching = true; isSearching = true;
var restoreSelection = preserveSelectionAfterUpdate();
var cachedSections = AppSearchService.getCachedDefaultSections(); var cachedSections = AppSearchService.getCachedDefaultSections();
if (cachedSections && !searchQuery && searchMode === "all" && !pluginFilter) { if (cachedSections && !searchQuery && searchMode === "all" && !pluginFilter) {
@@ -331,7 +352,7 @@ Item {
return copy; return copy;
}); });
flatModel = Scorer.flattenSections(sections); flatModel = Scorer.flattenSections(sections);
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
searchCompleted(); searchCompleted();
@@ -370,7 +391,7 @@ Item {
} }
flatModel = Scorer.flattenSections(sections); flatModel = Scorer.flattenSections(sections);
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
@@ -409,7 +430,7 @@ Item {
return copy; return copy;
}); });
flatModel = Scorer.flattenSections(sections); flatModel = Scorer.flattenSections(sections);
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
searchCompleted(); searchCompleted();
@@ -434,7 +455,7 @@ Item {
} }
flatModel = Scorer.flattenSections(sections); flatModel = Scorer.flattenSections(sections);
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
@@ -489,7 +510,7 @@ Item {
} }
flatModel = Scorer.flattenSections(sections); flatModel = Scorer.flattenSections(sections);
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
@@ -573,7 +594,7 @@ Item {
AppSearchService.setCachedDefaultSections(sections, flatModel); AppSearchService.setCachedDefaultSections(sections, flatModel);
} }
selectedFlatIndex = getFirstItemIndex(); selectedFlatIndex = restoreSelection(flatModel);
updateSelectedItem(); updateSelectedItem();
isSearching = false; isSearching = false;
@@ -674,241 +695,26 @@ Item {
function transformApp(app) { function transformApp(app) {
var appId = app.id || app.execString || app.exec || ""; var appId = app.id || app.execString || app.exec || "";
var override = SessionData.getAppOverride(appId); var override = SessionData.getAppOverride(appId);
return Transform.transformApp(app, override, [], I18n.tr("Launch"));
var actions = [];
if (app.actions && app.actions.length > 0) {
for (var i = 0; i < app.actions.length; i++) {
actions.push({
name: app.actions[i].name,
icon: "play_arrow",
actionData: app.actions[i]
});
}
}
return {
id: appId,
type: "app",
name: override?.name || app.name || "",
subtitle: override?.comment || app.comment || "",
icon: override?.icon || app.icon || "application-x-executable",
iconType: "image",
section: "apps",
data: app,
keywords: app.keywords || [],
actions: actions,
primaryAction: {
name: I18n.tr("Launch"),
icon: "open_in_new",
action: "launch"
}
};
} }
function transformCoreApp(app) { function transformCoreApp(app) {
var iconName = "apps"; return Transform.transformCoreApp(app, I18n.tr("Open"));
var iconType = "material";
if (app.icon) {
if (app.icon.startsWith("svg+corner:")) {
iconType = "composite";
} else if (app.icon.startsWith("material:")) {
iconName = app.icon.substring(9);
} else {
iconName = app.icon;
iconType = "image";
}
}
return {
id: app.builtInPluginId || app.action || "",
type: "app",
name: app.name || "",
subtitle: app.comment || "",
icon: iconName,
iconType: iconType,
iconFull: app.icon,
section: "apps",
data: app,
isCore: true,
actions: [],
primaryAction: {
name: I18n.tr("Open"),
icon: "open_in_new",
action: "launch"
}
};
} }
function transformBuiltInLauncherItem(item, pluginId) { function transformBuiltInLauncherItem(item, pluginId) {
var rawIcon = item.icon || "extension"; return Transform.transformBuiltInLauncherItem(item, pluginId, I18n.tr("Open"));
var icon = stripIconPrefix(rawIcon);
var iconType = item.iconType;
if (!iconType) {
if (rawIcon.startsWith("material:"))
iconType = "material";
else if (rawIcon.startsWith("unicode:"))
iconType = "unicode";
else
iconType = "image";
}
return {
id: item.action || "",
type: "plugin",
name: item.name || "",
subtitle: item.comment || "",
icon: icon,
iconType: iconType,
section: "plugin_" + pluginId,
data: item,
pluginId: pluginId,
isBuiltInLauncher: true,
keywords: item.keywords || [],
actions: [],
primaryAction: {
name: I18n.tr("Open"),
icon: "open_in_new",
action: "execute"
}
};
} }
function transformFileResult(file) { function transformFileResult(file) {
var filename = file.path ? file.path.split("/").pop() : ""; return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"));
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
return {
id: file.path || "",
type: "file",
name: filename,
subtitle: dirname,
icon: getFileIcon(filename),
iconType: "material",
section: "files",
data: file,
actions: [
{
name: I18n.tr("Open folder"),
icon: "folder_open",
action: "open_folder"
},
{
name: I18n.tr("Copy path"),
icon: "content_copy",
action: "copy_path"
}
],
primaryAction: {
name: I18n.tr("Open"),
icon: "open_in_new",
action: "open"
}
};
}
function getFileIcon(filename) {
var ext = filename.lastIndexOf(".") > 0 ? filename.substring(filename.lastIndexOf(".") + 1).toLowerCase() : "";
var iconMap = {
"pdf": "picture_as_pdf",
"doc": "description",
"docx": "description",
"odt": "description",
"xls": "table_chart",
"xlsx": "table_chart",
"ods": "table_chart",
"ppt": "slideshow",
"pptx": "slideshow",
"odp": "slideshow",
"txt": "article",
"md": "article",
"rst": "article",
"jpg": "image",
"jpeg": "image",
"png": "image",
"gif": "image",
"svg": "image",
"webp": "image",
"mp3": "audio_file",
"wav": "audio_file",
"flac": "audio_file",
"ogg": "audio_file",
"mp4": "video_file",
"mkv": "video_file",
"avi": "video_file",
"webm": "video_file",
"zip": "folder_zip",
"tar": "folder_zip",
"gz": "folder_zip",
"7z": "folder_zip",
"rar": "folder_zip",
"js": "code",
"ts": "code",
"py": "code",
"rs": "code",
"go": "code",
"java": "code",
"c": "code",
"cpp": "code",
"h": "code",
"html": "web",
"css": "web",
"htm": "web",
"json": "data_object",
"xml": "data_object",
"yaml": "data_object",
"yml": "data_object",
"sh": "terminal",
"bash": "terminal",
"zsh": "terminal"
};
return iconMap[ext] || "insert_drive_file";
} }
function evaluateCalculator(query) { function evaluateCalculator(query) {
if (!query || query.length === 0) var calc = Utils.evaluateCalculator(query);
return null; if (!calc)
var mathExpr = query.replace(/[^0-9+\-*/().%\s^]/g, "");
if (mathExpr.length < 2)
return null;
var hasMath = /[+\-*/^%]/.test(query) && /\d/.test(query);
if (!hasMath)
return null;
try {
var sanitized = mathExpr.replace(/\^/g, "**");
var result = Function('"use strict"; return (' + sanitized + ')')();
if (typeof result === "number" && isFinite(result)) {
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, "");
return {
id: "calculator_result",
type: "calculator",
name: displayResult,
subtitle: query + " =",
icon: "calculate",
iconType: "material",
section: "calculator",
data: {
expression: query,
result: result
},
actions: [],
primaryAction: {
name: I18n.tr("Copy"),
icon: "content_copy",
action: "copy"
}
};
}
} catch (e) {}
return null; return null;
return Transform.createCalculatorItem(calc, query, I18n.tr("Copy"));
} }
function detectTrigger(query) { function detectTrigger(query) {
@@ -980,17 +786,7 @@ Item {
} }
function sortPluginIdsByOrder(pluginIds) { function sortPluginIdsByOrder(pluginIds) {
var order = SettingsData.launcherPluginOrder || []; return Utils.sortPluginIdsByOrder(pluginIds, SettingsData.launcherPluginOrder || []);
if (order.length === 0)
return pluginIds;
var orderMap = {};
for (var i = 0; i < order.length; i++)
orderMap[order[i]] = i;
return pluginIds.slice().sort(function (a, b) {
var aOrder = orderMap[a] !== undefined ? orderMap[a] : 9999;
var bOrder = orderMap[b] !== undefined ? orderMap[b] : 9999;
return aOrder - bOrder;
});
} }
function getAllVisiblePluginsOrdered() { function getAllVisiblePluginsOrdered() {
@@ -1011,17 +807,7 @@ Item {
isBuiltIn: true isBuiltIn: true
}); });
} }
var order = SettingsData.launcherPluginOrder || []; return Utils.sortPluginsOrdered(all, SettingsData.launcherPluginOrder || []);
if (order.length === 0)
return all;
var orderMap = {};
for (var i = 0; i < order.length; i++)
orderMap[order[i]] = i;
return all.sort(function (a, b) {
var aOrder = orderMap[a.id] !== undefined ? orderMap[a.id] : 9999;
var bOrder = orderMap[b.id] !== undefined ? orderMap[b.id] : 9999;
return aOrder - bOrder;
});
} }
function getEmptyTriggerPluginsOrdered() { function getEmptyTriggerPluginsOrdered() {
@@ -1044,72 +830,27 @@ Item {
isBuiltIn: true isBuiltIn: true
}); });
} }
var order = SettingsData.launcherPluginOrder || []; return Utils.sortPluginsOrdered(all, SettingsData.launcherPluginOrder || []);
if (order.length === 0)
return all;
var orderMap = {};
for (var i = 0; i < order.length; i++)
orderMap[order[i]] = i;
return all.sort(function (a, b) {
var aOrder = orderMap[a.id] !== undefined ? orderMap[a.id] : 9999;
var bOrder = orderMap[b.id] !== undefined ? orderMap[b.id] : 9999;
return aOrder - bOrder;
});
} }
function getPluginBrowseItems() { function getPluginBrowseItems() {
var items = []; var items = [];
var browseLabel = I18n.tr("Browse");
var triggerLabel = I18n.tr("Trigger: %1");
var noTriggerLabel = I18n.tr("No trigger");
var launchers = PluginService.getLauncherPlugins(); var launchers = PluginService.getLauncherPlugins();
for (var pluginId in launchers) { for (var pluginId in launchers) {
var plugin = launchers[pluginId];
var trigger = PluginService.getPluginTrigger(pluginId); var trigger = PluginService.getPluginTrigger(pluginId);
var rawIcon = plugin.icon || "extension"; var isAllowed = SettingsData.getPluginAllowWithoutTrigger(pluginId);
items.push({ items.push(Transform.createPluginBrowseItem(pluginId, launchers[pluginId], trigger, false, isAllowed, browseLabel, triggerLabel, noTriggerLabel));
id: "browse_" + pluginId,
type: "plugin_browse",
name: plugin.name || pluginId,
subtitle: trigger ? I18n.tr("Trigger: %1").arg(trigger) : I18n.tr("No trigger"),
icon: stripIconPrefix(rawIcon),
iconType: detectIconType(rawIcon),
section: "browse_plugins",
data: {
pluginId: pluginId,
plugin: plugin
},
actions: [],
primaryAction: {
name: I18n.tr("Browse"),
icon: "arrow_forward",
action: "browse_plugin"
}
});
} }
var builtInLaunchers = AppSearchService.getBuiltInLauncherPlugins(); var builtInLaunchers = AppSearchService.getBuiltInLauncherPlugins();
for (var pluginId in builtInLaunchers) { for (var pluginId in builtInLaunchers) {
var plugin = builtInLaunchers[pluginId];
var trigger = AppSearchService.getBuiltInPluginTrigger(pluginId); var trigger = AppSearchService.getBuiltInPluginTrigger(pluginId);
items.push({ var isAllowed = SettingsData.getPluginAllowWithoutTrigger(pluginId);
id: "browse_" + pluginId, items.push(Transform.createPluginBrowseItem(pluginId, builtInLaunchers[pluginId], trigger, true, isAllowed, browseLabel, triggerLabel, noTriggerLabel));
type: "plugin_browse",
name: plugin.name || pluginId,
subtitle: trigger ? I18n.tr("Trigger: %1").arg(trigger) : I18n.tr("No trigger"),
icon: plugin.cornerIcon || "extension",
iconType: "material",
section: "browse_plugins",
data: {
pluginId: pluginId,
plugin: plugin,
isBuiltIn: true
},
actions: [],
primaryAction: {
name: I18n.tr("Browse"),
icon: "arrow_forward",
action: "browse_plugin"
}
});
} }
return items; return items;
@@ -1134,34 +875,6 @@ Item {
return transformed; return transformed;
} }
function detectIconType(iconName) {
if (!iconName)
return "material";
if (iconName.startsWith("unicode:"))
return "unicode";
if (iconName.startsWith("material:"))
return "material";
if (iconName.startsWith("image:"))
return "image";
if (iconName.indexOf("/") >= 0 || iconName.indexOf(".") >= 0)
return "image";
if (/^[a-z]+-[a-z]/.test(iconName.toLowerCase()))
return "image";
return "material";
}
function stripIconPrefix(iconName) {
if (!iconName)
return "extension";
if (iconName.startsWith("unicode:"))
return iconName.substring(8);
if (iconName.startsWith("material:"))
return iconName.substring(9);
if (iconName.startsWith("image:"))
return iconName.substring(6);
return iconName;
}
function getPluginName(pluginId, isBuiltIn) { function getPluginName(pluginId, isBuiltIn) {
if (isBuiltIn) { if (isBuiltIn) {
var plugin = AppSearchService.builtInPlugins[pluginId]; var plugin = AppSearchService.builtInPlugins[pluginId];
@@ -1187,7 +900,7 @@ Item {
var rawIcon = launchers[pluginId].icon || "extension"; var rawIcon = launchers[pluginId].icon || "extension";
return { return {
name: launchers[pluginId].name || pluginId, name: launchers[pluginId].name || pluginId,
icon: stripIconPrefix(rawIcon) icon: Utils.stripIconPrefix(rawIcon)
}; };
} }
return { return {
@@ -1219,9 +932,8 @@ Item {
defaultViewMode: viewPref.mode || "list" defaultViewMode: viewPref.mode || "list"
}; };
if (viewPref.enforced) { if (viewPref.mode)
setPluginViewPreference(section, viewPref.mode, true); setPluginViewPreference(section, viewPref.mode, viewPref.enforced);
}
basePriority += 0.01; basePriority += 0.01;
} }
@@ -1257,36 +969,7 @@ Item {
} }
function transformPluginItem(item, pluginId) { function transformPluginItem(item, pluginId) {
var rawIcon = item.icon || "extension"; return Transform.transformPluginItem(item, pluginId, I18n.tr("Select"));
var icon = stripIconPrefix(rawIcon);
var iconType = item.iconType;
if (!iconType) {
if (rawIcon.startsWith("material:"))
iconType = "material";
else if (rawIcon.startsWith("unicode:"))
iconType = "unicode";
else
iconType = "image";
}
return {
id: item.id || item.name || "",
type: "plugin",
name: item.name || "",
subtitle: item.comment || item.description || "",
icon: icon,
iconType: iconType,
section: "plugin_" + pluginId,
data: item,
pluginId: pluginId,
keywords: item.keywords || [],
actions: item.actions || [],
primaryAction: item.primaryAction || {
name: I18n.tr("Select"),
icon: "check",
action: "execute"
}
};
} }
function getFrecencyForItem(item) { function getFrecencyForItem(item) {
@@ -1312,11 +995,7 @@ Item {
} }
function getFirstItemIndex() { function getFirstItemIndex() {
for (var i = 0; i < flatModel.length; i++) { return Nav.getFirstItemIndex(flatModel);
if (!flatModel[i].isHeader)
return i;
}
return 0;
} }
function updateSelectedItem() { function updateSelectedItem() {
@@ -1337,266 +1016,67 @@ Item {
return getSectionViewMode(entry.sectionId); return getSectionViewMode(entry.sectionId);
} }
function findNextNonHeaderIndex(startIndex) {
for (var i = startIndex; i < flatModel.length; i++) {
if (!flatModel[i].isHeader)
return i;
}
return -1;
}
function findPrevNonHeaderIndex(startIndex) {
for (var i = startIndex; i >= 0; i--) {
if (!flatModel[i].isHeader)
return i;
}
return -1;
}
function getSectionBounds(sectionId) {
var start = -1, end = -1;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader && flatModel[i].section?.id === sectionId) {
start = i + 1;
} else if (start >= 0 && !flatModel[i].isHeader && flatModel[i].sectionId === sectionId) {
end = i;
} else if (start >= 0 && end >= 0 && flatModel[i].sectionId !== sectionId) {
break;
}
}
return {
start: start,
end: end,
count: end >= start ? end - start + 1 : 0
};
}
function getGridColumns(sectionId) { function getGridColumns(sectionId) {
var mode = getSectionViewMode(sectionId); return Nav.getGridColumns(getSectionViewMode(sectionId), gridColumns);
if (mode === "tile")
return 3;
if (mode === "grid")
return gridColumns;
return 1;
} }
function selectNext() { function selectNext() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculateNextIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
return; if (newIndex !== selectedFlatIndex) {
var entry = flatModel[selectedFlatIndex]; selectedFlatIndex = newIndex;
if (!entry || entry.isHeader) {
var next = findNextNonHeaderIndex(selectedFlatIndex + 1);
if (next !== -1) {
selectedFlatIndex = next;
updateSelectedItem(); updateSelectedItem();
} }
return;
}
var viewMode = getSectionViewMode(entry.sectionId);
if (viewMode === "list") {
var next = findNextNonHeaderIndex(selectedFlatIndex + 1);
if (next !== -1) {
selectedFlatIndex = next;
updateSelectedItem();
}
return;
}
var bounds = getSectionBounds(entry.sectionId);
var cols = getGridColumns(entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
var newPosInSection = posInSection + cols;
if (newPosInSection < bounds.count) {
selectedFlatIndex = bounds.start + newPosInSection;
updateSelectedItem();
} else {
var nextSection = findNextNonHeaderIndex(bounds.end + 1);
if (nextSection !== -1) {
selectedFlatIndex = nextSection;
updateSelectedItem();
}
}
} }
function selectPrevious() { function selectPrevious() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculatePrevIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
return; if (newIndex !== selectedFlatIndex) {
var entry = flatModel[selectedFlatIndex]; selectedFlatIndex = newIndex;
if (!entry || entry.isHeader) {
var prev = findPrevNonHeaderIndex(selectedFlatIndex - 1);
if (prev !== -1) {
selectedFlatIndex = prev;
updateSelectedItem(); updateSelectedItem();
} }
return;
}
var viewMode = getSectionViewMode(entry.sectionId);
if (viewMode === "list") {
var prev = findPrevNonHeaderIndex(selectedFlatIndex - 1);
if (prev !== -1) {
selectedFlatIndex = prev;
updateSelectedItem();
}
return;
}
var bounds = getSectionBounds(entry.sectionId);
var cols = getGridColumns(entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
var newPosInSection = posInSection - cols;
if (newPosInSection >= 0) {
selectedFlatIndex = bounds.start + newPosInSection;
updateSelectedItem();
} else {
var prevItem = findPrevNonHeaderIndex(bounds.start - 1);
if (prevItem !== -1) {
selectedFlatIndex = prevItem;
updateSelectedItem();
}
}
} }
function selectRight() { function selectRight() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculateRightIndex(flatModel, selectedFlatIndex, getSectionViewMode);
return; if (newIndex !== selectedFlatIndex) {
var entry = flatModel[selectedFlatIndex]; selectedFlatIndex = newIndex;
if (!entry || entry.isHeader) {
var next = findNextNonHeaderIndex(selectedFlatIndex + 1);
if (next !== -1) {
selectedFlatIndex = next;
updateSelectedItem();
}
return;
}
var viewMode = getSectionViewMode(entry.sectionId);
if (viewMode === "list") {
var next = findNextNonHeaderIndex(selectedFlatIndex + 1);
if (next !== -1) {
selectedFlatIndex = next;
updateSelectedItem();
}
return;
}
var bounds = getSectionBounds(entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
if (posInSection + 1 < bounds.count) {
selectedFlatIndex = bounds.start + posInSection + 1;
updateSelectedItem(); updateSelectedItem();
} }
} }
function selectLeft() { function selectLeft() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculateLeftIndex(flatModel, selectedFlatIndex, getSectionViewMode);
return; if (newIndex !== selectedFlatIndex) {
var entry = flatModel[selectedFlatIndex]; selectedFlatIndex = newIndex;
if (!entry || entry.isHeader) {
var prev = findPrevNonHeaderIndex(selectedFlatIndex - 1);
if (prev !== -1) {
selectedFlatIndex = prev;
updateSelectedItem();
}
return;
}
var viewMode = getSectionViewMode(entry.sectionId);
if (viewMode === "list") {
var prev = findPrevNonHeaderIndex(selectedFlatIndex - 1);
if (prev !== -1) {
selectedFlatIndex = prev;
updateSelectedItem();
}
return;
}
var bounds = getSectionBounds(entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
if (posInSection > 0) {
selectedFlatIndex = bounds.start + posInSection - 1;
updateSelectedItem(); updateSelectedItem();
} }
} }
function selectNextSection() { function selectNextSection() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
var currentSection = null; var newIndex = Nav.calculateNextSectionIndex(flatModel, selectedFlatIndex);
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) { if (newIndex !== selectedFlatIndex) {
currentSection = flatModel[selectedFlatIndex].sectionId; selectedFlatIndex = newIndex;
}
var foundCurrent = false;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader) {
if (foundCurrent) {
for (var j = i + 1; j < flatModel.length; j++) {
if (!flatModel[j].isHeader) {
selectedFlatIndex = j;
updateSelectedItem(); updateSelectedItem();
return;
}
}
}
if (flatModel[i].section.id === currentSection) {
foundCurrent = true;
}
}
} }
} }
function selectPreviousSection() { function selectPreviousSection() {
keyboardNavigationActive = true; keyboardNavigationActive = true;
var currentSection = null; var newIndex = Nav.calculatePrevSectionIndex(flatModel, selectedFlatIndex);
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) { if (newIndex !== selectedFlatIndex) {
currentSection = flatModel[selectedFlatIndex].sectionId; selectedFlatIndex = newIndex;
}
var lastSectionStart = -1;
var prevSectionStart = -1;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader) {
if (flatModel[i].section.id === currentSection) {
break;
}
prevSectionStart = lastSectionStart;
lastSectionStart = i;
}
}
if (prevSectionStart >= 0) {
for (var j = prevSectionStart + 1; j < flatModel.length; j++) {
if (!flatModel[j].isHeader) {
selectedFlatIndex = j;
updateSelectedItem(); updateSelectedItem();
return;
}
}
} }
} }
function selectPageDown(visibleItems) { function selectPageDown(visibleItems) {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculatePageDownIndex(flatModel, selectedFlatIndex, visibleItems);
return;
var itemsToSkip = visibleItems || 8;
var newIndex = selectedFlatIndex;
for (var i = 0; i < itemsToSkip; i++) {
var next = findNextNonHeaderIndex(newIndex + 1);
if (next === -1)
break;
newIndex = next;
}
if (newIndex !== selectedFlatIndex) { if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex; selectedFlatIndex = newIndex;
updateSelectedItem(); updateSelectedItem();
@@ -1605,18 +1085,7 @@ Item {
function selectPageUp(visibleItems) { function selectPageUp(visibleItems) {
keyboardNavigationActive = true; keyboardNavigationActive = true;
if (flatModel.length === 0) var newIndex = Nav.calculatePageUpIndex(flatModel, selectedFlatIndex, visibleItems);
return;
var itemsToSkip = visibleItems || 8;
var newIndex = selectedFlatIndex;
for (var i = 0; i < itemsToSkip; i++) {
var prev = findPrevNonHeaderIndex(newIndex - 1);
if (prev === -1)
break;
newIndex = prev;
}
if (newIndex !== selectedFlatIndex) { if (newIndex !== selectedFlatIndex) {
selectedFlatIndex = newIndex; selectedFlatIndex = newIndex;
updateSelectedItem(); updateSelectedItem();
@@ -1747,6 +1216,14 @@ Item {
launchAppWithNvidia(item.data); launchAppWithNvidia(item.data);
} }
break; break;
case "toggle_all_visibility":
if (item.type === "plugin_browse" && item.data?.pluginId) {
var pluginId = item.data.pluginId;
var currentState = SettingsData.getPluginAllowWithoutTrigger(pluginId);
SettingsData.setPluginAllowWithoutTrigger(pluginId, !currentState);
performSearch();
}
return;
default: default:
if (item.type === "app" && action.actionData) { if (item.type === "app" && action.actionData) {
launchAppAction({ launchAppAction({

View File

@@ -0,0 +1,157 @@
.pragma library
function getFileIcon(filename) {
var ext = filename.lastIndexOf(".") > 0 ? filename.substring(filename.lastIndexOf(".") + 1).toLowerCase() : "";
switch (ext) {
case "pdf":
return "picture_as_pdf";
case "doc":
case "docx":
case "odt":
return "description";
case "xls":
case "xlsx":
case "ods":
return "table_chart";
case "ppt":
case "pptx":
case "odp":
return "slideshow";
case "txt":
case "md":
case "rst":
return "article";
case "jpg":
case "jpeg":
case "png":
case "gif":
case "svg":
case "webp":
return "image";
case "mp3":
case "wav":
case "flac":
case "ogg":
return "audio_file";
case "mp4":
case "mkv":
case "avi":
case "webm":
return "video_file";
case "zip":
case "tar":
case "gz":
case "7z":
case "rar":
return "folder_zip";
case "js":
case "ts":
case "py":
case "rs":
case "go":
case "java":
case "c":
case "cpp":
case "h":
return "code";
case "html":
case "css":
case "htm":
return "web";
case "json":
case "xml":
case "yaml":
case "yml":
return "data_object";
case "sh":
case "bash":
case "zsh":
return "terminal";
default:
return "insert_drive_file";
}
}
function stripIconPrefix(iconName) {
if (!iconName)
return "extension";
if (iconName.startsWith("unicode:"))
return iconName.substring(8);
if (iconName.startsWith("material:"))
return iconName.substring(9);
if (iconName.startsWith("image:"))
return iconName.substring(6);
return iconName;
}
function detectIconType(iconName) {
if (!iconName)
return "material";
if (iconName.startsWith("unicode:"))
return "unicode";
if (iconName.startsWith("material:"))
return "material";
if (iconName.startsWith("image:"))
return "image";
if (iconName.indexOf("/") >= 0 || iconName.indexOf(".") >= 0)
return "image";
if (/^[a-z]+-[a-z]/.test(iconName.toLowerCase()))
return "image";
return "material";
}
function evaluateCalculator(query) {
if (!query || query.length === 0)
return null;
var mathExpr = query.replace(/[^0-9+\-*/().%\s^]/g, "");
if (mathExpr.length < 2)
return null;
var hasMath = /[+\-*/^%]/.test(query) && /\d/.test(query);
if (!hasMath)
return null;
try {
var sanitized = mathExpr.replace(/\^/g, "**");
var result = Function('"use strict"; return (' + sanitized + ')')();
if (typeof result === "number" && isFinite(result)) {
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, "");
return {
expression: query,
result: result,
displayResult: displayResult
};
}
} catch (e) { }
return null;
}
function sortPluginIdsByOrder(pluginIds, order) {
if (!order || order.length === 0)
return pluginIds;
var orderMap = {};
for (var i = 0; i < order.length; i++)
orderMap[order[i]] = i;
return pluginIds.slice().sort(function (a, b) {
var aOrder = orderMap[a] !== undefined ? orderMap[a] : 9999;
var bOrder = orderMap[b] !== undefined ? orderMap[b] : 9999;
return aOrder - bOrder;
});
}
function sortPluginsOrdered(plugins, order) {
if (!order || order.length === 0)
return plugins;
var orderMap = {};
for (var i = 0; i < order.length; i++)
orderMap[order[i]] = i;
return plugins.sort(function (a, b) {
var aOrder = orderMap[a.id] !== undefined ? orderMap[a.id] : 9999;
var bOrder = orderMap[b.id] !== undefined ? orderMap[b.id] : 9999;
return aOrder - bOrder;
});
}

View File

@@ -0,0 +1,223 @@
.pragma library
.import "ControllerUtils.js" as Utils
function transformApp(app, override, defaultActions, primaryActionLabel) {
var appId = app.id || app.execString || app.exec || "";
var actions = [];
if (app.actions && app.actions.length > 0) {
for (var i = 0; i < app.actions.length; i++) {
actions.push({
name: app.actions[i].name,
icon: "play_arrow",
actionData: app.actions[i]
});
}
}
return {
id: appId,
type: "app",
name: override?.name || app.name || "",
subtitle: override?.comment || app.comment || "",
icon: override?.icon || app.icon || "application-x-executable",
iconType: "image",
section: "apps",
data: app,
keywords: app.keywords || [],
actions: actions,
primaryAction: {
name: primaryActionLabel,
icon: "open_in_new",
action: "launch"
}
};
}
function transformCoreApp(app, openLabel) {
var iconName = "apps";
var iconType = "material";
if (app.icon) {
if (app.icon.startsWith("svg+corner:")) {
iconType = "composite";
} else if (app.icon.startsWith("material:")) {
iconName = app.icon.substring(9);
} else {
iconName = app.icon;
iconType = "image";
}
}
return {
id: app.builtInPluginId || app.action || "",
type: "app",
name: app.name || "",
subtitle: app.comment || "",
icon: iconName,
iconType: iconType,
iconFull: app.icon,
section: "apps",
data: app,
isCore: true,
actions: [],
primaryAction: {
name: openLabel,
icon: "open_in_new",
action: "launch"
}
};
}
function transformBuiltInLauncherItem(item, pluginId, openLabel) {
var rawIcon = item.icon || "extension";
var icon = Utils.stripIconPrefix(rawIcon);
var iconType = item.iconType;
if (!iconType) {
if (rawIcon.startsWith("material:"))
iconType = "material";
else if (rawIcon.startsWith("unicode:"))
iconType = "unicode";
else
iconType = "image";
}
return {
id: item.action || "",
type: "plugin",
name: item.name || "",
subtitle: item.comment || "",
icon: icon,
iconType: iconType,
section: "plugin_" + pluginId,
data: item,
pluginId: pluginId,
isBuiltInLauncher: true,
keywords: item.keywords || [],
actions: [],
primaryAction: {
name: openLabel,
icon: "open_in_new",
action: "execute"
}
};
}
function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel) {
var filename = file.path ? file.path.split("/").pop() : "";
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
return {
id: file.path || "",
type: "file",
name: filename,
subtitle: dirname,
icon: Utils.getFileIcon(filename),
iconType: "material",
section: "files",
data: file,
actions: [
{
name: openFolderLabel,
icon: "folder_open",
action: "open_folder"
},
{
name: copyPathLabel,
icon: "content_copy",
action: "copy_path"
}
],
primaryAction: {
name: openLabel,
icon: "open_in_new",
action: "open"
}
};
}
function transformPluginItem(item, pluginId, selectLabel) {
var rawIcon = item.icon || "extension";
var icon = Utils.stripIconPrefix(rawIcon);
var iconType = item.iconType;
if (!iconType) {
if (rawIcon.startsWith("material:"))
iconType = "material";
else if (rawIcon.startsWith("unicode:"))
iconType = "unicode";
else
iconType = "image";
}
return {
id: item.id || item.name || "",
type: "plugin",
name: item.name || "",
subtitle: item.comment || item.description || "",
icon: icon,
iconType: iconType,
section: "plugin_" + pluginId,
data: item,
pluginId: pluginId,
keywords: item.keywords || [],
actions: item.actions || [],
primaryAction: item.primaryAction || {
name: selectLabel,
icon: "check",
action: "execute"
}
};
}
function createCalculatorItem(calc, query, copyLabel) {
return {
id: "calculator_result",
type: "calculator",
name: calc.displayResult,
subtitle: query + " =",
icon: "calculate",
iconType: "material",
section: "calculator",
data: {
expression: calc.expression,
result: calc.result
},
actions: [],
primaryAction: {
name: copyLabel,
icon: "content_copy",
action: "copy"
}
};
}
function createPluginBrowseItem(pluginId, plugin, trigger, isBuiltIn, isAllowed, browseLabel, triggerLabel, noTriggerLabel) {
var rawIcon = isBuiltIn ? (plugin.cornerIcon || "extension") : (plugin.icon || "extension");
return {
id: "browse_" + pluginId,
type: "plugin_browse",
name: plugin.name || pluginId,
subtitle: trigger ? triggerLabel.replace("%1", trigger) : noTriggerLabel,
icon: isBuiltIn ? rawIcon : Utils.stripIconPrefix(rawIcon),
iconType: isBuiltIn ? "material" : Utils.detectIconType(rawIcon),
section: "browse_plugins",
data: {
pluginId: pluginId,
plugin: plugin,
isBuiltIn: isBuiltIn
},
actions: [
{
name: "All",
icon: isAllowed ? "visibility" : "visibility_off",
action: "toggle_all_visibility"
}
],
primaryAction: {
name: browseLabel,
icon: "arrow_forward",
action: "browse_plugin"
}
};
}

View File

@@ -0,0 +1,245 @@
.pragma library
function getFirstItemIndex(flatModel) {
for (var i = 0; i < flatModel.length; i++) {
if (!flatModel[i].isHeader)
return i;
}
return 0;
}
function findNextNonHeaderIndex(flatModel, startIndex) {
for (var i = startIndex; i < flatModel.length; i++) {
if (!flatModel[i].isHeader)
return i;
}
return -1;
}
function findPrevNonHeaderIndex(flatModel, startIndex) {
for (var i = startIndex; i >= 0; i--) {
if (!flatModel[i].isHeader)
return i;
}
return -1;
}
function getSectionBounds(flatModel, sectionId) {
var start = -1, end = -1;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader && flatModel[i].section?.id === sectionId) {
start = i + 1;
} else if (start >= 0 && !flatModel[i].isHeader && flatModel[i].sectionId === sectionId) {
end = i;
} else if (start >= 0 && end >= 0 && flatModel[i].sectionId !== sectionId) {
break;
}
}
return {
start: start,
end: end,
count: end >= start ? end - start + 1 : 0
};
}
function getGridColumns(viewMode, gridColumns) {
switch (viewMode) {
case "tile":
return 3;
case "grid":
return gridColumns;
default:
return 1;
}
}
function calculateNextIndex(flatModel, selectedFlatIndex, sectionId, viewMode, gridColumns, getSectionViewModeFn) {
if (flatModel.length === 0)
return selectedFlatIndex;
var entry = flatModel[selectedFlatIndex];
if (!entry || entry.isHeader) {
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
return next !== -1 ? next : selectedFlatIndex;
}
var actualViewMode = viewMode || getSectionViewModeFn(entry.sectionId);
if (actualViewMode === "list") {
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
return next !== -1 ? next : selectedFlatIndex;
}
var bounds = getSectionBounds(flatModel, entry.sectionId);
var cols = getGridColumns(actualViewMode, gridColumns);
var posInSection = selectedFlatIndex - bounds.start;
var newPosInSection = posInSection + cols;
if (newPosInSection < bounds.count) {
return bounds.start + newPosInSection;
}
var nextSection = findNextNonHeaderIndex(flatModel, bounds.end + 1);
return nextSection !== -1 ? nextSection : selectedFlatIndex;
}
function calculatePrevIndex(flatModel, selectedFlatIndex, sectionId, viewMode, gridColumns, getSectionViewModeFn) {
if (flatModel.length === 0)
return selectedFlatIndex;
var entry = flatModel[selectedFlatIndex];
if (!entry || entry.isHeader) {
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
return prev !== -1 ? prev : selectedFlatIndex;
}
var actualViewMode = viewMode || getSectionViewModeFn(entry.sectionId);
if (actualViewMode === "list") {
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
return prev !== -1 ? prev : selectedFlatIndex;
}
var bounds = getSectionBounds(flatModel, entry.sectionId);
var cols = getGridColumns(actualViewMode, gridColumns);
var posInSection = selectedFlatIndex - bounds.start;
var newPosInSection = posInSection - cols;
if (newPosInSection >= 0) {
return bounds.start + newPosInSection;
}
var prevItem = findPrevNonHeaderIndex(flatModel, bounds.start - 1);
return prevItem !== -1 ? prevItem : selectedFlatIndex;
}
function calculateRightIndex(flatModel, selectedFlatIndex, getSectionViewModeFn) {
if (flatModel.length === 0)
return selectedFlatIndex;
var entry = flatModel[selectedFlatIndex];
if (!entry || entry.isHeader) {
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
return next !== -1 ? next : selectedFlatIndex;
}
var viewMode = getSectionViewModeFn(entry.sectionId);
if (viewMode === "list") {
var next = findNextNonHeaderIndex(flatModel, selectedFlatIndex + 1);
return next !== -1 ? next : selectedFlatIndex;
}
var bounds = getSectionBounds(flatModel, entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
if (posInSection + 1 < bounds.count) {
return bounds.start + posInSection + 1;
}
return selectedFlatIndex;
}
function calculateLeftIndex(flatModel, selectedFlatIndex, getSectionViewModeFn) {
if (flatModel.length === 0)
return selectedFlatIndex;
var entry = flatModel[selectedFlatIndex];
if (!entry || entry.isHeader) {
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
return prev !== -1 ? prev : selectedFlatIndex;
}
var viewMode = getSectionViewModeFn(entry.sectionId);
if (viewMode === "list") {
var prev = findPrevNonHeaderIndex(flatModel, selectedFlatIndex - 1);
return prev !== -1 ? prev : selectedFlatIndex;
}
var bounds = getSectionBounds(flatModel, entry.sectionId);
var posInSection = selectedFlatIndex - bounds.start;
if (posInSection > 0) {
return bounds.start + posInSection - 1;
}
return selectedFlatIndex;
}
function calculateNextSectionIndex(flatModel, selectedFlatIndex) {
var currentSection = null;
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) {
currentSection = flatModel[selectedFlatIndex].sectionId;
}
var foundCurrent = false;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader) {
if (foundCurrent) {
for (var j = i + 1; j < flatModel.length; j++) {
if (!flatModel[j].isHeader)
return j;
}
}
if (flatModel[i].section.id === currentSection) {
foundCurrent = true;
}
}
}
return selectedFlatIndex;
}
function calculatePrevSectionIndex(flatModel, selectedFlatIndex) {
var currentSection = null;
if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) {
currentSection = flatModel[selectedFlatIndex].sectionId;
}
var lastSectionStart = -1;
var prevSectionStart = -1;
for (var i = 0; i < flatModel.length; i++) {
if (flatModel[i].isHeader) {
if (flatModel[i].section.id === currentSection) {
break;
}
prevSectionStart = lastSectionStart;
lastSectionStart = i;
}
}
if (prevSectionStart >= 0) {
for (var j = prevSectionStart + 1; j < flatModel.length; j++) {
if (!flatModel[j].isHeader)
return j;
}
}
return selectedFlatIndex;
}
function calculatePageDownIndex(flatModel, selectedFlatIndex, visibleItems) {
if (flatModel.length === 0)
return selectedFlatIndex;
var itemsToSkip = visibleItems || 8;
var newIndex = selectedFlatIndex;
for (var i = 0; i < itemsToSkip; i++) {
var next = findNextNonHeaderIndex(flatModel, newIndex + 1);
if (next === -1)
break;
newIndex = next;
}
return newIndex;
}
function calculatePageUpIndex(flatModel, selectedFlatIndex, visibleItems) {
if (flatModel.length === 0)
return selectedFlatIndex;
var itemsToSkip = visibleItems || 8;
var newIndex = selectedFlatIndex;
for (var i = 0; i < itemsToSkip; i++) {
var prev = findPrevNonHeaderIndex(flatModel, newIndex - 1);
if (prev === -1)
break;
newIndex = prev;
}
return newIndex;
}

View File

@@ -9,7 +9,7 @@ Rectangle {
property var item: null property var item: null
property bool isSelected: false property bool isSelected: false
property bool isHovered: itemArea.containsMouse property bool isHovered: itemArea.containsMouse || allModeToggleArea.containsMouse
property var controller: null property var controller: null
property int flatIndex: -1 property int flatIndex: -1
@@ -38,6 +38,29 @@ Rectangle {
color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : "transparent" color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryPressed : "transparent"
radius: Theme.cornerRadius radius: Theme.cornerRadius
MouseArea {
id: itemArea
anchors.fill: parent
anchors.rightMargin: root.item?.type === "plugin_browse" ? 40 : 0
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var scenePos = mapToItem(null, mouse.x, mouse.y);
root.rightClicked(scenePos.x, scenePos.y);
} else {
root.clicked();
}
}
onPositionChanged: {
if (root.controller)
root.controller.keyboardNavigationActive = false;
}
}
Row { Row {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: Theme.spacingM anchors.leftMargin: Theme.spacingM
@@ -86,7 +109,47 @@ Rectangle {
spacing: Theme.spacingS spacing: Theme.spacingS
Rectangle { Rectangle {
visible: root.item?.type && root.item.type !== "app" id: allModeToggle
visible: root.item?.type === "plugin_browse"
width: 28
height: 28
radius: 14
anchors.verticalCenter: parent.verticalCenter
color: allModeToggleArea.containsMouse ? Theme.surfaceHover : "transparent"
property bool isAllowed: {
if (root.item?.type !== "plugin_browse")
return false;
var pluginId = root.item?.data?.pluginId;
if (!pluginId)
return false;
SettingsData.launcherPluginVisibility;
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
}
DankIcon {
anchors.centerIn: parent
name: allModeToggle.isAllowed ? "visibility" : "visibility_off"
size: 18
color: allModeToggle.isAllowed ? Theme.primary : Theme.surfaceVariantText
}
MouseArea {
id: allModeToggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
var pluginId = root.item?.data?.pluginId;
if (!pluginId)
return;
SettingsData.setPluginAllowWithoutTrigger(pluginId, !allModeToggle.isAllowed);
}
}
}
Rectangle {
visible: root.item?.type && root.item.type !== "app" && root.item.type !== "plugin_browse"
width: typeBadge.implicitWidth + Theme.spacingS * 2 width: typeBadge.implicitWidth + Theme.spacingS * 2
height: 20 height: 20
radius: 10 radius: 10
@@ -116,27 +179,4 @@ Rectangle {
} }
} }
} }
MouseArea {
id: itemArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var scenePos = mapToItem(null, mouse.x, mouse.y);
root.rightClicked(scenePos.x, scenePos.y);
} else {
root.clicked();
}
}
onPositionChanged: {
if (root.controller) {
root.controller.keyboardNavigationActive = false;
}
}
}
} }

View File

@@ -1,7 +1,9 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { Rectangle {
@@ -21,9 +23,22 @@ Rectangle {
border.width: isSelected ? 2 : 0 border.width: isSelected ? 2 : 0
border.color: Theme.primary border.color: Theme.primary
readonly property string toplevelId: item?.data?.toplevelId ?? ""
readonly property var waylandToplevel: {
if (!toplevelId || !item?.pluginId)
return null;
const pluginInstance = PluginService.pluginInstances[item.pluginId];
if (!pluginInstance?.getToplevelById)
return null;
return pluginInstance.getToplevelById(toplevelId);
}
readonly property bool hasScreencopy: waylandToplevel !== null
readonly property string iconValue: { readonly property string iconValue: {
if (!item) if (!item)
return ""; return "";
if (hasScreencopy)
return "";
var data = item.data; var data = item.data;
if (data?.imageUrl) if (data?.imageUrl)
return "image:" + data.imageUrl; return "image:" + data.imageUrl;
@@ -63,12 +78,26 @@ Rectangle {
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
clip: true clip: true
ScreencopyView {
id: screencopyView
anchors.fill: parent
captureSource: root.waylandToplevel
live: root.hasScreencopy
visible: root.hasScreencopy
Rectangle {
anchors.fill: parent
color: root.isHovered ? Theme.withAlpha(Theme.surfaceVariant, 0.2) : "transparent"
}
}
AppIconRenderer { AppIconRenderer {
anchors.fill: parent anchors.fill: parent
iconValue: root.iconValue iconValue: root.iconValue
iconSize: Math.min(parent.width, parent.height) iconSize: Math.min(parent.width, parent.height)
fallbackText: (root.item?.name?.length > 0) ? root.item.name.charAt(0).toUpperCase() : "?" fallbackText: (root.item?.name?.length > 0) ? root.item.name.charAt(0).toUpperCase() : "?"
materialIconSizeAdjustment: iconSize * 0.3 materialIconSizeAdjustment: iconSize * 0.3
visible: !root.hasScreencopy
} }
Rectangle { Rectangle {
@@ -110,16 +139,26 @@ Rectangle {
} }
} }
Image { Rectangle {
id: attributionBadge
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.margins: Theme.spacingXS anchors.margins: Theme.spacingXS
width: 40 width: root.hasScreencopy ? 28 : 40
height: 16 height: root.hasScreencopy ? 28 : 16
radius: root.hasScreencopy ? 14 : 4
color: root.hasScreencopy ? Theme.surfaceContainer : "transparent"
visible: attributionImage.status === Image.Ready
opacity: 0.95
Image {
id: attributionImage
anchors.fill: parent
anchors.margins: root.hasScreencopy ? 4 : 0
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: root.item?.data?.attribution || "" source: root.item?.data?.attribution || ""
visible: source !== "" mipmap: true
opacity: 0.9 }
} }
} }
} }