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 } } } } }