Updates manifest id/author, the QML moduleName/ipcTarget, and every doc reference to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MLfSWFEMyY85ZDWEYicdaF
309 lines
9.5 KiB
QML
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: "dotjuice.hypr-rdp"
|
|
ipcTarget: "dotjuice.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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|