Add support for selecting an app to launch directly

This commit is contained in:
Cameron Gutman 2020-11-23 21:38:22 -06:00
parent d7ca3801be
commit 72182c7caa
7 changed files with 92 additions and 11 deletions

View File

@ -5,6 +5,7 @@
#define SER_APPHDR "hdr" #define SER_APPHDR "hdr"
#define SER_APPCOLLECTOR "appcollector" #define SER_APPCOLLECTOR "appcollector"
#define SER_HIDDEN "hidden" #define SER_HIDDEN "hidden"
#define SER_DIRECTLAUNCH "directlaunch"
NvApp::NvApp(QSettings& settings) NvApp::NvApp(QSettings& settings)
{ {
@ -13,6 +14,7 @@ NvApp::NvApp(QSettings& settings)
hdrSupported = settings.value(SER_APPHDR).toBool(); hdrSupported = settings.value(SER_APPHDR).toBool();
isAppCollectorGame = settings.value(SER_APPCOLLECTOR).toBool(); isAppCollectorGame = settings.value(SER_APPCOLLECTOR).toBool();
hidden = settings.value(SER_HIDDEN).toBool(); hidden = settings.value(SER_HIDDEN).toBool();
directLaunch = settings.value(SER_DIRECTLAUNCH).toBool();
} }
void NvApp::serialize(QSettings& settings) const void NvApp::serialize(QSettings& settings) const
@ -22,4 +24,5 @@ void NvApp::serialize(QSettings& settings) const
settings.setValue(SER_APPHDR, hdrSupported); settings.setValue(SER_APPHDR, hdrSupported);
settings.setValue(SER_APPCOLLECTOR, isAppCollectorGame); settings.setValue(SER_APPCOLLECTOR, isAppCollectorGame);
settings.setValue(SER_HIDDEN, hidden); settings.setValue(SER_HIDDEN, hidden);
settings.setValue(SER_DIRECTLAUNCH, directLaunch);
} }

View File

@ -14,7 +14,8 @@ public:
name == other.name && name == other.name &&
hdrSupported == other.hdrSupported && hdrSupported == other.hdrSupported &&
isAppCollectorGame == other.isAppCollectorGame && isAppCollectorGame == other.isAppCollectorGame &&
hidden == other.hidden; hidden == other.hidden &&
directLaunch == other.directLaunch;
} }
bool operator!=(const NvApp& other) const bool operator!=(const NvApp& other) const
@ -35,6 +36,7 @@ public:
bool hdrSupported = false; bool hdrSupported = false;
bool isAppCollectorGame = false; bool isAppCollectorGame = false;
bool hidden = false; bool hidden = false;
bool directLaunch = false;
}; };
Q_DECLARE_METATYPE(NvApp) Q_DECLARE_METATYPE(NvApp)

View File

@ -317,6 +317,7 @@ bool NvComputer::updateAppList(QVector<NvApp> newAppList) {
for (NvApp& newApp : newAppList) { for (NvApp& newApp : newAppList) {
if (existingApp.id == newApp.id) { if (existingApp.id == newApp.id) {
newApp.hidden = existingApp.hidden; newApp.hidden = existingApp.hidden;
newApp.directLaunch = existingApp.directLaunch;
} }
} }
} }

View File

@ -10,6 +10,7 @@ CenteredGridView {
property AppModel appModel : createModel() property AppModel appModel : createModel()
property bool activated property bool activated
property bool showHiddenGames property bool showHiddenGames
property bool showGames
id: appGrid id: appGrid
focus: true focus: true
@ -39,6 +40,19 @@ CenteredGridView {
if (currentIndex == -1 && SdlGamepadKeyNavigation.getConnectedGamepads() > 0) { if (currentIndex == -1 && SdlGamepadKeyNavigation.getConnectedGamepads() > 0) {
currentIndex = 0 currentIndex = 0
} }
if (!showGames && !showHiddenGames) {
// Check if there's a direct launch app
var directLaunchAppIndex = model.getDirectLaunchAppIndex();
if (directLaunchAppIndex >= 0) {
// Start the direct launch app if nothing else is running
currentIndex = directLaunchAppIndex
currentItem.launchOrResumeSelectedApp(false)
// Set showGames so we will not loop when the stream ends
showGames = true
}
}
} }
StackView.onDeactivating: { StackView.onDeactivating: {
@ -112,7 +126,7 @@ CenteredGridView {
} }
onClicked: { onClicked: {
launchOrResumeSelectedApp() launchOrResumeSelectedApp(true)
} }
ToolTip.text: qsTr("Resume Game") ToolTip.text: qsTr("Resume Game")
@ -167,15 +181,18 @@ CenteredGridView {
elide: Text.ElideRight elide: Text.ElideRight
} }
function launchOrResumeSelectedApp() function launchOrResumeSelectedApp(quitExistingApp)
{ {
var runningId = appModel.getRunningAppId() var runningId = appModel.getRunningAppId()
if (runningId !== 0 && runningId !== model.appid) { if (runningId !== 0 && runningId !== model.appid) {
quitAppDialog.appName = appModel.getRunningAppName() if (quitExistingApp) {
quitAppDialog.segueToStream = true quitAppDialog.appName = appModel.getRunningAppName()
quitAppDialog.nextAppName = model.name quitAppDialog.segueToStream = true
quitAppDialog.nextAppIndex = index quitAppDialog.nextAppName = model.name
quitAppDialog.open() quitAppDialog.nextAppIndex = index
quitAppDialog.open()
}
return return
} }
@ -190,7 +207,7 @@ CenteredGridView {
// will handle starting the game and clicks on the box art will // will handle starting the game and clicks on the box art will
// be ignored. // be ignored.
if (!model.running) { if (!model.running) {
launchOrResumeSelectedApp() launchOrResumeSelectedApp(true)
} }
} }
@ -240,7 +257,7 @@ CenteredGridView {
NavigableMenuItem { NavigableMenuItem {
parentMenu: appContextMenu parentMenu: appContextMenu
text: model.running ? qsTr("Resume Game") : qsTr("Launch Game") text: model.running ? qsTr("Resume Game") : qsTr("Launch Game")
onTriggered: launchOrResumeSelectedApp() onTriggered: launchOrResumeSelectedApp(true)
} }
NavigableMenuItem { NavigableMenuItem {
parentMenu: appContextMenu parentMenu: appContextMenu
@ -256,6 +273,19 @@ CenteredGridView {
onTriggered: appModel.setAppHidden(model.index, !model.hidden) onTriggered: appModel.setAppHidden(model.index, !model.hidden)
visible: !model.running || model.hidden visible: !model.running || model.hidden
} }
NavigableMenuItem {
parentMenu: appContextMenu
checkable: true
checked: model.directLaunch
text: qsTr("Direct Launch")
onTriggered: appModel.setAppDirectLaunch(model.index, !model.directLaunch)
visible: !model.hidden
ToolTip.text: qsTr("Launch this app immediately when the host is selected, bypassing the app selection grid.")
ToolTip.delay: 1000
ToolTip.timeout: 3000
ToolTip.visible: hovered
}
} }
} }

