mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
quickshell clipboard
This commit is contained in:
@@ -17,6 +17,7 @@ bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hi
|
||||
bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
|
||||
bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
|
||||
bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
|
||||
bindd = Super, Tab, Toggle overview, global, quickshell:overviewToggle # [hidden] Toggle overview/launcher (alt)
|
||||
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
|
||||
bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # Toggle left sidebar
|
||||
@@ -47,7 +48,7 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi
|
||||
|
||||
##! Utilities
|
||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||
bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard
|
||||
bindd = Super, V, Copy clipboard history entry, exec, qs ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard
|
||||
bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Emoji
|
||||
bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard
|
||||
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate
|
||||
|
||||
@@ -62,6 +62,7 @@ Singleton {
|
||||
property list<string> excludedSites: [ "quora.com" ]
|
||||
property QtObject prefix: QtObject {
|
||||
property string action: "/"
|
||||
property string clipboard: ":"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: overviewScope
|
||||
property bool dontAutoCancelSearch: false
|
||||
Variants {
|
||||
id: overviewVariants
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: root
|
||||
@@ -50,7 +53,14 @@ Scope {
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
delayedGrabTimer.start()
|
||||
if (!GlobalStates.overviewOpen) {
|
||||
overviewScope.dontAutoCancelSearch = false;
|
||||
} else {
|
||||
if (!overviewScope.dontAutoCancelSearch) {
|
||||
searchWidget.cancelSearch()
|
||||
}
|
||||
delayedGrabTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +77,10 @@ Scope {
|
||||
implicitWidth: columnLayout.width
|
||||
implicitHeight: columnLayout.height
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchWidget.setSearchingText(text);
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
@@ -84,6 +98,7 @@ Scope {
|
||||
}
|
||||
|
||||
SearchWidget {
|
||||
id: searchWidget
|
||||
panelWindow: root
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onSearchingTextChanged: (text) => {
|
||||
@@ -163,5 +178,27 @@ Scope {
|
||||
GlobalStates.superReleaseMightTrigger = false
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClipboardToggle"
|
||||
description: qsTr("Toggle clipboard query on overview widget")
|
||||
|
||||
onPressed: {
|
||||
if (GlobalStates.overviewOpen) {
|
||||
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.clipboard
|
||||
);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ RippleButton {
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
spacing: 10
|
||||
spacing: iconLoader.sourceComponent === null ? 0 : 10
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: root.horizontalMargin + root.buttonHorizontalPadding
|
||||
anchors.rightMargin: root.horizontalMargin + root.buttonHorizontalPadding
|
||||
@@ -76,7 +76,9 @@ RippleButton {
|
||||
Loader {
|
||||
id: iconLoader
|
||||
active: true
|
||||
sourceComponent: root.materialSymbol == "" ? iconImageComponent : materialSymbolComponent
|
||||
sourceComponent: root.materialSymbol !== "" ? materialSymbolComponent :
|
||||
root.itemIcon !== "" ? iconImageComponent :
|
||||
null
|
||||
}
|
||||
|
||||
Component {
|
||||
@@ -111,6 +113,7 @@ RippleButton {
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
id: nameText
|
||||
textFormat: Text.PlainText // TODO: make cliphist entry highlighting working
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
font.family: Appearance.font.family[root.fontType]
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
|
||||
@@ -24,6 +24,21 @@ Item { // Wrapper
|
||||
implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
property string mathResult: ""
|
||||
property bool lastQueryWasClipboard: false
|
||||
|
||||
onShowResultsChanged: {
|
||||
lastQueryWasClipboard = false;
|
||||
}
|
||||
function cancelSearch() {
|
||||
searchInput.selectAll()
|
||||
root.searchingText = ""
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
{
|
||||
action: "img",
|
||||
@@ -194,7 +209,7 @@ Item { // Wrapper
|
||||
Layout.leftMargin: 15
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
text: "search"
|
||||
text: root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard) ? 'content_paste_search' : 'search'
|
||||
}
|
||||
TextField { // Search box
|
||||
id: searchInput
|
||||
@@ -219,13 +234,6 @@ Item { // Wrapper
|
||||
}
|
||||
|
||||
onTextChanged: root.searchingText = text
|
||||
Connections {
|
||||
target: root
|
||||
function onVisibleChanged() {
|
||||
searchInput.selectAll()
|
||||
root.searchingText = ""
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (appResults.count > 0) {
|
||||
@@ -279,10 +287,31 @@ Item { // Wrapper
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
values: {
|
||||
values: { // Search results are handled here
|
||||
////////////////// Skip? //////////////////
|
||||
if(root.searchingText == "") return [];
|
||||
|
||||
// Start math calculation, declare special result objects
|
||||
///////////// Special cases ///////////////
|
||||
if (root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard)) { // Clipboard
|
||||
if (!root.lastQueryWasClipboard) {
|
||||
root.lastQueryWasClipboard = true;
|
||||
Cliphist.refresh(); // Refresh clipboard entries
|
||||
}
|
||||
const searchString = root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length);
|
||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: qsTr("Copy"),
|
||||
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
||||
execute: () => {
|
||||
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
|
||||
////////////////// Init ///////////////////
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
name: root.mathResult,
|
||||
@@ -322,10 +351,9 @@ Item { // Wrapper
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
// Init result array
|
||||
let result = [];
|
||||
|
||||
// Add filtered application entries
|
||||
//////////////// Apps //////////////////
|
||||
result = result.concat(
|
||||
AppSearch.fuzzyQuery(root.searchingText)
|
||||
.map((entry) => {
|
||||
@@ -335,23 +363,20 @@ Item { // Wrapper
|
||||
})
|
||||
);
|
||||
|
||||
// Add launcher actions
|
||||
////////// Launcher actions ////////////
|
||||
result = result.concat(launcherActionObjects);
|
||||
|
||||
// Insert math result before command if search starts with a number
|
||||
/////////// Math result & command //////////
|
||||
const startsWithNumber = /^\d/.test(root.searchingText);
|
||||
if (startsWithNumber)
|
||||
if (startsWithNumber) {
|
||||
result.push(mathResultObject);
|
||||
|
||||
// Command
|
||||
result.push(commandResultObject);
|
||||
|
||||
// If not already added, add math result after command
|
||||
if (!startsWithNumber) {
|
||||
result.push(commandResultObject);
|
||||
} else {
|
||||
result.push(commandResultObject);
|
||||
result.push(mathResultObject);
|
||||
}
|
||||
|
||||
// Web search
|
||||
///////////////// Web search ////////////////
|
||||
result.push({
|
||||
name: root.searchingText,
|
||||
clickActionName: qsTr("Search"),
|
||||
@@ -369,7 +394,8 @@ Item { // Wrapper
|
||||
return result;
|
||||
}
|
||||
}
|
||||
delegate: SearchItem {
|
||||
|
||||
delegate: SearchItem { // The selectable item for each search result
|
||||
entry: modelData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
|
||||
import "root:/modules/common"
|
||||
import "root:/"
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property list<string> entries: []
|
||||
property string highlightPrefix: `<b><font color="${Appearance.m3colors.m3primary}">`
|
||||
property string highlightSuffix: `</font></b>`
|
||||
readonly property var preparedEntries: entries.map(a => ({
|
||||
name: Fuzzy.prepare(`${a}`),
|
||||
entry: a
|
||||
}))
|
||||
function fuzzyQuery(search: string): var {
|
||||
return Fuzzy.go(search, preparedEntries, {
|
||||
all: true,
|
||||
key: "name"
|
||||
}).map(r => {
|
||||
return r.obj.entry
|
||||
// console.log(JSON.stringify(r))
|
||||
// return r.highlight(highlightPrefix, highlightSuffix);
|
||||
});
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
readProc.buffer = []
|
||||
readProc.running = false
|
||||
readProc.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: readProc
|
||||
property list<string> buffer: []
|
||||
|
||||
command: ["cliphist", "list"]
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: (line) => {
|
||||
readProc.buffer.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode === 0) {
|
||||
root.entries = readProc.buffer
|
||||
} else {
|
||||
console.error("[Cliphist] Failed to refresh with code", exitCode, "and status", exitStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ ShellRoot {
|
||||
MaterialThemeLoader.reapplyTheme()
|
||||
ConfigLoader.loadConfig()
|
||||
PersistentStateManager.loadStates()
|
||||
Cliphist.refresh()
|
||||
}
|
||||
|
||||
Bar {}
|
||||
|
||||
Reference in New Issue
Block a user