dragonmoonlight/app/gui/DragonDisplayPicker.qml

204 lines
6.9 KiB
QML
Raw Normal View History

2026-05-06 20:17:23 -04:00
// DragonDisplayPicker.qml
//
// Multi-display picker component for streaming to a specific monitor on a remote host.
// Shows a modal overlay with a list of available displays and lets the user select one.
//
// Usage:
// DragonDisplayPicker {
// visible: showDisplayPicker
// anchors.fill: parent
// hostIP: pickerHostIP
// hostName: pickerHostName
// displays: pickerDisplays
// onDisplaySelected: function(idx) {
// showDisplayPicker = false
// dragonRelay.streamHostDisplay(pickerHostIP, idx)
// }
// onCancelled: { showDisplayPicker = false }
// }
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Item {
id: root
property string hostIP: ""
property string hostName: ""
property var displays: [] // array of display maps
signal displaySelected(int displayIndex)
signal cancelled()
// ── Modal backdrop ─────────────────────────────────────────────────────
Rectangle {
anchors.fill: parent
color: "rgba(0, 0, 0, 0.7)"
MouseArea {
anchors.fill: parent
onClicked: root.cancelled()
}
}
// ── Dialog box ─────────────────────────────────────────────────────────
Rectangle {
id: dialogBox
anchors.centerIn: parent
width: Math.min(parent.width - 48, 500)
height: Math.min(parent.height - 48, 400)
radius: 12
color: "#0f172a" // dark blue slate
border.color: "#374151"
border.width: 1
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 16
// ── Title ──────────────────────────────────────────────────────
Label {
text: qsTr("Select Display")
font.pixelSize: 18
font.bold: true
color: "white"
}
Label {
text: qsTr("Host: %1 (%2)").arg(root.hostName).arg(root.hostIP)
font.pixelSize: 12
color: "#9ca3af"
Layout.fillWidth: true
elide: Text.ElideRight
}
// ── Divider ────────────────────────────────────────────────────
Rectangle {
Layout.fillWidth: true
height: 1
color: "#374151"
}
// ── Display list ───────────────────────────────────────────────
ListView {
id: displayList
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 8
clip: true
model: root.displays
delegate: Rectangle {
width: displayList.width
height: 56
radius: 8
color: displayMouse.containsMouse ? "#374151" : "#1f2937"
border.color: displayMouse.containsMouse ? "#4b5563" : "transparent"
border.width: 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 12
Column {
spacing: 2
Label {
text: modelData.friendlyName || modelData.name
font.pixelSize: 14
font.bold: true
color: "white"
}
Label {
text: qsTr("%1x%2").arg(modelData.width).arg(modelData.height)
font.pixelSize: 11
color: "#9ca3af"
}
}
Item { Layout.fillWidth: true }
Rectangle {
visible: modelData.isPrimary
width: primaryLabel.implicitWidth + 12
height: 20
radius: 4
color: "#10b981" // green
Label {
id: primaryLabel
anchors.centerIn: parent
text: qsTr("Primary")
font.pixelSize: 10
color: "white"
}
}
}
MouseArea {
id: displayMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.displaySelected(index)
}
}
}
// Empty state message
Label {
anchors.centerIn: parent
text: qsTr("No displays available")
color: "#9ca3af"
font.pixelSize: 12
visible: displayList.count === 0
}
}
// ── Divider ────────────────────────────────────────────────────
Rectangle {
Layout.fillWidth: true
height: 1
color: "#374151"
}
// ── Buttons ────────────────────────────────────────────────────
RowLayout {
spacing: 12
Layout.fillWidth: true
Item { Layout.fillWidth: true }
Button {
text: qsTr("Cancel")
flat: true
onClicked: root.cancelled()
}
Button {
text: qsTr("Select")
enabled: displayList.currentIndex >= 0
onClicked: {
if (displayList.currentIndex >= 0) {
root.displaySelected(displayList.currentIndex)
}
}
}
}
}
}
}