Build the Panama Hyprland desktop
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
// Clipboard history, backed by Vicinae's existing database. Opening the panel
|
||||
// performs one read-only query; nothing polls or repaints while it is closed.
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.widgets
|
||||
import qs.modules.quicksettings
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
visible: ShellState.clipboardOpen
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
right: true
|
||||
}
|
||||
margins {
|
||||
top: Theme.barHeight + Theme.barGap * 2
|
||||
right: Theme.barSideMargin
|
||||
}
|
||||
|
||||
exclusiveZone: 0
|
||||
implicitWidth: 430
|
||||
implicitHeight: 550
|
||||
|
||||
WlrLayershell.namespace: "qs-popover-clipboard"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
property string filter: ""
|
||||
|
||||
readonly property var filteredEntries: Clipboard.entries.filter(entry => {
|
||||
const needle = root.filter.trim().toLowerCase();
|
||||
if (!needle)
|
||||
return true;
|
||||
return [entry.preview, entry.source, entry.host, entry.typeLabel]
|
||||
.join(" ").toLowerCase().includes(needle);
|
||||
})
|
||||
|
||||
function close(): void {
|
||||
ShellState.close();
|
||||
}
|
||||
|
||||
function activateFirst(): void {
|
||||
if (root.filteredEntries.length === 0)
|
||||
return;
|
||||
Clipboard.copyEntry(root.filteredEntries[0].id);
|
||||
root.close();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (root.visible) {
|
||||
root.filter = "";
|
||||
search.clear();
|
||||
Clipboard.refresh();
|
||||
openAnim.restart();
|
||||
Qt.callLater(search.grab);
|
||||
}
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
active: root.visible
|
||||
onCleared: root.close()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: surface
|
||||
anchors.fill: parent
|
||||
radius: Theme.popoverRadius
|
||||
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.08)
|
||||
|
||||
PrismEdge {
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: 1
|
||||
}
|
||||
inset: parent.radius
|
||||
}
|
||||
|
||||
transform: Scale {
|
||||
id: openScale
|
||||
origin.x: surface.width
|
||||
origin.y: 0
|
||||
xScale: 1
|
||||
yScale: 1
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.popoverPadding
|
||||
spacing: Theme.sectionSpacing
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 30
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 9
|
||||
|
||||
ThemedIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 17
|
||||
icon: "edit-paste-symbolic"
|
||||
iconFallback: "edit-copy-symbolic"
|
||||
tint: Theme.accent
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Clipboard"
|
||||
color: Theme.fg
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeTitle
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Clipboard.entries.length === 1
|
||||
? "1 item"
|
||||
: Clipboard.entries.length + " items"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.features: Theme.tabularFigures
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
|
||||
SearchField {
|
||||
id: search
|
||||
width: parent.width
|
||||
placeholder: "Search clipboard history"
|
||||
onTextChanged: root.filter = text
|
||||
onAccepted: root.activateFirst()
|
||||
onDismissed: root.close()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
border.width: 0
|
||||
color: Theme.alpha(Theme.fg, 0.1)
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height - y
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: Clipboard.loading
|
||||
text: "Loading clipboard history…"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 40
|
||||
visible: !Clipboard.loading && !Clipboard.available
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
text: "Clipboard history is unavailable. Start Vicinae to restore it."
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: !Clipboard.loading && Clipboard.available
|
||||
&& root.filteredEntries.length === 0
|
||||
text: root.filter ? "No matching clipboard items" : "Nothing copied yet"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
}
|
||||
|
||||
ScrollColumn {
|
||||
anchors.fill: parent
|
||||
maxHeight: parent.height
|
||||
spacing: 4
|
||||
visible: !Clipboard.loading && Clipboard.available
|
||||
&& root.filteredEntries.length > 0
|
||||
|
||||
Repeater {
|
||||
model: root.filteredEntries
|
||||
|
||||
ClipboardEntry {
|
||||
required property var modelData
|
||||
|
||||
width: parent.width
|
||||
entry: modelData
|
||||
onActivated: {
|
||||
Clipboard.copyEntry(modelData.id);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: openAnim
|
||||
target: openScale
|
||||
property: "yScale"
|
||||
from: 0.96
|
||||
to: 1
|
||||
duration: Theme.durFast
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user