View File

@ -167,7 +167,7 @@ CenteredGridView {
text: qsTr("View Apps") text: qsTr("View Apps")
onTriggered: { onTriggered: {
var component = Qt.createComponent("AppView.qml") var component = Qt.createComponent("AppView.qml")
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name}) var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name, "showGames": true})
stackView.push(appView) stackView.push(appView)
} }
visible: model.online && model.paired visible: model.online && model.paired

View File

@ -47,6 +47,17 @@ Session* AppModel::createSessionForApp(int appIndex)
return new Session(m_Computer, app); return new Session(m_Computer, app);
} }
int AppModel::getDirectLaunchAppIndex()
{
for (int i = 0; i < m_AllApps.count(); i++) {
if (m_VisibleApps[i].directLaunch) {
return i;
}
}
return -1;
}
int AppModel::rowCount(const QModelIndex &parent) const int AppModel::rowCount(const QModelIndex &parent) const
{ {
// For list models only the root node (an invalid parent) should return the list's size. For all // For list models only the root node (an invalid parent) should return the list's size. For all
@ -78,6 +89,8 @@ QVariant AppModel::data(const QModelIndex &index, int role) const
return app.hidden; return app.hidden;
case AppIdRole: case AppIdRole:
return app.id; return app.id;
case DirectLaunchRole:
return app.directLaunch;
default: default:
return QVariant(); return QVariant();
} }
@ -92,6 +105,7 @@ QHash<int, QByteArray> AppModel::roleNames() const
names[BoxArtRole] = "boxart"; names[BoxArtRole] = "boxart";
names[HiddenRole] = "hidden"; names[HiddenRole] = "hidden";
names[AppIdRole] = "appid"; names[AppIdRole] = "appid";
names[DirectLaunchRole] = "directLaunch";
return names; return names;
} }
@ -207,6 +221,32 @@ void AppModel::setAppHidden(int appIndex, bool hidden)
m_ComputerManager->clientSideAttributeUpdated(m_Computer); m_ComputerManager->clientSideAttributeUpdated(m_Computer);
} }
void AppModel::setAppDirectLaunch(int appIndex, bool directLaunch)
{
Q_ASSERT(appIndex < m_VisibleApps.count());
int appId = m_VisibleApps.at(appIndex).id;
{
QWriteLocker lock(&m_Computer->lock);
for (NvApp& app : m_Computer->appList) {
if (directLaunch) {
// We must clear direct launch from all other apps
// to set it on the new app.
app.directLaunch = app.id == appId;
}
else if (app.id == appId) {
// If we're clearing direct launch, we're done once we
// find our matching app ID.
app.directLaunch = false;
break;
}
}
}
m_ComputerManager->clientSideAttributeUpdated(m_Computer);
}
void AppModel::handleComputerStateChanged(NvComputer* computer) void AppModel::handleComputerStateChanged(NvComputer* computer)
{ {
// Ignore updates for computers that aren't ours // Ignore updates for computers that aren't ours

View File

@ -17,6 +17,7 @@ class AppModel : public QAbstractListModel
BoxArtRole, BoxArtRole,
HiddenRole, HiddenRole,
AppIdRole, AppIdRole,
DirectLaunchRole
}; };
public: public:
@ -27,6 +28,8 @@ public:
Q_INVOKABLE Session* createSessionForApp(int appIndex); Q_INVOKABLE Session* createSessionForApp(int appIndex);
Q_INVOKABLE int getDirectLaunchAppIndex();
Q_INVOKABLE int getRunningAppId(); Q_INVOKABLE int getRunningAppId();
Q_INVOKABLE QString getRunningAppName(); Q_INVOKABLE QString getRunningAppName();
@ -35,6 +38,8 @@ public:
Q_INVOKABLE void setAppHidden(int appIndex, bool hidden); Q_INVOKABLE void setAppHidden(int appIndex, bool hidden);
Q_INVOKABLE void setAppDirectLaunch(int appIndex, bool directLaunch);
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;