Start GUI rewrite in QML

This commit is contained in:
Cameron Gutman 2018-07-04 14:16:25 -07:00
parent 04c9a3a2eb
commit 6a3b95a4b1
11 changed files with 106 additions and 325 deletions

View file

@ -1,12 +1,8 @@
#-------------------------------------------------
#
# Project created by QtCreator 2018-04-28T14:01:01
#
#-------------------------------------------------
QT += core quick network
CONFIG += c++11
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
# TODO: Rid ourselves of QtWidgets
QT += widgets
TARGET = moonlight-qt
TEMPLATE = app
@ -52,8 +48,6 @@ win32 {
SOURCES += \
main.cpp \
gui/mainwindow.cpp \
gui/popupmanager.cpp \
backend/identitymanager.cpp \
backend/nvhttp.cpp \
backend/nvpairingmanager.cpp \
@ -67,8 +61,6 @@ SOURCES += \
HEADERS += \
utils.h \
gui/mainwindow.h \
gui/popupmanager.h \
backend/identitymanager.h \
backend/nvhttp.h \
backend/nvpairingmanager.h \
@ -78,11 +70,15 @@ HEADERS += \
streaming/input.hpp \
streaming/session.hpp
FORMS += \
gui/mainwindow.ui
RESOURCES += \
resources.qrc
resources.qrc \
qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../moonlight-common-c/release/ -lmoonlight-common-c
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../moonlight-common-c/debug/ -lmoonlight-common-c
@ -104,3 +100,8 @@ else:unix: LIBS += -L$$OUT_PWD/../qmdnsengine/ -lqmdnsengine
INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
DEPENDPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

65
app/gui/main.qml Normal file
View file

@ -0,0 +1,65 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Stack")
header: ToolBar {
contentHeight: toolButton.implicitHeight
ToolButton {
id: toolButton
text: stackView.depth > 1 ? "\u25C0" : "\u2630"
font.pixelSize: Qt.application.font.pixelSize * 1.6
onClicked: {
if (stackView.depth > 1) {
stackView.pop()
} else {
drawer.open()
}
}
}
Label {
text: stackView.currentItem.title
anchors.centerIn: parent
}
}
Drawer {
id: drawer
width: window.width * 0.66
height: window.height
Column {
anchors.fill: parent
ItemDelegate {
text: qsTr("Page 1")
width: parent.width
onClicked: {
stackView.push("Page1Form.ui.qml")
drawer.close()
}
}
ItemDelegate {
text: qsTr("Page 2")
width: parent.width
onClicked: {
stackView.push("Page2Form.ui.qml")
drawer.close()
}
}
}
}
StackView {
id: stackView
initialItem: "HomeForm.ui.qml"
anchors.fill: parent
}
}

View file

@ -1,105 +0,0 @@
#include "gui/mainwindow.h"
#include "ui_mainwindow.h"
#include "gui/popupmanager.h"
#include "backend/identitymanager.h"
#include "backend/nvpairingmanager.h"
#include "streaming/session.hpp"
#include "backend/computermanager.h"
#include "backend/boxartmanager.h"
#include "settings/streamingpreferences.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_BoxArtManager(this),
m_ComputerManager(this)
{
ui->setupUi(this);
connect(&m_BoxArtManager, SIGNAL(boxArtLoadComplete(NvComputer*,NvApp,QImage)),
this, SLOT(boxArtLoadComplete(NvComputer*,NvApp,QImage)));
connect(&m_ComputerManager, SIGNAL(computerStateChanged(NvComputer*)),
this, SLOT(computerStateChanged(NvComputer*)));
m_ComputerManager.startPolling();
qDebug() << "Cached computers: " << m_ComputerManager.getComputers().count();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::boxArtLoadComplete(NvComputer* computer, NvApp app, QImage image)
{
qDebug() << "Loaded image";
}
void MainWindow::on_actionExit_triggered()
{
exit(EXIT_SUCCESS);
}
void MainWindow::computerStateChanged(NvComputer* computer)
{
QReadLocker lock(&computer->lock);
NvHTTP http(computer->activeAddress);
if (computer->pairState == NvComputer::PS_NOT_PAIRED) {
NvPairingManager pm(computer->activeAddress);
QString pin = pm.generatePinString();
pm.pair(http.getServerInfo(), pin);
}
else if (!computer->appList.isEmpty()) {
QImage im = m_BoxArtManager.loadBoxArt(computer, computer->appList[0]);
// Stop polling before launching a game
m_ComputerManager.stopPollingAsync();
Session session(computer, computer->appList.last());
QStringList warnings;
QString errorMessage;
// First check for a fatal configuration error
errorMessage = session.checkForFatalValidationError();
if (!errorMessage.isEmpty()) {
// TODO: display error dialog
goto AfterStreaming;
}
// Check for any informational messages to display
warnings = session.checkForAdvisoryValidationError();
if (!warnings.isEmpty()) {
// TODO: display toast or something before we start
}
// Run the streaming session until termination
session.exec();
AfterStreaming:
m_ComputerManager.startPolling();
}
}
void MainWindow::on_newHostBtn_clicked()
{
QString hostname = popupmanager::getHostnameDialog(this);
if (!hostname.isEmpty()) {
m_ComputerManager.addNewHost(hostname, false);
}
}
void MainWindow::addHostToDisplay(QMap<QString, bool> hostMdnsMap) {
QMapIterator<QString, bool> i(hostMdnsMap);
while (i.hasNext()) {
i.next();
ui->hostSelectCombo->addItem(i.key());
// we can ignore the mdns for now, it's only useful for displaying unpairing options
}
}
void MainWindow::on_selectHostComboBox_activated(const QString &selectedHostname)
{
// TODO: get all the applications that "selectedHostname" has listed
// probably populate another combobox of applications for the time being
}

View file

@ -1,36 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "backend/computermanager.h"
#include "backend/boxartmanager.h"
#include <QMainWindow>
#include <QtWidgets>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_actionExit_triggered();
void on_newHostBtn_clicked();
void addHostToDisplay(QMap<QString, bool>);
void on_selectHostComboBox_activated(const QString &);
void computerStateChanged(NvComputer* computer);
void boxArtLoadComplete(NvComputer* computer, NvApp app, QImage image);
private:
Ui::MainWindow *ui;
BoxArtManager m_BoxArtManager;
ComputerManager m_ComputerManager;
};
#endif // MAINWINDOW_H

