quickshell emoji picker

This commit is contained in:
end-4
2025-06-02 00:09:47 +02:00
parent 751e5ca543
commit c940b72776
7 changed files with 119 additions and 4 deletions
@@ -84,7 +84,8 @@ Singleton {
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
property QtObject prefix: QtObject {
property string action: "/"
property string clipboard: ":"
property string clipboard: ";"
property string emojis: ":"
}
}
@@ -210,4 +210,27 @@ Scope {
}
}
GlobalShortcut {
name: "overviewEmojiToggle"
description: qsTr("Toggle emoji query on overview widget")
onPressed: {
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
GlobalStates.overviewOpen = false;
return;
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
ConfigOptions.search.prefix.emojis
);
GlobalStates.overviewOpen = true;
return
}
}
}
}
}
@@ -24,6 +24,7 @@ RippleButton {
property var itemExecute: entry?.execute
property string fontType: entry?.fontType ?? "main"
property string itemClickActionName: entry?.clickActionName
property string bigText: entry?.bigText ?? ""
property string materialSymbol: entry?.materialSymbol ?? ""
property string cliphistRawString: entry?.cliphistRawString ?? ""
@@ -120,6 +121,7 @@ RippleButton {
id: iconLoader
active: true
sourceComponent: root.materialSymbol !== "" ? materialSymbolComponent :
root.bigText ? bigTextComponent :
root.itemIcon !== "" ? iconImageComponent :
null
}
@@ -142,6 +144,15 @@ RippleButton {
}
}
Component {
id: bigTextComponent
StyledText {
text: root.bigText
font.pixelSize: Appearance.font.pixelSize.larger
color: Appearance.m3colors.m3onSurface
}
}
// Main text
ColumnLayout {
id: contentColumn
@@ -302,7 +302,22 @@ Item { // Wrapper
}
};
}).filter(Boolean);
}
}
if (root.searchingText.startsWith(ConfigOptions.search.prefix.emojis)) { // Clipboard
const searchString = root.searchingText.slice(ConfigOptions.search.prefix.emojis.length);
return Emojis.fuzzyQuery(searchString).map(entry => {
return {
cliphistRawString: entry,
bigText: entry.match(/^\s*(\S+)/)?.[1] || "",
name: entry.replace(/^\s*\S+\s+/, ""),
clickActionName: "",
type: "Emoji",
execute: () => {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(entry.match(/^\s*(\S+)/)?.[1])}'`);
}
};
}).filter(Boolean);
}
////////////////// Init ///////////////////
-1
View File
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
import "root:/modules/common/functions/levendist.js" as Levendist
import "root:/modules/common/functions/string_utils.js" as StringUtils
import "root:/modules/common"
import "root:/"
import QtQuick
+65
View File
@@ -0,0 +1,65 @@
pragma Singleton
pragma ComponentBehavior: Bound
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
import "root:/modules/common/functions/levendist.js" as Levendist
import "root:/modules/common"
import QtQuick
import Quickshell
import Quickshell.Io
/**
* Emojis.
*/
Singleton {
id: root
property string emojiScriptPath: `${Directories.config}/hypr/hyprland/scripts/fuzzel-emoji.sh`
property string lineBeforeData: "### DATA ###"
property list<var> list
readonly property var preparedEntries: list.map(a => ({
name: Fuzzy.prepare(`${a}`),
entry: a
}))
function fuzzyQuery(search: string): var {
if (root.sloppySearch) {
const results = entries.slice(0, 100).map(str => ({
entry: str,
score: Levendist.computeTextMatchScore(str.toLowerCase(), search.toLowerCase())
})).filter(item => item.score > root.scoreThreshold)
.sort((a, b) => b.score - a.score)
return results
.map(item => item.entry)
}
return Fuzzy.go(search, preparedEntries, {
all: true,
key: "name"
}).map(r => {
return r.obj.entry
});
}
function load() {
emojiFileView.reload()
}
function updateEmojis(fileContent) {
const lines = fileContent.split("\n")
const dataIndex = lines.indexOf(root.lineBeforeData)
if (dataIndex === -1) {
console.warn("No data section found in emoji script file.")
return
}
const emojis = lines.slice(dataIndex + 1).filter(line => line.trim() !== "")
root.list = emojis.map(line => line.trim())
}
FileView {
id: emojiFileView
path: Qt.resolvedUrl(root.emojiScriptPath)
onLoadedChanged: {
const fileContent = emojiFileView.text()
root.updateEmojis(fileContent)
}
}
}