Files
omarchy-rdp-service/Panel.qml
Johan 82d76b726e Initial commit: hypr-rdp Control bar widget
Toggles the hypr-rdp systemd --user service, isolates its headless
monitor's workspace via Omarchy's toggle-flag convention, shows what's
running on that workspace, and manages hypr-rdp's connection credentials.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MLfSWFEMyY85ZDWEYicdaF
2026-09-08 15:57:18 +01:00

309 lines
9.5 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Ui
Panel {
id: root
moduleName: "jandieman.hypr-rdp"
ipcTarget: "jandieman.hypr-rdp"
readonly property color fg: root.bar ? root.bar.foreground : Color.foreground
readonly property color dim: Qt.darker(fg, 1.45)
readonly property color good: "#22c55e"
readonly property color bad: "#ef4444"
readonly property string fontFamily: root.bar ? root.bar.fontFamily : "JetBrainsMono Nerd Font"
property string editUsername: ""
property string editPassword: ""
property string editBind: ""
property bool showPassword: false
function syncEditFields() {
editUsername = service.username
editPassword = service.password
editBind = service.bindAddress
}
function triggerPress(button) {
if (button === Qt.MiddleButton) { service.refresh(); return }
if (opened) close()
else { open(); service.refresh() }
}
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
Service {
id: service
settings: root.settings
}
onOpenedChanged: if (opened) syncEditFields()
WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: "RDP"
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(30)
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
foreground: !service.installed ? root.dim : (service.active ? root.good : root.dim)
tooltipText: !service.installed ? "hypr-rdp: not installed" : ("hypr-rdp: " + service.statusText)
onPressed: function(b) { root.triggerPress(b) }
}
KeyboardPanel {
id: panel
anchorItem: button
owner: root
bar: root.bar
open: root.opened
focusTarget: keyCatcher
contentWidth: panel.fittedContentWidth(Style.space(400))
contentHeight: panel.fittedContentHeight(contentColumn.implicitHeight)
PanelKeyCatcher {
id: keyCatcher
anchors.fill: parent
onCloseRequested: root.close()
ColumnLayout {
id: contentColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: Style.space(10)
// ── Header ──
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
text: "hypr-rdp"
color: root.fg
font.family: root.fontFamily
font.pixelSize: Style.font.title
font.bold: true
Layout.fillWidth: true
textFormat: Text.PlainText
}
Text {
text: service.statusText
color: !service.installed ? root.dim : (service.active ? root.good : root.dim)
font.family: root.fontFamily
font.pixelSize: Style.font.caption
textFormat: Text.PlainText
}
Button {
text: "Refresh"
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.caption
horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY
onClicked: service.refresh()
}
}
Text {
visible: service.actionStatus !== ""
Layout.fillWidth: true
text: service.actionStatus
color: root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.caption
textFormat: Text.PlainText
}
Text {
visible: service.lastError !== ""
Layout.fillWidth: true
text: service.lastError
color: root.bad
wrapMode: Text.WordWrap
font.family: root.fontFamily
font.pixelSize: Style.font.caption
textFormat: Text.PlainText
}
PanelSeparator { Layout.fillWidth: true; foreground: root.fg }
// ── Not installed ──
ColumnLayout {
visible: !service.installed
Layout.fillWidth: true
spacing: Style.space(8)
Text {
Layout.fillWidth: true
text: "hypr-rdp isn't installed on this machine."
color: root.dim
wrapMode: Text.WordWrap
font.family: root.fontFamily
font.pixelSize: Style.font.body
textFormat: Text.PlainText
}
Button {
text: "Install hypr-rdp (opens a terminal)"
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.body
horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY
onClicked: service.installHyprRdp()
}
}
// ── Installed: full UI ──
ColumnLayout {
visible: service.installed
Layout.fillWidth: true
spacing: Style.space(12)
// Service
PanelSectionHeader { text: "SERVICE"; foreground: root.fg }
Toggle {
Layout.fillWidth: true
label: "RDP server"
description: service.bindAddress !== "" ? ("Listening on " + service.bindAddress) : "systemd --user service"
checked: service.active
foreground: root.fg
onClicked: service.toggleService()
}
// Isolation
PanelSectionHeader { text: "MONITOR ISOLATION"; foreground: root.fg }
Toggle {
Layout.fillWidth: true
label: "Isolate RDP monitor"
description: "Keeps it off workspace " + service.isolatedWorkspace +
" only, out of normal auto-assignment. Turn off to let Hyprland treat it" +
" like a real second display. Takes effect next time hypr-rdp (re)starts."
checked: service.isolated
foreground: root.fg
onClicked: service.toggleIsolation()
}
// Workspace view
PanelSectionHeader { text: "WORKSPACE " + service.isolatedWorkspace; foreground: root.fg }
Text {
visible: service.windowsOnIsolated.length === 0
Layout.fillWidth: true
text: service.running ? "No windows" : "hypr-rdp isn't running"
color: root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.caption
textFormat: Text.PlainText
}
ColumnLayout {
visible: service.windowsOnIsolated.length > 0
Layout.fillWidth: true
spacing: Style.space(4)
Repeater {
model: service.windowsOnIsolated
delegate: Text {
required property var modelData
Layout.fillWidth: true
text: modelData.cls !== "" ? (modelData.cls + " — " + modelData.title) : modelData.title
color: root.fg
elide: Text.ElideRight
font.family: root.fontFamily
font.pixelSize: Style.font.caption
textFormat: Text.PlainText
}
}
}
Button {
visible: service.running
text: service.peeking ? "Back to your workspace" : "Peek at RDP workspace"
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.caption
horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY
onClicked: service.peeking ? service.returnFromPeek() : service.peekIsolatedWorkspace()
}
// Credentials
PanelSectionHeader { text: "CREDENTIALS"; foreground: root.fg }
TextField {
Layout.fillWidth: true
placeholderText: "Username"
text: root.editUsername
foreground: root.fg
onTextEdited: root.editUsername = text
}
RowLayout {
Layout.fillWidth: true
spacing: Style.space(6)
TextField {
Layout.fillWidth: true
placeholderText: "Password"
text: root.editPassword
password: !root.showPassword
foreground: root.fg
onTextEdited: root.editPassword = text
}
Button {
text: root.showPassword ? "Hide" : "Show"
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.caption
horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY
onClicked: root.showPassword = !root.showPassword
}
}
TextField {
Layout.fillWidth: true
placeholderText: "Bind address (e.g. 0.0.0.0:3389)"
text: root.editBind
foreground: root.fg
onTextEdited: root.editBind = text
}
Button {
text: "Save credentials"
foreground: root.fg
fontFamily: root.fontFamily
fontSize: Style.font.body
horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY
onClicked: service.saveCredentials(root.editUsername, root.editPassword, root.editBind)
}
}
// ── Footer ──
Text {
Layout.fillWidth: true
text: "esc closes"
color: Qt.rgba(root.fg.r, root.fg.g, root.fg.b, 0.3)
font.family: root.fontFamily
font.pixelSize: Style.font.caption
horizontalAlignment: Text.AlignHCenter
textFormat: Text.PlainText
}
}
}
}
}