mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2025-07-01 23:35:55 +00:00
Start GUI rewrite in QML
This commit is contained in:
parent
04c9a3a2eb
commit
6a3b95a4b1
33
app/app.pro
33
app/app.pro
@ -1,12 +1,8 @@
|
|||||||
#-------------------------------------------------
|
QT += core quick network
|
||||||
#
|
CONFIG += c++11
|
||||||
# Project created by QtCreator 2018-04-28T14:01:01
|
|
||||||
#
|
|
||||||
#-------------------------------------------------
|
|
||||||
|
|
||||||
QT += core gui network
|
# TODO: Rid ourselves of QtWidgets
|
||||||
|
QT += widgets
|
||||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
|
||||||
|
|
||||||
TARGET = moonlight-qt
|
TARGET = moonlight-qt
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
@ -52,8 +48,6 @@ win32 {
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
gui/mainwindow.cpp \
|
|
||||||
gui/popupmanager.cpp \
|
|
||||||
backend/identitymanager.cpp \
|
backend/identitymanager.cpp \
|
||||||
backend/nvhttp.cpp \
|
backend/nvhttp.cpp \
|
||||||
backend/nvpairingmanager.cpp \
|
backend/nvpairingmanager.cpp \
|
||||||
@ -67,8 +61,6 @@ SOURCES += \
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
utils.h \
|
utils.h \
|
||||||
gui/mainwindow.h \
|
|
||||||
gui/popupmanager.h \
|
|
||||||
backend/identitymanager.h \
|
backend/identitymanager.h \
|
||||||
backend/nvhttp.h \
|
backend/nvhttp.h \
|
||||||
backend/nvpairingmanager.h \
|
backend/nvpairingmanager.h \
|
||||||
@ -78,11 +70,15 @@ HEADERS += \
|
|||||||
streaming/input.hpp \
|
streaming/input.hpp \
|
||||||
streaming/session.hpp
|
streaming/session.hpp
|
||||||
|
|
||||||
FORMS += \
|
|
||||||
gui/mainwindow.ui
|
|
||||||
|
|
||||||
RESOURCES += \
|
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
|
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
|
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
|
INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
|
||||||
DEPENDPATH += $$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
65
app/gui/main.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
6
app/gui/qtquickcontrols2.conf
Normal file
6
app/gui/qtquickcontrols2.conf
Normal 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
|
18
app/main.cpp
18
app/main.cpp
@ -1,5 +1,5 @@
|
|||||||
#include "gui/mainwindow.h"
|
#include <QGuiApplication>
|
||||||
#include <QApplication>
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
#include "backend/nvhttp.h"
|
#include "backend/nvhttp.h"
|
||||||
|
|
||||||
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
// This avoids using the default keychain for SSL, which may cause
|
// This avoids using the default keychain for SSL, which may cause
|
||||||
// password prompts on macOS.
|
// password prompts on macOS.
|
||||||
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
|
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
|
// Register custom metatypes for use in signals
|
||||||
qRegisterMetaType<NvApp>("NvApp");
|
qRegisterMetaType<NvApp>("NvApp");
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
MainWindow w;
|
|
||||||
w.show();
|
// 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
|
// Ensure that SDL is always initialized since we may need to use it
|
||||||
// for non-streaming purposes (like checking on audio devices)
|
// for non-streaming purposes (like checking on audio devices)
|
||||||
@ -37,7 +43,7 @@ int main(int argc, char *argv[])
|
|||||||
SDL_GetError());
|
SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = a.exec();
|
int err = app.exec();
|
||||||
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
|
6
app/qml.qrc
Normal file
6
app/qml.qrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>gui/main.qml</file>
|
||||||
|
<file>gui/qtquickcontrols2.conf</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
|
||||||
class Session
|
class Session
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user