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
2018-07-06 05:08:55 +00:00
import ComputerManager 1.0
2019-03-28 01:13:20 +00:00
import StreamingPreferences 1.0
2019-05-19 18:08:23 +00:00
import SdlGamepadKeyNavigation 1.0
2018-07-06 05:08:55 +00:00
2019-04-06 18:48:58 +00:00
CenteredGridView {
2018-07-06 06:12:55 +00:00
property ComputerModel computerModel : createModel ( )
2018-07-09 06:05:36 +00:00
id: pcGrid
2018-09-23 22:16:27 +00:00
focus: true
activeFocusOnTab: true
2019-03-27 08:28:46 +00:00
topMargin: 20
bottomMargin: 5
2019-04-19 04:03:37 +00:00
cellWidth: 310 ; cellHeight: 330 ;
2018-07-08 05:15:02 +00:00
objectName: "Computers"
2018-07-06 03:11:35 +00:00
2019-02-23 06:44:09 +00:00
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
}
2019-02-23 06:14:06 +00:00
StackView.onActivated: {
2018-07-06 07:34:16 +00:00
// Setup signals on CM
ComputerManager . computerAddCompleted . connect ( addComplete )
2019-05-19 18:08:23 +00:00
// 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
}
2018-07-06 05:08:55 +00:00
}
2019-02-23 06:14:06 +00:00
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
2018-08-30 03:59:19 +00:00
if ( error !== undefined ) {
2018-07-06 07:34:16 +00:00
errorDialog . text = error
2019-03-23 20:51:34 +00:00
errorDialog . helpText = ""
2018-07-06 07:34:16 +00:00
errorDialog . open ( )
}
}
function addComplete ( success )
{
if ( ! success ) {
2019-03-23 20:51:34 +00:00
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
}
}
2018-07-06 05:08: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 )
2018-07-06 05:08:55 +00:00
return model
}
2019-03-28 01:13:20 +00:00
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
}
}
2018-08-02 04:29:03 +00:00
model: computerModel
2018-07-06 03:11:35 +00:00
2018-09-23 22:16:27 +00:00
delegate: NavigableItemDelegate {
2019-04-19 04:03:37 +00:00
width: 300 ; height: 320 ;
2018-09-23 22:16:27 +00:00
grid: pcGrid
2018-07-06 03:11:35 +00:00
Image {
id: pcIcon
2018-08-05 19:47:08 +00:00
anchors.horizontalCenter: parent . horizontalCenter
2019-11-06 01:06:57 +00:00
source: "qrc:/res/desktop_windows-48px.svg"
2018-07-06 03:11:35 +00:00
sourceSize {
width: 200
height: 200
2018-07-04 23:40:21 +00:00
}
2018-07-06 03:11:35 +00:00
}
2018-08-05 18:47:14 +00:00
Image {
// TODO: Tooltip
id: stateIcon
2018-08-05 19:47:08 +00:00
anchors.horizontalCenter: pcIcon . horizontalCenter
anchors.verticalCenter: pcIcon . verticalCenter
2019-11-06 01:06:57 +00:00
anchors.verticalCenterOffset: - 15
2019-03-27 04:31:51 +00:00
visible: ! model . statusUnknown && ( ! model . online || ! model . paired )
2018-08-05 18:47:14 +00:00
source: ! model . online ? "qrc:/res/baseline-warning-24px.svg" : "qrc:/res/baseline-lock-24px.svg"
sourceSize {
2018-08-05 19:47:08 +00:00
width: 75
height: 75
2018-08-05 18:47:14 +00:00
}
}
2018-08-05 19:13:08 +00:00
BusyIndicator {
id: statusUnknownSpinner
2018-08-05 19:47:08 +00:00
anchors.horizontalCenter: pcIcon . horizontalCenter
anchors.verticalCenter: pcIcon . verticalCenter
2019-11-06 01:06:57 +00:00
anchors.verticalCenterOffset: - 15
2018-08-05 19:47:08 +00:00
width: 75
height: 75
2019-03-27 04:31:51 +00:00
visible: model . statusUnknown
2018-08-05 19:13:08 +00:00
}
2018-11-22 10:35:25 +00:00
Label {
2018-07-06 03:11:35 +00:00
id: pcNameText
text: model . name
width: parent . width
anchors.top: pcIcon . bottom
2019-04-19 04:03:37 +00:00
anchors.bottom: parent . bottom
2018-07-07 23:47:39 +00:00
font.pointSize: 36
2018-07-06 03:11:35 +00:00
horizontalAlignment: Text . AlignHCenter
2018-07-07 23:47:39 +00:00
wrapMode: Text . Wrap
2019-04-19 04:03:37 +00:00
elide: Text . ElideRight
2018-07-06 03:11:35 +00:00
}
2018-07-04 23:40:21 +00:00
2019-02-10 03:59:01 +00:00
NavigableMenu {
2018-07-29 23:04:45 +00:00
id: pcContextMenu
2018-09-24 02:06:26 +00:00
NavigableMenuItem {
2019-02-10 03:59:01 +00:00
parentMenu: pcContextMenu
2018-07-29 23:04:45 +00:00
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 ( )
}
}
2019-02-10 03:59:01 +00:00
NavigableMenuItem {
parentMenu: pcContextMenu
text: "Wake PC"
onTriggered: computerModel . wakeComputer ( index )
2019-03-27 04:31:51 +00:00
visible: ! model . online && model . wakeable
2019-02-10 03:59:01 +00:00
}
2018-07-29 23:04:45 +00:00
}
2018-09-06 00:08:27 +00:00
onClicked: {
2019-03-27 04:31:51 +00:00
if ( model . online ) {
2018-09-06 00:08:27 +00:00
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 = ( "0000" + Math . floor ( Math . random ( ) * 10000 ) ) . slice ( - 4 )
// Kick off pairing in the background
computerModel . pairComputer ( index , pin )
// Display the pairing dialog
pairDialog . pin = pin
pairDialog . open ( )
2018-07-06 03:37:51 +00:00
}
2018-09-06 00:08:27 +00:00
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."
2019-03-23 20:51:34 +00:00
errorDialog . helpText = ""
2018-09-06 00:08:27 +00:00
errorDialog . open ( )
2018-07-06 03:37:51 +00:00
}
}
2018-09-06 00:08:27 +00:00
} else if ( ! model . online ) {
2018-09-30 20:41:32 +00:00
// Using open() here because it may be activated by keyboard
2018-09-06 00:08:27 +00:00
pcContextMenu . open ( )
}
}
2019-01-27 07:11:09 +00:00
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 ( )
2019-01-27 07:11:09 +00:00
}
}
2018-09-06 00:08:27 +00:00
MouseArea {
anchors.fill: parent
acceptedButtons: Qt . RightButton ;
onClicked: {
2019-01-27 07:11:09 +00:00
parent . onPressAndHold ( )
2018-07-05 01:48:09 +00:00
}
}
2018-09-30 20:41:32 +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-09-30 20:41:32 +00:00
}
2018-07-05 01:48:09 +00:00
}
2019-03-23 20:51:34 +00:00
ErrorMessageDialog {
2018-07-06 07:34:16 +00:00
id: errorDialog
2019-03-31 22:16:48 +00:00
// 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"
2018-07-06 03:49:10 +00:00
}
2019-03-31 20:57:57 +00:00
NavigableMessageDialog {
2018-07-05 01:48:09 +00:00
id: pairDialog
2019-11-05 03:30:38 +00:00
// 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."
2019-04-01 00:24:25 +00:00
standardButtons: Dialog . Cancel
2018-07-05 01:48:09 +00:00
onRejected: {
2018-07-06 03:37:51 +00:00
// FIXME: We should interrupt pairing here
2018-07-05 01:48:09 +00:00
}
}
2019-03-31 20:57:57 +00:00
NavigableMessageDialog {
2018-07-29 23:04:45 +00:00
id: deletePcDialog
// don't allow edits to the rest of the window while open
property int pcIndex : - 1 ;
2018-08-01 05:10:38 +00:00
text: "Are you sure you want to remove this PC?"
2019-04-01 00:24:25 +00:00
standardButtons: Dialog . Yes | Dialog . No
2018-10-01 05:20:19 +00:00
function deletePc ( ) {
2018-07-29 23:04:45 +00:00
console . log ( "deleting PC pairing for PC at index: " + pcIndex )
computerModel . deleteComputer ( pcIndex ) ;
}
2018-10-01 05:20:19 +00:00
onAccepted: deletePc ( )
2018-07-29 23:04:45 +00:00
}
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 ( )
}
}
}
}
2018-07-09 06:05:36 +00:00
ScrollBar.vertical: ScrollBar {
parent: pcGrid . parent
anchors {
2019-04-02 03:40:52 +00:00
top: parent . top
right: parent . right
bottom: parent . bottom
2018-07-09 06:05:36 +00:00
}
}
2018-07-04 23:40:21 +00:00
}