mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
launcher search service: use proper type instead of json clumps
This commit is contained in:
@@ -590,8 +590,9 @@ Singleton {
|
||||
// false will make (some) stuff also be like that for accuracy.
|
||||
// Example: the right-click menu of the Start button
|
||||
property JsonObject tweaks: JsonObject {
|
||||
property bool smootherMenuAnimations: true
|
||||
property bool switchHandlePositionFix: true
|
||||
property bool smootherMenuAnimations: true
|
||||
property bool smootherSearchBar: true
|
||||
}
|
||||
property JsonObject bar: JsonObject {
|
||||
property bool bottom: true
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
QtObject {
|
||||
enum IconType { Material, Text, System, None }
|
||||
enum FontType { Normal, Monospace }
|
||||
|
||||
// General stuff
|
||||
property string type: ""
|
||||
property var fontType: LauncherSearchResult.FontType.Normal
|
||||
property string name: ""
|
||||
property string rawValue: ""
|
||||
property string iconName: ""
|
||||
property var iconType: LauncherSearchResult.IconType.None
|
||||
property string verb: ""
|
||||
property bool blurImage: false
|
||||
property var execute: () => {
|
||||
print("Not implemented");
|
||||
}
|
||||
property var actions: []
|
||||
|
||||
// Stuff needed for DesktopEntry objects
|
||||
property bool shown: true
|
||||
property string comment: ""
|
||||
property bool runInTerminal: false
|
||||
property string genericName: ""
|
||||
property list<string> keywords: []
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
@@ -12,20 +13,27 @@ import Quickshell.Hyprland
|
||||
|
||||
RippleButton {
|
||||
id: root
|
||||
property var entry
|
||||
property LauncherSearchResult entry
|
||||
property string query
|
||||
property bool entryShown: entry?.shown ?? true
|
||||
property string itemType: entry?.type ?? Translation.tr("App")
|
||||
property string itemName: entry?.name ?? ""
|
||||
property string itemIcon: entry?.icon ?? ""
|
||||
property var iconType: entry?.iconType
|
||||
property string iconName: entry?.iconName ?? ""
|
||||
property var itemExecute: entry?.execute
|
||||
property string fontType: entry?.fontType ?? "main"
|
||||
property string itemClickActionName: entry?.clickActionName ?? "Open"
|
||||
property string bigText: entry?.bigText ?? ""
|
||||
property string materialSymbol: entry?.materialSymbol ?? ""
|
||||
property string cliphistRawString: entry?.cliphistRawString ?? ""
|
||||
property var fontType: switch(entry?.fontType) {
|
||||
case LauncherSearchResult.FontType.Monospace:
|
||||
return "monospace"
|
||||
case LauncherSearchResult.FontType.Normal:
|
||||
return "main"
|
||||
default:
|
||||
return "main"
|
||||
}
|
||||
property string itemClickActionName: entry?.verb ?? "Open"
|
||||
property string bigText: entry?.iconType === LauncherSearchResult.IconType.Text ? entry?.iconName ?? "" : ""
|
||||
property string materialSymbol: entry.iconType === LauncherSearchResult.IconType.Material ? entry?.iconName ?? "" : ""
|
||||
property string cliphistRawString: entry?.rawValue ?? ""
|
||||
property bool blurImage: entry?.blurImage ?? false
|
||||
property string blurImageText: entry?.blurImageText ?? "Image hidden"
|
||||
|
||||
visible: root.entryShown
|
||||
property int horizontalMargin: 10
|
||||
@@ -97,7 +105,7 @@ RippleButton {
|
||||
}
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Delete && event.modifiers === Qt.ShiftModifier) {
|
||||
const deleteAction = root.entry.actions.find(action => action.name == "Delete");
|
||||
const deleteAction = root.entry.actions.find(action => action.name == Translation.tr("Delete"));
|
||||
|
||||
if (deleteAction) {
|
||||
deleteAction.execute()
|
||||
@@ -126,16 +134,24 @@ RippleButton {
|
||||
Loader {
|
||||
id: iconLoader
|
||||
active: true
|
||||
sourceComponent: root.materialSymbol !== "" ? materialSymbolComponent :
|
||||
root.bigText ? bigTextComponent :
|
||||
root.itemIcon !== "" ? iconImageComponent :
|
||||
null
|
||||
sourceComponent: switch(root.iconType) {
|
||||
case LauncherSearchResult.IconType.Material:
|
||||
return materialSymbolComponent
|
||||
case LauncherSearchResult.IconType.Text:
|
||||
return bigTextComponent
|
||||
case LauncherSearchResult.IconType.System:
|
||||
return iconImageComponent
|
||||
case LauncherSearchResult.IconType.None:
|
||||
return null
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconImageComponent
|
||||
IconImage {
|
||||
source: Quickshell.iconPath(root.itemIcon, "image-missing")
|
||||
source: Quickshell.iconPath(root.iconName, "image-missing")
|
||||
width: 35
|
||||
height: 35
|
||||
}
|
||||
@@ -217,7 +233,6 @@ RippleButton {
|
||||
maxWidth: contentColumn.width
|
||||
maxHeight: 140
|
||||
blur: root.blurImage
|
||||
blurText: root.blurImageText
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,8 +258,8 @@ RippleButton {
|
||||
delegate: RippleButton {
|
||||
id: actionButton
|
||||
required property var modelData
|
||||
property string iconName: modelData.icon ?? ""
|
||||
property string materialIconName: modelData.materialIcon ?? ""
|
||||
property var iconType: modelData.iconType
|
||||
property string iconName: modelData.iconName ?? ""
|
||||
implicitHeight: 34
|
||||
implicitWidth: 34
|
||||
|
||||
@@ -256,16 +271,16 @@ RippleButton {
|
||||
anchors.centerIn: parent
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: !(actionButton.iconName !== "") || actionButton.materialIconName
|
||||
active: actionButton.iconType === LauncherSearchResult.IconType.Material || actionButton.iconName === ""
|
||||
sourceComponent: MaterialSymbol {
|
||||
text: actionButton.materialIconName || "video_settings"
|
||||
text: actionButton.iconName || "video_settings"
|
||||
font.pixelSize: Appearance.font.pixelSize.hugeass
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: actionButton.materialIconName.length == 0 && actionButton.iconName && actionButton.iconName !== ""
|
||||
active: actionButton.iconType === LauncherSearchResult.IconType.System && actionButton.iconName !== ""
|
||||
sourceComponent: IconImage {
|
||||
source: Quickshell.iconPath(actionButton.iconName)
|
||||
implicitSize: 20
|
||||
|
||||
@@ -42,12 +42,6 @@ Item { // Wrapper
|
||||
LauncherSearch.query = text;
|
||||
}
|
||||
|
||||
function containsUnsafeLink(entry) {
|
||||
if (entry == undefined) return false;
|
||||
const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords;
|
||||
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
// Prevent Esc and Backspace from registering
|
||||
if (event.key === Qt.Key_Escape)
|
||||
|
||||
@@ -4,7 +4,6 @@ import qs.modules.common
|
||||
import qs.modules.common.models
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
@@ -12,6 +11,15 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property string query: ""
|
||||
|
||||
function ensurePrefix(prefix) {
|
||||
if ([Config.options.search.prefix.action, Config.options.search.prefix.app, Config.options.search.prefix.clipboard, Config.options.search.prefix.emojis, Config.options.search.prefix.math, Config.options.search.prefix.shellCommand, Config.options.search.prefix.webSearch,].some(i => root.query.startsWith(i))) {
|
||||
root.query = prefix + root.query.slice(1);
|
||||
} else {
|
||||
root.query = prefix + root.query;
|
||||
}
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
{
|
||||
action: "accentcolor",
|
||||
@@ -74,12 +82,13 @@ Singleton {
|
||||
property string mathResult: ""
|
||||
property bool clipboardWorkSafetyActive: {
|
||||
const enabled = Config.options.workSafety.enable.clipboard;
|
||||
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
|
||||
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords));
|
||||
return enabled && sensitiveNetwork;
|
||||
}
|
||||
|
||||
function containsUnsafeLink(entry) {
|
||||
if (entry == undefined) return false;
|
||||
if (entry == undefined)
|
||||
return false;
|
||||
const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords;
|
||||
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
|
||||
}
|
||||
@@ -128,95 +137,121 @@ Singleton {
|
||||
shouldBlurImage = shouldBlurImage && (root.containsUnsafeLink(array[index - 1]) || root.containsUnsafeLink(array[index + 1]));
|
||||
}
|
||||
const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`;
|
||||
return {
|
||||
key: type,
|
||||
cliphistRawString: entry,
|
||||
return resultComp.createObject(null, {
|
||||
rawValue: entry,
|
||||
name: StringUtils.cleanCliphistEntry(entry),
|
||||
clickActionName: "",
|
||||
verb: "",
|
||||
type: type,
|
||||
execute: () => {
|
||||
Cliphist.copy(entry);
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
name: "Copy",
|
||||
materialIcon: "content_copy",
|
||||
actions: [resultComp.createObject(null, {
|
||||
name: Translation.tr("Copy"),
|
||||
iconName: "content_copy",
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
Cliphist.copy(entry);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Delete",
|
||||
materialIcon: "delete",
|
||||
}), resultComp.createObject(null, {
|
||||
name: Translation.tr("Delete"),
|
||||
iconName: "delete",
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
Cliphist.deleteEntry(entry);
|
||||
}
|
||||
}
|
||||
],
|
||||
blurImage: shouldBlurImage,
|
||||
blurImageText: Translation.tr("Work safety")
|
||||
};
|
||||
})],
|
||||
blurImage: shouldBlurImage
|
||||
});
|
||||
}).filter(Boolean);
|
||||
} else if (root.query.startsWith(Config.options.search.prefix.emojis)) {
|
||||
// Clipboard
|
||||
const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.emojis);
|
||||
return Emojis.fuzzyQuery(searchString).map(entry => {
|
||||
const emoji = entry.match(/^\s*(\S+)/)?.[1] || "";
|
||||
return {
|
||||
key: emoji,
|
||||
cliphistRawString: entry,
|
||||
bigText: emoji,
|
||||
return resultComp.createObject(null, {
|
||||
rawValue: entry,
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: "Emoji",
|
||||
iconName: emoji,
|
||||
iconType: LauncherSearchResult.IconType.Text,
|
||||
verb: Translation.tr("Copy"),
|
||||
type: Translation.tr("Emoji"),
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
|
||||
}
|
||||
};
|
||||
});
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
////////////////// Init ///////////////////
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
key: `Math result: ${root.mathResult}`,
|
||||
const mathResultObject = resultComp.createObject(null, {
|
||||
name: root.mathResult,
|
||||
clickActionName: Translation.tr("Copy"),
|
||||
verb: Translation.tr("Copy"),
|
||||
type: Translation.tr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
fontType: LauncherSearchResult.FontType.Monospace,
|
||||
iconName: 'calculate',
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = root.mathResult;
|
||||
}
|
||||
};
|
||||
const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => {
|
||||
entry.clickActionName = Translation.tr("Launch");
|
||||
entry.type = Translation.tr("App");
|
||||
entry.key = entry.execute;
|
||||
return entry;
|
||||
});
|
||||
const commandResultObject = {
|
||||
key: `cmd ${root.query}`,
|
||||
const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => {
|
||||
return resultComp.createObject(null, {
|
||||
type: Translation.tr("App"),
|
||||
name: entry.name,
|
||||
iconName: entry.icon,
|
||||
iconType: LauncherSearchResult.IconType.System,
|
||||
verb: Translation.tr("Launch"),
|
||||
execute: () => {
|
||||
if (!entry.runInTerminal)
|
||||
entry.execute();
|
||||
else {
|
||||
// Probably needs more proper escaping, but this will do for now
|
||||
Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(entry.command.join(' '))}'`]);
|
||||
}
|
||||
},
|
||||
comment: entry.comment,
|
||||
runInTerminal: entry.runInTerminal,
|
||||
genericName: entry.genericName,
|
||||
keywords: entry.keywords,
|
||||
actions: entry.actions.map(action => {
|
||||
return resultComp.createObject(null, {
|
||||
name: action.name,
|
||||
iconName: action.icon,
|
||||
iconType: LauncherSearchResult.IconType.System,
|
||||
execute: () => {
|
||||
if (!action.runInTerminal)
|
||||
action.execute();
|
||||
else {
|
||||
Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(action.command.join(' '))}'`]);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
const commandResultObject = resultComp.createObject(null, {
|
||||
name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""),
|
||||
clickActionName: Translation.tr("Run"),
|
||||
verb: Translation.tr("Run"),
|
||||
type: Translation.tr("Run command"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'terminal',
|
||||
fontType: LauncherSearchResult.FontType.Monospace,
|
||||
iconName: 'terminal',
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
let cleanedCommand = root.query.replace("file://", "");
|
||||
cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand);
|
||||
if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) {
|
||||
cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length);
|
||||
}
|
||||
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
|
||||
Quickshell.execDetached(["bash", "-c", root.query.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
|
||||
}
|
||||
};
|
||||
const webSearchResultObject = {
|
||||
key: `website ${root.query}`,
|
||||
});
|
||||
const webSearchResultObject = resultComp.createObject(null, {
|
||||
name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch),
|
||||
clickActionName: Translation.tr("Search"),
|
||||
verb: Translation.tr("Search"),
|
||||
type: Translation.tr("Search the web"),
|
||||
materialSymbol: 'travel_explore',
|
||||
iconName: 'travel_explore',
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
let query = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch);
|
||||
let url = Config.options.search.engineBaseUrl + query;
|
||||
@@ -225,20 +260,20 @@ Singleton {
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
};
|
||||
});
|
||||
const launcherActionObjects = root.searchActions.map(action => {
|
||||
const actionString = `${Config.options.search.prefix.action}${action.action}`;
|
||||
if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) {
|
||||
return {
|
||||
key: `Action ${actionString}`,
|
||||
return resultComp.createObject(null, {
|
||||
name: root.query.startsWith(actionString) ? root.query : actionString,
|
||||
clickActionName: Translation.tr("Run"),
|
||||
verb: Translation.tr("Run"),
|
||||
type: Translation.tr("Action"),
|
||||
materialSymbol: 'settings_suggest',
|
||||
iconName: 'settings_suggest',
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
action.execute(root.query.split(" ").slice(1).join(" "));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
@@ -275,4 +310,9 @@ Singleton {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Component {
|
||||
id: resultComp
|
||||
LauncherSearchResult {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user