mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-14 23:47:09 +00:00
213 lines
6.7 KiB
QML
213 lines
6.7 KiB
QML
import QtQuick 2.0
|
|
import QtQuick.Controls 2.2
|
|
import QtQuick.Window 2.2
|
|
|
|
import SdlGamepadKeyNavigation 1.0
|
|
import Session 1.0
|
|
|
|
Item {
|
|
property Session session
|
|
property string appName
|
|
property string stageText : isResume ? qsTr("Resuming %1...").arg(appName) :
|
|
qsTr("Starting %1...").arg(appName)
|
|
property bool isResume : false
|
|
property bool quitAfter : false
|
|
|
|
function stageStarting(stage)
|
|
{
|
|
// Update the spinner text
|
|
stageText = qsTr("Starting %1...").arg(stage)
|
|
}
|
|
|
|
function stageFailed(stage, errorCode, failingPorts)
|
|
{
|
|
// Display the error dialog after Session::exec() returns
|
|
streamSegueErrorDialog.text = qsTr("Starting %1 failed: Error %2").arg(stage).arg(errorCode)
|
|
|
|
if (failingPorts) {
|
|
streamSegueErrorDialog.text += "\n\n" + qsTr("Check your firewall and port forwarding rules for port(s): %1").arg(failingPorts)
|
|
}
|
|
}
|
|
|
|
function connectionStarted()
|
|
{
|
|
// Hide the UI contents so the user doesn't
|
|
// see them briefly when we pop off the StackView
|
|
stageSpinner.visible = false
|
|
stageLabel.visible = false
|
|
hintText.visible = false
|
|
|
|
// Hide the window now that streaming has begun
|
|
window.visible = false
|
|
}
|
|
|
|
function displayLaunchError(text)
|
|
{
|
|
// Display the error dialog after Session::exec() returns
|
|
streamSegueErrorDialog.text = text
|
|
}
|
|
|
|
function displayLaunchWarning(text)
|
|
{
|
|
// This toast appears for 3 seconds, just shorter than how long
|
|
// Session will wait for it to be displayed. This gives it time
|
|
// to transition to invisible before continuing.
|
|
var toast = Qt.createQmlObject('import QtQuick.Controls 2.2; ToolTip {}', parent, '')
|
|
toast.text = text
|
|
toast.timeout = 3000
|
|
toast.visible = true
|
|
}
|
|
|
|
function quitStarting()
|
|
{
|
|
// Avoid the push transition animation
|
|
var component = Qt.createComponent("QuitSegue.qml")
|
|
stackView.replace(stackView.currentItem, component.createObject(stackView, {"appName": appName}), StackView.Immediate)
|
|
|
|
// Show the Qt window again to show quit segue
|
|
window.visible = true
|
|
}
|
|
|
|
function sessionFinished(portTestResult)
|
|
{
|
|
if (portTestResult !== 0 && portTestResult !== -1 && streamSegueErrorDialog.text) {
|
|
streamSegueErrorDialog.text += "\n\n" + qsTr("This PC's Internet connection is blocking Moonlight. Streaming over the Internet may not work while connected to this network.")
|
|
}
|
|
|
|
// Enable GUI gamepad usage now
|
|
SdlGamepadKeyNavigation.enable()
|
|
|
|
if (quitAfter) {
|
|
if (streamSegueErrorDialog.text) {
|
|
// Quit when the error dialog is acknowledged
|
|
streamSegueErrorDialog.quitAfter = quitAfter
|
|
streamSegueErrorDialog.open()
|
|
}
|
|
else {
|
|
// Quit immediately
|
|
Qt.quit()
|
|
}
|
|
} else {
|
|
// Exit this view
|
|
stackView.pop()
|
|
|
|
// Show the Qt window again after streaming
|
|
window.visible = true
|
|
|
|
// Display any launch errors. We do this after
|
|
// the Qt UI is visible again to prevent losing
|
|
// focus on the dialog which would impact gamepad
|
|
// users.
|
|
if (streamSegueErrorDialog.text) {
|
|
streamSegueErrorDialog.quitAfter = quitAfter
|
|
streamSegueErrorDialog.open()
|
|
}
|
|
}
|
|
}
|
|
|
|
function sessionReadyForDeletion()
|
|
{
|
|
// Garbage collect the Session object since it's pretty heavyweight
|
|
// and keeps other libraries (like SDL_TTF) around until it is deleted.
|
|
session = null
|
|
gc()
|
|
}
|
|
|
|
StackView.onDeactivating: {
|
|
// Show the toolbar again when popped off the stack
|
|
toolBar.visible = true
|
|
|
|
// Enable GUI gamepad usage now
|
|
SdlGamepadKeyNavigation.enable()
|
|
}
|
|
|
|
StackView.onActivated: {
|
|
// Hide the toolbar before we start loading
|
|
toolBar.visible = false
|
|
|
|
// Hook up our signals
|
|
session.stageStarting.connect(stageStarting)
|
|
session.stageFailed.connect(stageFailed)
|
|
session.connectionStarted.connect(connectionStarted)
|
|
session.displayLaunchError.connect(displayLaunchError)
|
|
session.displayLaunchWarning.connect(displayLaunchWarning)
|
|
session.quitStarting.connect(quitStarting)
|
|
session.sessionFinished.connect(sessionFinished)
|
|
session.readyForDeletion.connect(sessionReadyForDeletion)
|
|
|
|
// Kick off the stream
|
|
spinnerTimer.start()
|
|
streamLoader.active = true
|
|
}
|
|
|
|
Timer {
|
|
id: spinnerTimer
|
|
|
|
// Display the spinner appearance a bit to allow us to reach
|
|
// the code in Session.exec() that pumps the event loop.
|
|
// If we display it immediately, it will briefly hang in the
|
|
// middle of the animation on Windows, which looks very
|
|
// obviously broken.
|
|
interval: 100
|
|
onTriggered: stageSpinner.running = true
|
|
}
|
|
|
|
Loader {
|
|
id: streamLoader
|
|
active: false
|
|
asynchronous: true
|
|
|
|
onLoaded: {
|
|
// Set the hint text. We do this here rather than
|
|
// in the hintText control itself to synchronize
|
|
// with Session.exec() which requires no concurrent
|
|
// gamepad usage.
|
|
hintText.text = qsTr("Tip:") + " " + qsTr("Press %1 to disconnect your session").arg(SdlGamepadKeyNavigation.getConnectedGamepads() > 0 ?
|
|
qsTr("Start+Select+L1+R1") : qsTr("Ctrl+Alt+Shift+Q"))
|
|
|
|
// Stop GUI gamepad usage now
|
|
SdlGamepadKeyNavigation.disable()
|
|
|
|
// Garbage collect QML stuff before we start streaming,
|
|
// since we'll probably be streaming for a while and we
|
|
// won't be able to GC during the stream.
|
|
gc()
|
|
|
|
// Run the streaming session to completion
|
|
session.exec(Screen.virtualX, Screen.virtualY)
|
|
}
|
|
|
|
sourceComponent: Item {}
|
|
}
|
|
|
|
Row {
|
|
anchors.centerIn: parent
|
|
spacing: 5
|
|
|
|
BusyIndicator {
|
|
id: stageSpinner
|
|
running: false
|
|
}
|
|
|
|
Label {
|
|
id: stageLabel
|
|
height: stageSpinner.height
|
|
text: stageText
|
|
font.pointSize: 20
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
wrapMode: Text.Wrap
|
|
}
|
|
}
|
|
|
|
Label {
|
|
id: hintText
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 50
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
font.pointSize: 18
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
wrapMode: Text.Wrap
|
|
}
|
|
}
|