View file

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>483</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>50</x>
<y>30</y>
<width>306</width>
<height>191</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="newHostBtn">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>Add New Host</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/res/icon128.png</normaloff>:/res/icon128.png</iconset>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="hostSelectCombo"/>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSettings"/>
<addaction name="actionGamepad_Mapping"/>
<addaction name="actionExit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<action name="actionSettings">
<property name="text">
<string>Settings</string>
</property>
</action>
<action name="actionGamepad_Mapping">
<property name="text">
<string>Gamepad Mapping</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -1,41 +0,0 @@
#include "popupmanager.h"
QMessageBox *popupmanager::pinMsgBox = nullptr;
popupmanager::popupmanager(){}
// this opens a non-blocking informative message telling the user to enter the given pin
// it is open-loop: if the user cancels, nothing happens
// it is expected that upon pairing completion, the ::closePinDialog function will be called.
void popupmanager::displayPinDialog(QString pin, QWidget* parent) {
popupmanager::pinMsgBox = new QMessageBox( parent );
popupmanager::pinMsgBox->setAttribute( Qt::WA_DeleteOnClose ); //makes sure the msgbox is deleted automatically when closed
popupmanager::pinMsgBox->setStandardButtons( QMessageBox::Ok );
popupmanager::pinMsgBox->setText("Please enter the number " + pin + " on the GFE dialog on the computer.");
popupmanager::pinMsgBox->setInformativeText("This dialog will be dismissed once complete.");
popupmanager::pinMsgBox->open();
}
// to be called when the pairing is complete
void popupmanager::closePinDialog() {
pinMsgBox->close();
delete pinMsgBox;
}
QString popupmanager::getHostnameDialog(QWidget* parent) {
bool ok;
QString responseHost
= QInputDialog::getText(parent, QObject::tr("Add Host Manually"),
QObject::tr("IP Address or Hostname of GeForce PC"),
QLineEdit::Normal,
QObject::tr("default string"),
&ok);
if (ok && !responseHost.isEmpty()) {
return responseHost;
} else {
return QObject::tr("");
}
}

View file

@ -1,18 +0,0 @@
#ifndef POPUPMANAGER_H
#define POPUPMANAGER_H
#include <QtWidgets>
class popupmanager
{
public:
popupmanager();
static void displayPinDialog(QString pin, QWidget* parent);
static void closePinDialog();
static QString getHostnameDialog(QWidget* parent);
private:
static QMessageBox *pinMsgBox;
};
#endif // POPUPMANAGER_H

View file

@ -0,0 +1,6 @@
; This file can be edited to change the style of the application
; Read "Qt Quick Controls 2 Configuration File" for details:
; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
[Controls]
Style=Imagine

View file

@ -1,5 +1,5 @@
#include "gui/mainwindow.h"
#include <QApplication>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "backend/nvhttp.h"
@ -10,6 +10,8 @@
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// This avoids using the default keychain for SSL, which may cause
// password prompts on macOS.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
@ -22,9 +24,13 @@ int main(int argc, char *argv[])
// Register custom metatypes for use in signals
qRegisterMetaType<NvApp>("NvApp");
QApplication a(argc, argv);
MainWindow w;
w.show();
QGuiApplication app(argc, argv);
// Load the main.qml file
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/gui/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// Ensure that SDL is always initialized since we may need to use it
// for non-streaming purposes (like checking on audio devices)
@ -37,7 +43,7 @@ int main(int argc, char *argv[])
SDL_GetError());
}
int err = a.exec();
int err = app.exec();
SDL_Quit();

6
app/qml.qrc Normal file
View file

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>gui/main.qml</file>
<file>gui/qtquickcontrols2.conf</file>
</qresource>
</RCC>

View file

@ -7,7 +7,6 @@
#include <QMessageBox>
class Session
{
public: