1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-13 22:46:34 -04:00

feat(Clipboard): Implement clipboard/settings search functionality in DMS Launcher

This commit is contained in:
purian23
2026-05-16 17:43:52 -04:00
parent 9f2ae6241e
commit c923c43322
11 changed files with 426 additions and 17 deletions
@@ -76,6 +76,13 @@ Rectangle {
} }
} }
break; break;
case "clipboard":
if (selectedItem?.actions) {
for (var i = 0; i < selectedItem.actions.length; i++) {
result.push(selectedItem.actions[i]);
}
}
break;
} }
return result; return result;
} }
@@ -0,0 +1,67 @@
pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property var entry: null
property string cachedImageData: ""
property var _requestedEntryId: null
readonly property bool canLoadImage: !!entry?.isImage && (entry?.mimeType ?? "").startsWith("image/")
readonly property string sourceUrl: cachedImageData.length > 0 ? "data:" + (entry?.mimeType ?? "image/png") + ";base64," + cachedImageData : ""
radius: Math.max(6, Theme.cornerRadius - 2)
clip: true
color: Theme.surfaceContainerHigh
border.color: Theme.withAlpha(Theme.outline, 0.16)
border.width: 1
onEntryChanged: reloadPreview()
Component.onCompleted: reloadPreview()
function reloadPreview() {
cachedImageData = "";
if (!canLoadImage || !entry?.id) {
_requestedEntryId = null;
return;
}
const entryId = entry.id;
_requestedEntryId = entryId;
DMSService.sendRequest("clipboard.getEntry", {
"id": entryId
}, function (response) {
if (_requestedEntryId !== entryId)
return;
if (response.error)
return;
const data = response.result?.data ?? "";
if (data.length > 0)
cachedImageData = data;
});
}
Image {
id: previewImage
anchors.fill: parent
source: root.sourceUrl
asynchronous: true
cache: false
smooth: true
fillMode: Image.PreserveAspectCrop
visible: status === Image.Ready
}
DankIcon {
anchors.centerIn: parent
name: "image"
size: Math.min(22, Math.max(16, root.height * 0.46))
color: Theme.primary
visible: previewImage.status !== Image.Ready
}
}
+144 -7
View File
@@ -44,7 +44,10 @@ Item {
signal searchQueryRequested(string query) signal searchQueryRequested(string query)
onActiveChanged: { onActiveChanged: {
if (!active) { if (active) {
if (clipboardSearchEnabledInAll())
ClipboardService.ensureLauncherHistory();
} else {
SessionData.addLauncherHistory(searchQuery); SessionData.addLauncherHistory(searchQuery);
sections = []; sections = [];
@@ -69,6 +72,28 @@ Item {
AppSearchService.invalidateLauncherCache(); AppSearchService.invalidateLauncherCache();
_clearModeCache(); _clearModeCache();
} }
function onLauncherPluginVisibilityChanged() {
AppSearchService.invalidateLauncherCache();
_clearModeCache();
if (active)
performSearch();
}
function onBuiltInPluginSettingsChanged() {
AppSearchService.invalidateLauncherCache();
_clearModeCache();
if (active)
performSearch();
}
}
Connections {
target: ClipboardService
function onInternalEntriesChanged() {
if (!active || !clipboardSearchEnabledInAll())
return;
if (searchMode === "all" && searchQuery.length >= 2)
performSearch();
}
} }
Connections { Connections {
@@ -124,6 +149,18 @@ Item {
function pasteSelected() { function pasteSelected() {
if (!selectedItem) if (!selectedItem)
return; return;
if (selectedItem.type === "clipboard") {
if (SettingsData.clipboardEnterToPaste) {
ClipboardService.copyEntry(selectedItem.data, function () {
root.itemExecuted();
});
} else {
ClipboardService.pasteEntry(selectedItem.data, function () {
root.itemExecuted();
});
}
return;
}
if (!SessionService.wtypeAvailable) { if (!SessionService.wtypeAvailable) {
ToastService.showError(I18n.tr("wtype not available - install wtype for paste support")); ToastService.showError(I18n.tr("wtype not available - install wtype for paste support"));
return; return;
@@ -155,6 +192,20 @@ Item {
priority: 2, priority: 2,
defaultViewMode: "list" defaultViewMode: "list"
}, },
{
id: "settings",
title: I18n.tr("Settings", "settings window title"),
icon: "settings",
priority: 2.35,
defaultViewMode: "list"
},
{
id: "clipboard",
title: I18n.tr("Clipboard"),
icon: "content_paste",
priority: 2.45,
defaultViewMode: "list"
},
{ {
id: "browse_plugins", id: "browse_plugins",
title: I18n.tr("Browse"), title: I18n.tr("Browse"),
@@ -352,6 +403,9 @@ Item {
searchQuery = query; searchQuery = query;
searchDebounce.restart(); searchDebounce.restart();
if (searchMode === "all" && clipboardSearchEnabledInAll() && query.length >= 2)
ClipboardService.ensureLauncherHistory();
var filesInAll = searchMode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll); var filesInAll = searchMode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll);
if (searchMode !== "plugins" && (searchMode === "files" || query.startsWith("/") || filesInAll) && query.length > 0) { if (searchMode !== "plugins" && (searchMode === "files" || query.startsWith("/") || filesInAll) && query.length > 0) {
fileSearchDebounce.restart(); fileSearchDebounce.restart();
@@ -370,6 +424,8 @@ Item {
searchMode = mode; searchMode = mode;
modeChanged(mode); modeChanged(mode);
performSearch(); performSearch();
if (mode === "all" && clipboardSearchEnabledInAll() && searchQuery.length >= 2)
ClipboardService.ensureLauncherHistory();
var filesInAll = mode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll) && searchQuery.length > 0; var filesInAll = mode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll) && searchQuery.length > 0;
if (mode === "files" || filesInAll) { if (mode === "files" || filesInAll) {
fileSearchDebounce.restart(); fileSearchDebounce.restart();
@@ -612,7 +668,7 @@ Item {
if (triggerMatch.isBuiltIn) { if (triggerMatch.isBuiltIn) {
var builtInItems = AppSearchService.getBuiltInLauncherItems(triggerMatch.pluginId, triggerMatch.query); var builtInItems = AppSearchService.getBuiltInLauncherItems(triggerMatch.pluginId, triggerMatch.query);
for (var j = 0; j < builtInItems.length; j++) { for (var j = 0; j < builtInItems.length; j++) {
allItems.push(transformBuiltInLauncherItem(builtInItems[j], triggerMatch.pluginId)); allItems.push(transformBuiltInSearchItem(builtInItems[j], triggerMatch.pluginId));
} }
} }
@@ -748,7 +804,7 @@ Item {
var builtInItems = AppSearchService.getBuiltInLauncherItems(pluginFilter, searchQuery); var builtInItems = AppSearchService.getBuiltInLauncherItems(pluginFilter, searchQuery);
for (var j = 0; j < builtInItems.length; j++) { for (var j = 0; j < builtInItems.length; j++) {
allItems.push(transformBuiltInLauncherItem(builtInItems[j], pluginFilter)); allItems.push(transformBuiltInSearchItem(builtInItems[j], pluginFilter));
} }
} else { } else {
var emptyTriggerPlugins = getEmptyTriggerPlugins(); var emptyTriggerPlugins = getEmptyTriggerPlugins();
@@ -764,7 +820,7 @@ Item {
var pluginId = builtInLauncherPlugins[i]; var pluginId = builtInLauncherPlugins[i];
var blItems = AppSearchService.getBuiltInLauncherItems(pluginId, searchQuery); var blItems = AppSearchService.getBuiltInLauncherItems(pluginId, searchQuery);
for (var j = 0; j < blItems.length; j++) { for (var j = 0; j < blItems.length; j++) {
allItems.push(transformBuiltInLauncherItem(blItems[j], pluginId)); allItems.push(transformBuiltInSearchItem(blItems[j], pluginId));
} }
} }
} }
@@ -799,6 +855,7 @@ Item {
} }
if (searchMode === "all") { if (searchMode === "all") {
appendSharedAllResults(allItems, searchQuery);
if (searchQuery && searchQuery.length >= 2) { if (searchQuery && searchQuery.length >= 2) {
_pluginPhasePending = true; _pluginPhasePending = true;
_phase1Items = allItems.slice(); _phase1Items = allItems.slice();
@@ -814,7 +871,7 @@ Item {
if (plugin.isBuiltIn) { if (plugin.isBuiltIn) {
var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
for (var j = 0; j < blItems.length; j++) for (var j = 0; j < blItems.length; j++)
allItems.push(transformBuiltInLauncherItem(blItems[j], plugin.id)); allItems.push(transformBuiltInSearchItem(blItems[j], plugin.id));
} else { } else {
var pItems = getPluginItems(plugin.id, searchQuery); var pItems = getPluginItems(plugin.id, searchQuery);
for (var j = 0; j < pItems.length; j++) for (var j = 0; j < pItems.length; j++)
@@ -883,11 +940,13 @@ Item {
if (currentVersion !== _searchVersion) if (currentVersion !== _searchVersion)
return; return;
var plugin = allPluginsOrdered[i]; var plugin = allPluginsOrdered[i];
if (plugin.isBuiltIn && (plugin.id === "dms_settings_search" || plugin.id === "dms_clipboard_search"))
continue;
if (plugin.isBuiltIn) { if (plugin.isBuiltIn) {
var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery); var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
var blLimit = Math.min(blItems.length, maxPerPlugin); var blLimit = Math.min(blItems.length, maxPerPlugin);
for (var j = 0; j < blLimit; j++) { for (var j = 0; j < blLimit; j++) {
var item = transformBuiltInLauncherItem(blItems[j], plugin.id); var item = transformBuiltInSearchItem(blItems[j], plugin.id);
item._preScored = 900 - j; item._preScored = 900 - j;
allItems.push(item); allItems.push(item);
} }
@@ -1110,10 +1169,56 @@ Item {
return Transform.transformBuiltInLauncherItem(item, pluginId, I18n.tr("Open")); return Transform.transformBuiltInLauncherItem(item, pluginId, I18n.tr("Open"));
} }
function transformBuiltInSearchItem(item, pluginId) {
if (pluginId === "dms_clipboard_search" || item.type === "clipboard")
return transformClipboardEntry(item.data || item);
return transformBuiltInLauncherItem(item, pluginId);
}
function transformFileResult(file) { function transformFileResult(file) {
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"), I18n.tr("Open in terminal")); return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"), I18n.tr("Open in terminal"));
} }
function transformClipboardEntry(entry) {
var copyLabel = I18n.tr("Copy");
var pasteLabel = I18n.tr("Paste");
var primaryLabel = SettingsData.clipboardEnterToPaste ? pasteLabel : copyLabel;
var pasteHintLabel = SettingsData.clipboardEnterToPaste ? I18n.tr("Shift+Enter to copy") : I18n.tr("Shift+Enter to paste");
return Transform.transformClipboardItem(entry, copyLabel, pasteLabel, primaryLabel, I18n.tr("Image"), I18n.tr("Text"), I18n.tr("Pinned"), pasteHintLabel, "", I18n.tr("Clipboard"));
}
function builtInLauncherVisibleInAll(pluginId) {
return SettingsData.getBuiltInPluginSetting(pluginId, "enabled", true) && SettingsData.getPluginAllowWithoutTrigger(pluginId);
}
function clipboardSearchEnabledInAll() {
return builtInLauncherVisibleInAll("dms_clipboard_search") && ClipboardService.clipboardAvailable;
}
function appendSharedAllResults(allItems, query) {
if (!query || query.length < 2)
return;
if (builtInLauncherVisibleInAll("dms_settings_search")) {
var settingsItems = AppSearchService.getBuiltInLauncherItems("dms_settings_search", query);
var settingsLimit = Math.min(settingsItems.length, 8);
for (var i = 0; i < settingsLimit; i++) {
settingsItems[i]._preScored = 890 - i;
allItems.push(transformBuiltInSearchItem(settingsItems[i], "dms_settings_search"));
}
}
if (clipboardSearchEnabledInAll()) {
ClipboardService.ensureLauncherHistory();
var clipboardItems = AppSearchService.getBuiltInLauncherItems("dms_clipboard_search", query);
var clipboardLimit = Math.min(clipboardItems.length, 8);
for (var j = 0; j < clipboardLimit; j++) {
clipboardItems[j]._preScored = 840 - j;
allItems.push(transformBuiltInSearchItem(clipboardItems[j], "dms_clipboard_search"));
}
}
}
function detectTrigger(query) { function detectTrigger(query) {
if (!query || query.length === 0) if (!query || query.length === 0)
return { return {
@@ -1308,7 +1413,9 @@ Item {
} }
function buildDynamicSectionDefs(items) { function buildDynamicSectionDefs(items) {
var baseDefs = sectionDefinitions.slice(); var baseDefs = sectionDefinitions.map(function (def) {
return Object.assign({}, def);
});
var pluginSections = {}; var pluginSections = {};
var order = SettingsData.launcherPluginOrder || []; var order = SettingsData.launcherPluginOrder || [];
var orderMap = {}; var orderMap = {};
@@ -1316,6 +1423,12 @@ Item {
orderMap[order[k]] = k; orderMap[order[k]] = k;
var unorderedPriority = 2.6 + order.length * 0.01; var unorderedPriority = 2.6 + order.length * 0.01;
for (var d = 0; d < baseDefs.length; d++) {
var virtualId = baseDefs[d].id === "settings" ? "dms_settings_search" : baseDefs[d].id === "clipboard" ? "dms_clipboard_search" : "";
if (virtualId && orderMap[virtualId] !== undefined)
baseDefs[d].priority = 2.6 + orderMap[virtualId] * 0.01;
}
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var section = items[i].section; var section = items[i].section;
if (!section || !section.startsWith("plugin_")) if (!section || !section.startsWith("plugin_"))
@@ -1768,6 +1881,20 @@ Item {
AppSearchService.executePluginItem(item.data, item.pluginId); AppSearchService.executePluginItem(item.data, item.pluginId);
} }
break; break;
case "setting":
AppSearchService.executeBuiltInLauncherItem(item.data);
break;
case "clipboard":
if (SettingsData.clipboardEnterToPaste) {
ClipboardService.pasteEntry(item.data, function () {
root.itemExecuted();
});
} else {
ClipboardService.copyEntry(item.data, function () {
root.itemExecuted();
});
}
return;
case "file": case "file":
openFile(item.data?.path); openFile(item.data?.path);
break; break;
@@ -1803,6 +1930,16 @@ Item {
case "execute": case "execute":
executeItem(item); executeItem(item);
break; break;
case "clipboard_copy":
ClipboardService.copyEntry(item.data, function () {
root.itemExecuted();
});
return;
case "clipboard_paste":
ClipboardService.pasteEntry(item.data, function () {
root.itemExecuted();
});
return;
case "launch_dgpu": case "launch_dgpu":
if (item.type === "app" && item.data) { if (item.type === "app" && item.data) {
launchAppWithNvidia(item.data); launchAppWithNvidia(item.data);
@@ -94,17 +94,19 @@ function transformBuiltInLauncherItem(item, pluginId, openLabel) {
return { return {
id: item.action || "", id: item.action || "",
type: "plugin", type: item.type || "plugin",
name: item.name || "", name: item.name || "",
subtitle: item.comment || "", subtitle: item.comment || "",
icon: icon, icon: icon,
iconType: iconType, iconType: iconType,
section: "plugin_" + pluginId, section: item.section || ("plugin_" + pluginId),
data: item, data: item.data || item,
pluginId: pluginId, pluginId: pluginId,
isBuiltInLauncher: true, isBuiltInLauncher: true,
keywords: item.keywords || [], keywords: item.keywords || [],
actions: [], actions: [],
source: item.source || "",
badgeLabel: item.badgeLabel || "",
primaryAction: { primaryAction: {
name: openLabel, name: openLabel,
icon: "open_in_new", icon: "open_in_new",
@@ -117,6 +119,58 @@ function transformBuiltInLauncherItem(item, pluginId, openLabel) {
}; };
} }
function transformClipboardItem(entry, copyLabel, pasteLabel, primaryLabel, imageLabel, textLabel, pinnedLabel, pasteHintLabel, copyHintLabel, clipboardLabel) {
var preview = (entry.preview || "").toString().replace(/\s+/g, " ").trim();
var isImage = entry.isImage || false;
var title = preview.length > 0 ? preview : imageLabel;
if (title.length > 140)
title = title.substring(0, 137) + "...";
var typeLabel = isImage ? imageLabel : textLabel;
var subtitle = typeLabel;
if (entry.pinned)
subtitle = pinnedLabel + " - " + subtitle;
if (pasteHintLabel)
subtitle += " - " + pasteHintLabel;
if (copyHintLabel)
subtitle += " - " + copyHintLabel;
return {
id: "clipboard_" + (entry.id || entry.hash || title),
type: "clipboard",
name: title,
subtitle: subtitle,
icon: isImage ? "image" : "content_paste",
iconType: "material",
section: "clipboard",
data: entry,
keywords: [preview, isImage ? "image" : "text", "clipboard"],
actions: [
{
name: copyLabel,
icon: "content_copy",
action: "clipboard_copy"
},
{
name: pasteLabel,
icon: "content_paste",
action: "clipboard_paste"
}
],
source: clipboardLabel,
badgeLabel: clipboardLabel,
primaryAction: {
name: primaryLabel,
icon: primaryLabel === pasteLabel ? "content_paste" : "content_copy",
action: primaryLabel === pasteLabel ? "clipboard_paste" : "clipboard_copy"
},
_hName: "",
_hSub: "",
_hRich: false,
_preScored: entry._preScored
};
}
function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel, openTerminalLabel) { function transformFileResult(file, openLabel, openFolderLabel, copyPathLabel, openTerminalLabel) {
var filename = file.path ? file.path.split("/").pop() : ""; var filename = file.path ? file.path.split("/").pop() : "";
var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : ""; var dirname = file.path ? file.path.substring(0, file.path.lastIndexOf("/")) : "";
@@ -32,6 +32,8 @@ Popup {
var actions = instance.getContextMenuActions(spotlightItem.data); var actions = instance.getContextMenuActions(spotlightItem.data);
return Array.isArray(actions) && actions.length > 0; return Array.isArray(actions) && actions.length > 0;
} }
if (spotlightItem.actions && spotlightItem.actions.length > 0)
return true;
return false; return false;
} }
@@ -80,6 +82,13 @@ Popup {
hide(); hide();
} }
function executeLauncherAction(actionData) {
if (!controller || !item || !actionData)
return;
controller.executeAction(item, actionData);
hide();
}
readonly property var menuItems: { readonly property var menuItems: {
var items = []; var items = [];
@@ -97,6 +106,19 @@ Popup {
return items; return items;
} }
if (item?.type !== "app" && item?.actions && item.actions.length > 0) {
for (var i = 0; i < item.actions.length; i++) {
var genericAct = item.actions[i];
items.push({
type: "item",
icon: genericAct.icon || "play_arrow",
text: genericAct.name || "",
launcherActionData: genericAct
});
}
return items;
}
if (item?.type === "app") { if (item?.type === "app") {
items.push({ items.push({
type: "item", type: "item",
@@ -293,6 +315,8 @@ Popup {
menuItem.action(); menuItem.action();
else if (menuItem.pluginAction) else if (menuItem.pluginAction)
executePluginAction(menuItem.pluginAction); executePluginAction(menuItem.pluginAction);
else if (menuItem.launcherActionData)
executeLauncherAction(menuItem.launcherActionData);
else if (menuItem.actionData) else if (menuItem.actionData)
executeDesktopAction(menuItem.actionData); executeDesktopAction(menuItem.actionData);
return; return;
@@ -500,6 +524,8 @@ Popup {
menuItem.action(); menuItem.action();
else if (menuItem.pluginAction) else if (menuItem.pluginAction)
root.executePluginAction(menuItem.pluginAction); root.executePluginAction(menuItem.pluginAction);
else if (menuItem.launcherActionData)
root.executeLauncherAction(menuItem.launcherActionData);
else if (menuItem.actionData) else if (menuItem.actionData)
root.executeDesktopAction(menuItem.actionData); root.executeDesktopAction(menuItem.actionData);
} }
@@ -33,6 +33,7 @@ Rectangle {
return item.icon || ""; return item.icon || "";
} }
} }
readonly property bool hasClipboardPreview: item?.type === "clipboard" && item?.data?.isImage === true && (item?.data?.mimeType ?? "").startsWith("image/")
width: parent?.width ?? 200 width: parent?.width ?? 200
height: 52 height: 52
@@ -154,6 +155,14 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS spacing: Theme.spacingS
ClipboardLauncherPreview {
width: root.hasClipboardPreview ? 56 : 0
height: 36
visible: root.hasClipboardPreview
anchors.verticalCenter: parent.verticalCenter
entry: root.item?.data ?? null
}
Rectangle { Rectangle {
id: allModeToggle id: allModeToggle
visible: root.item?.type === "plugin_browse" visible: root.item?.type === "plugin_browse"
@@ -208,9 +217,15 @@ Rectangle {
text: { text: {
if (!root.item) if (!root.item)
return ""; return "";
if ((root.item.badgeLabel ?? "").length > 0)
return root.item.badgeLabel;
switch (root.item.type) { switch (root.item.type) {
case "plugin": case "plugin":
return I18n.tr("Plugin"); return I18n.tr("Plugin");
case "setting":
return I18n.tr("Setting");
case "clipboard":
return I18n.tr("Clipboard");
case "file": case "file":
return root.item.data?.is_dir ? I18n.tr("Folder") : I18n.tr("File"); return root.item.data?.is_dir ? I18n.tr("Folder") : I18n.tr("File");
default: default:
@@ -10,6 +10,8 @@ const Weights = {
typeBonus: { typeBonus: {
app: 1000, app: 1000,
plugin: 900, plugin: 900,
setting: 850,
clipboard: 825,
file: 800, file: 800,
action: 600 action: 600
} }
@@ -48,17 +48,24 @@ Rectangle {
return "file://" + raw; return "file://" + raw;
return raw; return raw;
} }
readonly property bool hasMediaPreview: previewSource.length > 0 readonly property bool hasClipboardPreview: item?.type === "clipboard" && item?.data?.isImage === true && (item?.data?.mimeType ?? "").startsWith("image/")
readonly property bool hasMediaPreview: previewSource.length > 0 || hasClipboardPreview
readonly property bool previewAnimated: previewSource.toLowerCase().indexOf(".gif") >= 0 readonly property bool previewAnimated: previewSource.toLowerCase().indexOf(".gif") >= 0
readonly property string typeLabel: { readonly property string typeLabel: {
if (!item) if (!item)
return ""; return "";
if ((item.badgeLabel ?? "").length > 0)
return item.badgeLabel;
switch (item.type) { switch (item.type) {
case "plugin_browse": case "plugin_browse":
return I18n.tr("Browse"); return I18n.tr("Browse");
case "plugin": case "plugin":
return I18n.tr("Plugin"); return I18n.tr("Plugin");
case "setting":
return I18n.tr("Setting");
case "clipboard":
return I18n.tr("Clipboard");
case "file": case "file":
return item.data?.is_dir ? I18n.tr("Folder") : I18n.tr("File"); return item.data?.is_dir ? I18n.tr("Folder") : I18n.tr("File");
default: default:
@@ -278,7 +285,7 @@ Rectangle {
source: root.previewSource source: root.previewSource
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: !root.previewAnimated visible: !root.hasClipboardPreview && !root.previewAnimated
} }
AnimatedImage { AnimatedImage {
@@ -286,7 +293,13 @@ Rectangle {
source: root.previewSource source: root.previewSource
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
playing: visible playing: visible
visible: root.previewAnimated visible: !root.hasClipboardPreview && root.previewAnimated
}
ClipboardLauncherPreview {
anchors.fill: parent
entry: root.item?.data ?? null
visible: root.hasClipboardPreview
} }
} }
+1 -1
View File
@@ -565,7 +565,7 @@ Item {
spacing: Theme.spacingS spacing: Theme.spacingS
Repeater { Repeater {
model: ["dms_settings", "dms_notepad", "dms_sysmon", "dms_settings_search"] model: ["dms_settings", "dms_notepad", "dms_sysmon", "dms_settings_search", "dms_clipboard_search"]
delegate: Rectangle { delegate: Rectangle {
id: pluginDelegate id: pluginDelegate
+28 -3
View File
@@ -211,11 +211,21 @@ Singleton {
}, },
"dms_settings_search": { "dms_settings_search": {
id: "dms_settings_search", id: "dms_settings_search",
name: I18n.tr("Settings", "settings window title"), name: I18n.tr("Settings Search"),
cornerIcon: "search", cornerIcon: "search",
comment: "DMS", comment: I18n.tr("DMS Settings"),
defaultTrigger: "?", defaultTrigger: "?",
isLauncher: true isLauncher: true
},
"dms_clipboard_search": {
id: "dms_clipboard_search",
name: I18n.tr("Clipboard"),
cornerIcon: "content_paste",
comment: "DMS",
defaultTrigger: "cb",
isLauncher: true,
viewMode: "list",
viewModeEnforced: true
} }
}) })
@@ -285,6 +295,16 @@ Singleton {
} }
function getBuiltInLauncherItems(pluginId, query) { function getBuiltInLauncherItems(pluginId, query) {
if (pluginId === "dms_clipboard_search") {
ClipboardService.ensureLauncherHistory();
const trimmed = (query || "").toString().trim();
const entries = trimmed.length === 0 ? ClipboardService.getRecentLauncherEntries(20) : ClipboardService.getLauncherEntries(trimmed, 20, 1);
return entries.map(entry => ({
type: "clipboard",
data: entry
}));
}
if (pluginId !== "dms_settings_search") if (pluginId !== "dms_settings_search")
return []; return [];
@@ -295,10 +315,15 @@ Singleton {
const r = results[i]; const r = results[i];
items.push({ items.push({
name: r.label, name: r.label,
type: "setting",
section: "settings",
icon: "material:" + r.icon, icon: "material:" + r.icon,
comment: r.category, comment: r.description || r.category,
action: "settings_nav:" + r.tabIndex + ":" + r.section, action: "settings_nav:" + r.tabIndex + ":" + r.section,
categories: ["Settings"], categories: ["Settings"],
keywords: r.keywords || [],
source: I18n.tr("Settings", "settings window title"),
badgeLabel: I18n.tr("Setting"),
isCore: true, isCore: true,
isBuiltInLauncher: true, isBuiltInLauncher: true,
builtInPluginId: pluginId builtInPluginId: pluginId
+63
View File
@@ -26,6 +26,7 @@ Singleton {
property int selectedIndex: 0 property int selectedIndex: 0
property bool keyboardNavigationActive: false property bool keyboardNavigationActive: false
property int refCount: 0 property int refCount: 0
property real _launcherLastRefresh: 0
signal historyCopied signal historyCopied
signal historyCleared signal historyCleared
@@ -90,6 +91,68 @@ Singleton {
}); });
} }
function ensureLauncherHistory() {
if (!clipboardAvailable) {
return;
}
const now = Date.now();
if (internalEntries.length === 0 || now - _launcherLastRefresh > 5000) {
_launcherLastRefresh = now;
refresh();
}
}
function getLauncherEntries(query, limit, minLength) {
if (!clipboardAvailable) {
return [];
}
const trimmed = (query || "").toString().trim();
const requiredLength = minLength !== undefined ? minLength : 2;
if (trimmed.length < requiredLength) {
return [];
}
const lowerQuery = trimmed.toLowerCase();
const maxItems = limit > 0 ? limit : 8;
const matches = [];
for (var i = 0; i < internalEntries.length; i++) {
const entry = internalEntries[i];
const preview = getEntryPreview(entry).toString();
const typeText = entry.isImage ? "image picture screenshot clipboard" : "text clipboard";
const haystack = (preview + " " + typeText).toLowerCase();
if (haystack.indexOf(lowerQuery) === -1) {
continue;
}
matches.push(entry);
}
matches.sort((a, b) => {
if (a.pinned !== b.pinned)
return b.pinned ? 1 : -1;
return (b.id || 0) - (a.id || 0);
});
return matches.slice(0, maxItems);
}
function getRecentLauncherEntries(limit) {
if (!clipboardAvailable) {
return [];
}
const maxItems = limit > 0 ? limit : 20;
const entries = internalEntries.slice();
entries.sort((a, b) => {
if (a.pinned !== b.pinned)
return b.pinned ? 1 : -1;
return (b.id || 0) - (a.id || 0);
});
return entries.slice(0, maxItems);
}
function reset() { function reset() {
searchText = ""; searchText = "";
selectedIndex = 0; selectedIndex = 0;