Krolyxon
Commit fb9b856

Branches

Refs →

aurora.configquickshellauroraClipWindow.qml

222 lines · 8.8 KB · QML

1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Wayland
5import Quickshell.Hyprland
6
7// Clipboard history: type to filter, Up/Down, Enter copies + closes, Delete removes, Esc closes.
8PanelWindow {
9 id: win
10 property bool opened: false
11 signal closeRequested()
12
13 visible: opened
14 implicitWidth: 620
15 implicitHeight: 560
16 exclusiveZone: 0
17 color: "transparent"
18 WlrLayershell.layer: WlrLayer.Overlay
19 WlrLayershell.namespace: "aurora-clipboard"
20 WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
21
22 HyprlandFocusGrab {
23 windows: [win]
24 active: win.opened
25 onCleared: win.closeRequested()
26 }
27
28 onOpenedChanged: {
29 if (!opened) return
30 filter.text = ""
31 armed = false
32 Clip.refresh()
33 list.currentIndex = 0
34 filter.forceActiveFocus()
35 }
36
37 property bool armed: false
38 Timer { id: disarm; interval: 3000; onTriggered: win.armed = false }
39
40 readonly property var shown: {
41 var q = filter.text.toLowerCase(), out = [], l = Clip.entries
42 for (var i = 0; i < l.length; i++)
43 if (q === "" || l[i].text.toLowerCase().indexOf(q) >= 0) out.push(l[i])
44 return out
45 }
46 function pick(e) {
47 if (!e) return
48 Clip.copy(e.id)
49 closeRequested()
50 }
51
52 Rectangle {
53 anchors.fill: parent
54 color: Theme.bgA
55 border.width: 2
56 border.color: Theme.accent
57
58 ColumnLayout {
59 anchors.fill: parent
60 anchors.margins: 12
61 spacing: 10
62
63 RowLayout {
64 Layout.fillWidth: true
65 spacing: 6
66 Rectangle {
67 Layout.fillWidth: true
68 implicitHeight: 30
69 color: Theme.surface
70 border.width: 1
71 border.color: filter.activeFocus ? Theme.accent : Theme.line
72 Text {
73 anchors.left: parent.left
74 anchors.leftMargin: 10
75 anchors.verticalCenter: parent.verticalCenter
76 text: Theme.iClipboard
77 color: Theme.dim
78 font.family: Theme.font
79 font.pixelSize: Theme.fontSize
80 }
81 TextInput {
82 id: filter
83 anchors.fill: parent
84 anchors.leftMargin: 32
85 anchors.rightMargin: 10
86 verticalAlignment: TextInput.AlignVCenter
87 color: Theme.fg
88 selectionColor: Theme.accent
89 selectedTextColor: Theme.bg
90 font.family: Theme.font
91 font.pixelSize: Theme.fontSize
92 clip: true
93 onTextChanged: list.currentIndex = 0
94 Keys.onEscapePressed: win.closeRequested()
95 Keys.onReturnPressed: win.pick(win.shown[list.currentIndex])
96 Keys.onEnterPressed: win.pick(win.shown[list.currentIndex])
97 Keys.onUpPressed: list.decrementCurrentIndex()
98 Keys.onDownPressed: list.incrementCurrentIndex()
99 Keys.onPressed: event => {
100 if (event.key === Qt.Key_Delete && list.currentIndex >= 0 && win.shown.length) {
101 Clip.remove(win.shown[list.currentIndex].id)
102 event.accepted = true
103 } else if (event.key === Qt.Key_PageDown) {
104 list.currentIndex = Math.min(win.shown.length - 1, list.currentIndex + 10); event.accepted = true
105 } else if (event.key === Qt.Key_PageUp) {
106 list.currentIndex = Math.max(0, list.currentIndex - 10); event.accepted = true
107 }
108 }
109 }
110 }
111 Text {
112 text: win.shown.length
113 color: Theme.dim
114 font.family: Theme.font
115 font.pixelSize: Theme.fontSize - 1
116 }
117 IconBtn {
118 icon: Theme.iTrash
119 label: win.armed ? "sure?" : "wipe"
120 active: win.armed
121 onClicked: {
122 if (!win.armed) { win.armed = true; disarm.restart(); return }
123 win.armed = false
124 Clip.wipe()
125 }
126 }
127 IconBtn { icon: Theme.iClose; onClicked: win.closeRequested() }
128 }
129
130 ListView {
131 id: list
132 Layout.fillWidth: true
133 Layout.fillHeight: true
134 clip: true
135 model: win.shown
136 spacing: 2
137 boundsBehavior: Flickable.StopAtBounds
138 highlightMoveDuration: 0
139
140 delegate: Rectangle {
141 id: row
142 required property var modelData
143 required property int index
144 readonly property bool selected: list.currentIndex === index
145 readonly property bool hasImg: modelData.image && Clip.ready[modelData.id] === true
146 width: list.width
147 height: hasImg ? 96 : 28
148 color: selected ? Theme.surfaceHi : (rm.containsMouse ? Theme.surface : "transparent")
149 border.width: selected ? 1 : 0
150 border.color: Theme.accent
151
152 Rectangle { // selection marker, like the bar's active workspace
153 visible: row.selected
154 width: 2
155 height: parent.height
156 color: Theme.accent
157 }
158 Row {
159 anchors.fill: parent
160 anchors.leftMargin: 10
161 anchors.rightMargin: 34
162 spacing: 10
163 Image {
164 visible: row.hasImg
165 anchors.verticalCenter: parent.verticalCenter
166 width: visible ? 150 : 0
167 height: 84
168 fillMode: Image.PreserveAspectFit
169 asynchronous: true
170 sourceSize.width: 300
171 sourceSize.height: 168
172 source: row.hasImg ? "file://" + Clip.imageOf(row.modelData) : ""
173 }
174 Text {
175 anchors.verticalCenter: parent.verticalCenter
176 width: parent.width - (row.hasImg ? 160 : 0)
177 text: (row.modelData.image ? Theme.iImage + " " : "") + row.modelData.text
178 elide: Text.ElideRight
179 maximumLineCount: 1
180 color: row.selected ? Theme.fg : (row.modelData.image ? Theme.accent : Theme.fg)
181 font.family: Theme.font
182 font.pixelSize: Theme.fontSize
183 }
184 }
185 MouseArea {
186 id: rm
187 anchors.fill: parent
188 hoverEnabled: true
189 cursorShape: Qt.PointingHandCursor
190 onClicked: win.pick(row.modelData)
191 }
192 Text { // delete
193 anchors.right: parent.right
194 anchors.rightMargin: 10
195 anchors.verticalCenter: parent.verticalCenter
196 visible: row.selected
197 text: Theme.iClose
198 color: dm.containsMouse ? Theme.err : Theme.dim
199 font.family: Theme.font
200 font.pixelSize: Theme.fontSize
201 MouseArea {
202 id: dm
203 anchors.fill: parent
204 anchors.margins: -4
205 hoverEnabled: true
206 cursorShape: Qt.PointingHandCursor
207 onClicked: Clip.remove(row.modelData.id)
208 }
209 }
210 }
211 }
212
213 Text {
214 Layout.fillWidth: true
215 text: Clip.available ? "enter copy del remove esc close" : "cliphist not installed"
216 color: Clip.available ? Theme.dim : Theme.err
217 font.family: Theme.font
218 font.pixelSize: Theme.fontSize - 2
219 }
220 }
221 }
222}