moonlight-qt/app/gui/PcView.qml

342 lines
11 KiB
QML
Raw Normal View History

2018-07-04 23:40:21 +00:00
import QtQuick 2.9
import QtQuick.Controls 2.2
2020-05-02 01:34:15 +00:00
import QtQuick.Layouts 1.3
2018-07-04 23:40:21 +00:00
import ComputerModel 1.0
import ComputerManager 1.0
import StreamingPreferences 1.0
import SdlGamepadKeyNavigation 1.0
CenteredGridView {
2018-07-06 06:12:55 +00:00
property ComputerModel computerModel : createModel()
id: pcGrid
focus: true
activeFocusOnTab: true
topMargin: 20
bottomMargin: 5
cellWidth: 310; cellHeight: 330;
objectName: "Computers"
Component.onCompleted: {
// Don't show any highlighted item until interacting with them.
// We do this here instead of onActivated to avoid losing the user's
// selection when backing out of a different page of the app.
currentIndex = -1
}
StackView.onActivated: {
2018-07-06 07:34:16 +00:00
// Setup signals on CM
ComputerManager.computerAddCompleted.connect(addComplete)
// This is a bit of a hack to do this here as opposed to main.qml, but
// we need it enabled before calling getConnectedGamepads() and PcView
// is never destroyed, so it should be okay.
SdlGamepadKeyNavigation.enable()
// Highlight the first item if a gamepad is connected
if (currentIndex == -1 && SdlGamepadKeyNavigation.getConnectedGamepads() > 0) {
currentIndex = 0
}
}
StackView.onDeactivating: {
ComputerManager.computerAddCompleted.disconnect(addComplete)
}
2018-07-06 06:12:55 +00:00
function pairingComplete(error)
{
// Close the PIN dialog
pairDialog.close()
// Display a failed dialog if we got an error
if (error !== undefined) {
2018-07-06 07:34:16 +00:00
errorDialog.text = error
errorDialog.helpText = ""
2018-07-06 07:34:16 +00:00
errorDialog.open()
}
}
function addComplete(success)
{
if (!success) {
errorDialog.text = "Unable to connect to the specified PC."
errorDialog.helpText = "Click the Help button for possible solutions."
2018-07-06 07:34:16 +00:00
errorDialog.open()
2018-07-06 06:12:55 +00:00
}
}
function createModel()
{
var model = Qt.createQmlObject('import ComputerModel 1.0; ComputerModel {}', parent, '')
model.initialize(ComputerManager)
2018-07-06 06:12:55 +00:00
model.pairingCompleted.connect(pairingComplete)
return model
}
Row {
anchors.centerIn: parent
spacing: 5
visible: pcGrid.count === 0
BusyIndicator {
id: searchSpinner
visible: StreamingPreferences.enableMdns
}
Label {
height: searchSpinner.height
elide: Label.ElideRight
text: StreamingPreferences.enableMdns ? "Searching for PCs with NVIDIA GameStream enabled..."
: "Automatic PC discovery is disabled. Add your PC manually."
font.pointSize: 20
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
}
}
model: computerModel
delegate: NavigableItemDelegate {
width: 300; height: 320;
grid: pcGrid
Image {
id: pcIcon
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/res/desktop_windows-48px.svg"
sourceSize {
width: 200
height: 200
2018-07-04 23:40:21 +00:00
}
}
Image {
// TODO: Tooltip
id: stateIcon
anchors.horizontalCenter: pcIcon.horizontalCenter
anchors.verticalCenter: pcIcon.verticalCenter
anchors.verticalCenterOffset: -15
2019-03-27 04:31:51 +00:00
visible: !model.statusUnknown && (!model.online || !model.paired)
source: !model.online ? "qrc:/res/baseline-warning-24px.svg" : "qrc:/res/baseline-lock-24px.svg"
sourceSize {
width: 75
height: 75
}
}
BusyIndicator {
id: statusUnknownSpinner
anchors.horizontalCenter: pcIcon.horizontalCenter
anchors.verticalCenter: pcIcon.verticalCenter
anchors.verticalCenterOffset: -15
width: 75
height: 75
2019-03-27 04:31:51 +00:00
visible: model.statusUnknown
}
Label {
id: pcNameText
text: model.name
width: parent.width
anchors.top: pcIcon.bottom
anchors.bottom: parent.bottom
2018-07-07 23:47:39 +00:00
font.pointSize: 36
horizontalAlignment: Text.AlignHCenter
2018-07-07 23:47:39 +00:00
wrapMode: Text.Wrap
elide: Text.ElideRight
}
2018-07-04 23:40:21 +00:00
NavigableMenu {
id: pcContextMenu
NavigableMenuItem {
parentMenu: pcContextMenu
text: "View Apps"
onTriggered: {
var component = Qt.createComponent("AppView.qml")
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name})
stackView.push(appView)
}
visible: model.online && model.paired
}
NavigableMenuItem {
parentMenu: pcContextMenu
text: "View Hidden Apps"
onTriggered: {
var component = Qt.createComponent("AppView.qml")
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name, "showHiddenGames": true})
stackView.push(appView)
}
visible: model.online && model.paired
}
NavigableMenuItem {
parentMenu: pcContextMenu
text: "Delete PC"
onTriggered: {
deletePcDialog.pcIndex = index
// get confirmation first, actual closing is called from the dialog
deletePcDialog.open()
}
}
2020-05-02 01:34:15 +00:00
NavigableMenuItem {
parentMenu: pcContextMenu
text: "Rename PC"
onTriggered: {
renamePcDialog.pcIndex = index
renamePcDialog.originalName = model.name
renamePcDialog.open()
}
}
NavigableMenuItem {
parentMenu: pcContextMenu
text: "Wake PC"
onTriggered: computerModel.wakeComputer(index)
2019-03-27 04:31:51 +00:00
visible: !model.online && model.wakeable
}
}
onClicked: {
2019-03-27 04:31:51 +00:00
if (model.online) {
if (model.paired) {
// go to game view
var component = Qt.createComponent("AppView.qml")
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name})
stackView.push(appView)
}
else {
if (!model.busy) {
var pin = computerModel.generatePinString()
// Kick off pairing in the background
computerModel.pairComputer(index, pin)
// Display the pairing dialog
pairDialog.pin = pin
pairDialog.open()
}
else {
// cannot pair while something is streaming or attempting to pair
2019-03-17 07:33:52 +00:00
errorDialog.text = "You cannot pair while a previous session is still running on the host PC. Quit any running games or reboot the host PC, then try pairing again."
errorDialog.helpText = ""
errorDialog.open()
}
}
} else if (!model.online) {
// Using open() here because it may be activated by keyboard
pcContextMenu.open()
}
}
onPressAndHold: {
2019-03-27 04:31:51 +00:00
// popup() ensures the menu appears under the mouse cursor
if (pcContextMenu.popup) {
pcContextMenu.popup()
}
else {
// Qt 5.9 doesn't have popup()
pcContextMenu.open()
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton;
onClicked: {
parent.onPressAndHold()
2018-07-05 01:48:09 +00:00
}
}
Keys.onMenuPressed: {
2019-03-27 04:31:51 +00:00
// We must use open() here so the menu is positioned on
// the ItemDelegate and not where the mouse cursor is
pcContextMenu.open()
}
2018-07-05 01:48:09 +00:00
}
ErrorMessageDialog {
2018-07-06 07:34:16 +00:00
id: errorDialog
// Using Setup-Guide here instead of Troubleshooting because it's likely that users
// will arrive here by forgetting to enable GameStream or not forwarding ports.
helpUrl: "https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide"
}
NavigableMessageDialog {
2018-07-05 01:48:09 +00:00
id: pairDialog
// Pairing dialog must be modal to prevent double-clicks from triggering
// pairing twice
modal: true
closePolicy: Popup.CloseOnEscape
2018-07-05 01:48:09 +00:00
// don't allow edits to the rest of the window while open
2018-07-06 04:16:32 +00:00
property string pin : "0000"
text:"Please enter " + pin + " on your GameStream PC. This dialog will close when pairing is completed."
standardButtons: Dialog.Cancel
2018-07-05 01:48:09 +00:00
onRejected: {
// FIXME: We should interrupt pairing here
2018-07-05 01:48:09 +00:00
}
}
NavigableMessageDialog {
id: deletePcDialog
// don't allow edits to the rest of the window while open
property int pcIndex : -1;
text:"Are you sure you want to remove this PC?"
standardButtons: Dialog.Yes | Dialog.No
function deletePc() {
console.log("deleting PC pairing for PC at index: " + pcIndex)
computerModel.deleteComputer(pcIndex);
}
onAccepted: deletePc()
}
2020-05-02 01:34:15 +00:00
NavigableDialog {
id: renamePcDialog
property string label: "Enter the new name for this PC:"
property string originalName
property int pcIndex : -1;
standardButtons: Dialog.Ok | Dialog.Cancel
onOpened: {
// Force keyboard focus on the textbox so keyboard navigation works
editText.forceActiveFocus()
}
onClosed: {
editText.clear()
}
onAccepted: {
if (editText.text) {
computerModel.renameComputer(pcIndex, editText.text)
}
}
ColumnLayout {
Label {
text: renamePcDialog.label
font.bold: true
}
TextField {
id: editText
placeholderText: renamePcDialog.originalName
Layout.fillWidth: true
focus: true
Keys.onReturnPressed: {
renamePcDialog.accept()
}
}
}
}
ScrollBar.vertical: ScrollBar {}
2018-07-04 23:40:21 +00:00
}