FGQCanvas: Multi-window support (for RPi4)
To support EGLFS on the RPi4 dual outputs, enable multiple windows within a single process.
This commit is contained in:
parent
331ef3232f
commit
6e465f9dbe
13 changed files with 341 additions and 94 deletions
86
utils/fgqcanvas/WindowData.cpp
Normal file
86
utils/fgqcanvas/WindowData.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#include "WindowData.h"
|
||||||
|
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "jsonutils.h"
|
||||||
|
|
||||||
|
WindowData::WindowData(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject WindowData::saveState() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
json["rect"] = rectToJsonArray(m_windowRect);
|
||||||
|
if (!m_screenName.isEmpty()) {
|
||||||
|
json["screen"] = m_screenName;
|
||||||
|
}
|
||||||
|
if (!m_title.isEmpty()) {
|
||||||
|
json["title"] = m_title;
|
||||||
|
}
|
||||||
|
// support frameless option here?
|
||||||
|
json["state"] = static_cast<int>(m_state);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowData::restoreState(QJsonObject state)
|
||||||
|
{
|
||||||
|
m_windowRect = jsonArrayToRect(state.value("rect").toArray());
|
||||||
|
emit windowRectChanged(m_windowRect);
|
||||||
|
|
||||||
|
if (state.contains("screen")) {
|
||||||
|
m_screenName = state.value("screen").toString();
|
||||||
|
} else {
|
||||||
|
m_screenName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.contains("title")) {
|
||||||
|
m_title = state.value("title").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.contains("state")) {
|
||||||
|
m_state = static_cast<Qt::WindowState>(state.value("state").toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect WindowData::windowRect() const
|
||||||
|
{
|
||||||
|
return m_windowRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScreen *WindowData::screen() const
|
||||||
|
{
|
||||||
|
if (m_screenName.isEmpty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
QStringList screenNames;
|
||||||
|
Q_FOREACH(auto s, qApp->screens()) {
|
||||||
|
if (s->name() == m_screenName) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
screenNames.append(s->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "couldn't find a screen with name:" << m_screenName;
|
||||||
|
qWarning() << "Available screens:" << screenNames.join(", ");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowData::setWindowState(Qt::WindowState ws)
|
||||||
|
{
|
||||||
|
m_state = ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowData::setWindowRect(QRect windowRect)
|
||||||
|
{
|
||||||
|
if (m_windowRect == windowRect)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_windowRect = windowRect;
|
||||||
|
emit windowRectChanged(m_windowRect);
|
||||||
|
}
|
46
utils/fgqcanvas/WindowData.h
Normal file
46
utils/fgqcanvas/WindowData.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef WINDOWDATA_H
|
||||||
|
#define WINDOWDATA_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
|
class QScreen;
|
||||||
|
|
||||||
|
class WindowData : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QRect windowRect READ windowRect WRITE setWindowRect NOTIFY windowRectChanged)
|
||||||
|
public:
|
||||||
|
explicit WindowData(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QJsonObject saveState() const;
|
||||||
|
bool restoreState(QJsonObject state);
|
||||||
|
|
||||||
|
QRect windowRect() const;
|
||||||
|
QScreen* screen() const;
|
||||||
|
|
||||||
|
Qt::WindowState windowState() const
|
||||||
|
{ return m_state; }
|
||||||
|
|
||||||
|
void setWindowState(Qt::WindowState ws);
|
||||||
|
|
||||||
|
QString title() const
|
||||||
|
{ return m_title; }
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void windowRectChanged(QRect windowRect);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void setWindowRect(QRect windowRect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRect m_windowRect;
|
||||||
|
Qt::WindowState m_state = Qt::WindowNoState;
|
||||||
|
QString m_screenName;
|
||||||
|
QString m_title;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WINDOWDATA_H
|
|
@ -36,9 +36,12 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QQuickView>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
#include "jsonutils.h"
|
#include "jsonutils.h"
|
||||||
#include "canvasconnection.h"
|
#include "canvasconnection.h"
|
||||||
|
#include "WindowData.h"
|
||||||
|
|
||||||
ApplicationController::ApplicationController(QObject *parent)
|
ApplicationController::ApplicationController(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -72,18 +75,6 @@ ApplicationController::~ApplicationController()
|
||||||
delete m_netAccess;
|
delete m_netAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationController::setWindow(QWindow *window)
|
|
||||||
{
|
|
||||||
m_window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplicationController::restoreWindowState()
|
|
||||||
{
|
|
||||||
if (!m_window)
|
|
||||||
return;
|
|
||||||
m_window->setWindowState(m_windowState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplicationController::loadFromFile(QString path)
|
void ApplicationController::loadFromFile(QString path)
|
||||||
{
|
{
|
||||||
if (!QFile::exists(path)) {
|
if (!QFile::exists(path)) {
|
||||||
|
@ -104,6 +95,42 @@ void ApplicationController::setDaemonMode()
|
||||||
m_daemonMode = true;
|
m_daemonMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplicationController::createWindows()
|
||||||
|
{
|
||||||
|
if (m_windowList.empty()) {
|
||||||
|
defineDefaultWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < m_windowList.size(); ++index) {
|
||||||
|
auto wd = m_windowList.at(index);
|
||||||
|
QQuickView* qqv = new QQuickView;
|
||||||
|
qqv->rootContext()->setContextProperty("_application", this);
|
||||||
|
qqv->rootContext()->setContextProperty("_windowNumber", index);
|
||||||
|
qqv->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
|
qqv->setSource(QUrl{"qrc:///qml/Window.qml"});
|
||||||
|
qqv->setTitle(wd->title());
|
||||||
|
|
||||||
|
if (m_daemonMode) {
|
||||||
|
qqv->setScreen(wd->screen());
|
||||||
|
qqv->setGeometry(wd->windowRect());
|
||||||
|
qqv->setWindowState(wd->windowState());
|
||||||
|
} else {
|
||||||
|
// interactive mode, restore window size etc
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
qqv->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::defineDefaultWindow()
|
||||||
|
{
|
||||||
|
auto w = new WindowData(this);
|
||||||
|
w->setWindowRect(QRect{0, 0, 1024, 768});
|
||||||
|
m_windowList.append(w);
|
||||||
|
emit windowListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void ApplicationController::save(QString configName)
|
void ApplicationController::save(QString configName)
|
||||||
{
|
{
|
||||||
QDir d(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
QDir d(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
@ -364,6 +391,11 @@ QQmlListProperty<CanvasConnection> ApplicationController::activeCanvases()
|
||||||
return QQmlListProperty<CanvasConnection>(this, m_activeCanvases);
|
return QQmlListProperty<CanvasConnection>(this, m_activeCanvases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<WindowData> ApplicationController::windowList()
|
||||||
|
{
|
||||||
|
return QQmlListProperty<WindowData>(this, m_windowList);
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkAccessManager *ApplicationController::netAccess() const
|
QNetworkAccessManager *ApplicationController::netAccess() const
|
||||||
{
|
{
|
||||||
return m_netAccess;
|
return m_netAccess;
|
||||||
|
@ -371,6 +403,9 @@ QNetworkAccessManager *ApplicationController::netAccess() const
|
||||||
|
|
||||||
bool ApplicationController::showUI() const
|
bool ApplicationController::showUI() const
|
||||||
{
|
{
|
||||||
|
if (m_daemonMode)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (m_blockUIIdle)
|
if (m_blockUIIdle)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -490,10 +525,12 @@ QByteArray ApplicationController::saveState(QString name) const
|
||||||
}
|
}
|
||||||
|
|
||||||
json["canvases"] = canvases;
|
json["canvases"] = canvases;
|
||||||
if (m_window) {
|
|
||||||
json["window-rect"] = rectToJsonArray(m_window->geometry());
|
QJsonArray windows;
|
||||||
json["window-state"] = static_cast<int>(m_window->windowState());
|
Q_FOREACH (auto w, m_windowList) {
|
||||||
|
windows.append(w->saveState());
|
||||||
}
|
}
|
||||||
|
json["windows"] = windows;
|
||||||
|
|
||||||
// background color?
|
// background color?
|
||||||
|
|
||||||
|
@ -509,18 +546,29 @@ void ApplicationController::restoreState(QByteArray bytes)
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes);
|
||||||
QJsonObject json = jsonDoc.object();
|
QJsonObject json = jsonDoc.object();
|
||||||
|
|
||||||
if (m_window) {
|
// clear windows
|
||||||
QRect r = jsonArrayToRect(json.value("window-rect").toArray());
|
Q_FOREACH(auto w, m_windowList) {
|
||||||
if (r.isValid()) {
|
w->deleteLater();
|
||||||
m_window->setGeometry(r);
|
}
|
||||||
|
m_windowList.clear();
|
||||||
|
|
||||||
|
for (auto w : json.value("windows").toArray()) {
|
||||||
|
auto wd = new WindowData(this);
|
||||||
|
m_windowList.append(wd);
|
||||||
|
wd->restoreState(w.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have to cache the state becuase it seems to be ignored
|
if (m_windowList.isEmpty()) {
|
||||||
// before show is called();
|
// check for previous single-window data
|
||||||
m_windowState = static_cast<Qt::WindowState>(json.value("window-state").toInt());
|
auto w = new WindowData(this);
|
||||||
m_window->setWindowState(m_windowState);
|
if (json.contains("window-rect")) {
|
||||||
|
w->setWindowRect(jsonArrayToRect(json.value("window-rect").toArray()));
|
||||||
|
}
|
||||||
|
if (json.contains("window-state")) {
|
||||||
|
w->setWindowState(static_cast<Qt::WindowState>(json.value("window-state").toInt()));
|
||||||
|
}
|
||||||
|
m_windowList.append(w);
|
||||||
}
|
}
|
||||||
// background color
|
|
||||||
|
|
||||||
for (auto c : json.value("canvases").toArray()) {
|
for (auto c : json.value("canvases").toArray()) {
|
||||||
auto cc = new CanvasConnection(this);
|
auto cc = new CanvasConnection(this);
|
||||||
|
@ -533,6 +581,7 @@ void ApplicationController::restoreState(QByteArray bytes)
|
||||||
cc->reconnect();
|
cc->reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit windowListChanged();
|
||||||
emit activeCanvasesChanged();
|
emit activeCanvasesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
class CanvasConnection;
|
class CanvasConnection;
|
||||||
class QWindow;
|
class QWindow;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
class WindowData;
|
||||||
|
|
||||||
class ApplicationController : public QObject
|
class ApplicationController : public QObject
|
||||||
{
|
{
|
||||||
|
@ -42,6 +43,7 @@ class ApplicationController : public QObject
|
||||||
|
|
||||||
|
|
||||||
Q_PROPERTY(QQmlListProperty<CanvasConnection> activeCanvases READ activeCanvases NOTIFY activeCanvasesChanged)
|
Q_PROPERTY(QQmlListProperty<CanvasConnection> activeCanvases READ activeCanvases NOTIFY activeCanvasesChanged)
|
||||||
|
Q_PROPERTY(QQmlListProperty<WindowData> windowList READ windowList NOTIFY windowListChanged)
|
||||||
|
|
||||||
Q_ENUMS(Status)
|
Q_ENUMS(Status)
|
||||||
|
|
||||||
|
@ -54,15 +56,12 @@ class ApplicationController : public QObject
|
||||||
Q_PROPERTY(bool showGettingStarted READ showGettingStarted WRITE setShowGettingStarted NOTIFY showGettingStartedChanged)
|
Q_PROPERTY(bool showGettingStarted READ showGettingStarted WRITE setShowGettingStarted NOTIFY showGettingStartedChanged)
|
||||||
public:
|
public:
|
||||||
explicit ApplicationController(QObject *parent = nullptr);
|
explicit ApplicationController(QObject *parent = nullptr);
|
||||||
~ApplicationController();
|
~ApplicationController() override;
|
||||||
|
|
||||||
void setWindow(QWindow* window);
|
|
||||||
|
|
||||||
void restoreWindowState();
|
|
||||||
|
|
||||||
void loadFromFile(QString path);
|
void loadFromFile(QString path);
|
||||||
|
|
||||||
void setDaemonMode();
|
void setDaemonMode();
|
||||||
|
void createWindows();
|
||||||
|
|
||||||
Q_INVOKABLE void query();
|
Q_INVOKABLE void query();
|
||||||
Q_INVOKABLE void cancelQuery();
|
Q_INVOKABLE void cancelQuery();
|
||||||
|
@ -86,6 +85,7 @@ public:
|
||||||
QVariantList canvases() const;
|
QVariantList canvases() const;
|
||||||
|
|
||||||
QQmlListProperty<CanvasConnection> activeCanvases();
|
QQmlListProperty<CanvasConnection> activeCanvases();
|
||||||
|
QQmlListProperty<WindowData> windowList();
|
||||||
|
|
||||||
QNetworkAccessManager* netAccess() const;
|
QNetworkAccessManager* netAccess() const;
|
||||||
|
|
||||||
|
@ -129,6 +129,7 @@ signals:
|
||||||
void portChanged(unsigned int port);
|
void portChanged(unsigned int port);
|
||||||
|
|
||||||
void activeCanvasesChanged();
|
void activeCanvasesChanged();
|
||||||
|
void windowListChanged();
|
||||||
|
|
||||||
void canvasListChanged();
|
void canvasListChanged();
|
||||||
void statusChanged(Status status);
|
void statusChanged(Status status);
|
||||||
|
@ -179,6 +180,8 @@ private:
|
||||||
|
|
||||||
QByteArray createSnapshot(QString name) const;
|
QByteArray createSnapshot(QString name) const;
|
||||||
|
|
||||||
|
void defineDefaultWindow();
|
||||||
|
|
||||||
QString m_host;
|
QString m_host;
|
||||||
unsigned int m_port;
|
unsigned int m_port;
|
||||||
QVariantList m_canvases;
|
QVariantList m_canvases;
|
||||||
|
@ -189,8 +192,7 @@ private:
|
||||||
QNetworkReply* m_query = nullptr;
|
QNetworkReply* m_query = nullptr;
|
||||||
QVariantList m_snapshots;
|
QVariantList m_snapshots;
|
||||||
|
|
||||||
QWindow* m_window = nullptr;
|
QList<WindowData*> m_windowList;
|
||||||
Qt::WindowState m_windowState = Qt::WindowNoState;
|
|
||||||
|
|
||||||
bool m_daemonMode = false;
|
bool m_daemonMode = false;
|
||||||
bool m_showUI = true;
|
bool m_showUI = true;
|
||||||
|
|
|
@ -77,6 +77,7 @@ QJsonObject CanvasConnection::saveState() const
|
||||||
json["url"] = m_webSocketUrl.toString();
|
json["url"] = m_webSocketUrl.toString();
|
||||||
json["path"] = QString::fromUtf8(m_rootPropertyPath);
|
json["path"] = QString::fromUtf8(m_rootPropertyPath);
|
||||||
json["rect"] = rectToJsonArray(m_destRect.toRect());
|
json["rect"] = rectToJsonArray(m_destRect.toRect());
|
||||||
|
json["window"] = m_windowIndex;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +87,10 @@ bool CanvasConnection::restoreState(QJsonObject state)
|
||||||
m_rootPropertyPath = state.value("path").toString().toUtf8();
|
m_rootPropertyPath = state.value("path").toString().toUtf8();
|
||||||
m_destRect = jsonArrayToRect(state.value("rect").toArray());
|
m_destRect = jsonArrayToRect(state.value("rect").toArray());
|
||||||
|
|
||||||
|
if (state.contains("window")) {
|
||||||
|
m_windowIndex = state.value("window").toInt();
|
||||||
|
}
|
||||||
|
|
||||||
emit geometryChanged();
|
emit geometryChanged();
|
||||||
emit rootPathChanged();
|
emit rootPathChanged();
|
||||||
emit webSocketUrlChanged();
|
emit webSocketUrlChanged();
|
||||||
|
@ -167,6 +172,14 @@ QSizeF CanvasConnection::size() const
|
||||||
return m_destRect.size();
|
return m_destRect.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setWindowIndex(int index)
|
||||||
|
{
|
||||||
|
if (m_windowIndex != index) {
|
||||||
|
m_windowIndex = index;
|
||||||
|
emit geometryChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LocalProp *CanvasConnection::propertyRoot() const
|
LocalProp *CanvasConnection::propertyRoot() const
|
||||||
{
|
{
|
||||||
return m_localPropertyRoot.get();
|
return m_localPropertyRoot.get();
|
||||||
|
|
|
@ -46,6 +46,8 @@ class CanvasConnection : public QObject
|
||||||
Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY geometryChanged)
|
Q_PROPERTY(QPointF origin READ origin WRITE setOrigin NOTIFY geometryChanged)
|
||||||
Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY geometryChanged)
|
Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY geometryChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(int windowIndex READ windowIndex WRITE setWindowIndex NOTIFY geometryChanged)
|
||||||
|
|
||||||
Q_PROPERTY(QUrl webSocketUrl READ webSocketUrl NOTIFY webSocketUrlChanged)
|
Q_PROPERTY(QUrl webSocketUrl READ webSocketUrl NOTIFY webSocketUrlChanged)
|
||||||
Q_PROPERTY(QString rootPath READ rootPath NOTIFY rootPathChanged)
|
Q_PROPERTY(QString rootPath READ rootPath NOTIFY rootPathChanged)
|
||||||
public:
|
public:
|
||||||
|
@ -84,6 +86,13 @@ public:
|
||||||
|
|
||||||
QSizeF size() const;
|
QSizeF size() const;
|
||||||
|
|
||||||
|
int windowIndex() const
|
||||||
|
{
|
||||||
|
return m_windowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWindowIndex(int index);
|
||||||
|
|
||||||
LocalProp* propertyRoot() const;
|
LocalProp* propertyRoot() const;
|
||||||
|
|
||||||
QUrl webSocketUrl() const
|
QUrl webSocketUrl() const
|
||||||
|
@ -132,6 +141,7 @@ private:
|
||||||
QUrl m_webSocketUrl;
|
QUrl m_webSocketUrl;
|
||||||
QByteArray m_rootPropertyPath;
|
QByteArray m_rootPropertyPath;
|
||||||
QRectF m_destRect;
|
QRectF m_destRect;
|
||||||
|
int m_windowIndex = 0;
|
||||||
|
|
||||||
QWebSocket m_webSocket;
|
QWebSocket m_webSocket;
|
||||||
QNetworkAccessManager* m_netAccess = nullptr;
|
QNetworkAccessManager* m_netAccess = nullptr;
|
||||||
|
|
|
@ -108,7 +108,6 @@ void CanvasPaintedDisplay::onConnectionStatusChanged()
|
||||||
|
|
||||||
void CanvasPaintedDisplay::buildElements()
|
void CanvasPaintedDisplay::buildElements()
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
m_rootElement = new FGCanvasGroup(nullptr, m_connection->propertyRoot());
|
m_rootElement = new FGCanvasGroup(nullptr, m_connection->propertyRoot());
|
||||||
// this is important to elements can discover their connection
|
// this is important to elements can discover their connection
|
||||||
// by walking their parent chain
|
// by walking their parent chain
|
||||||
|
|
32
utils/fgqcanvas/configs/example_config.json
Normal file
32
utils/fgqcanvas/configs/example_config.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"canvases": [
|
||||||
|
{
|
||||||
|
"path": "/canvas/by-index/texture[4]",
|
||||||
|
"rect": [
|
||||||
|
300,
|
||||||
|
253,
|
||||||
|
852,
|
||||||
|
745
|
||||||
|
],
|
||||||
|
"url": "ws://localhost:8080/PropertyTreeMirror/canvas/by-index/texture[4]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/canvas/by-index/texture[7]",
|
||||||
|
"rect": [
|
||||||
|
1171,
|
||||||
|
259,
|
||||||
|
747,
|
||||||
|
711
|
||||||
|
],
|
||||||
|
"url": "ws://localhost:8080/PropertyTreeMirror/canvas/by-index/texture[7]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configName": "738_captain",
|
||||||
|
"window-rect": [
|
||||||
|
1001,
|
||||||
|
2188,
|
||||||
|
1920,
|
||||||
|
1052
|
||||||
|
],
|
||||||
|
"window-state": 2
|
||||||
|
}
|
38
utils/fgqcanvas/configs/multi_window_config.json
Normal file
38
utils/fgqcanvas/configs/multi_window_config.json
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"canvases": [
|
||||||
|
{
|
||||||
|
"path": "/canvas/by-index/texture[4]",
|
||||||
|
"rect": [
|
||||||
|
300,
|
||||||
|
253,
|
||||||
|
852,
|
||||||
|
745
|
||||||
|
],
|
||||||
|
"url": "ws://localhost:8080/PropertyTreeMirror/canvas/by-index/texture[4]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/canvas/by-index/texture[7]",
|
||||||
|
"rect": [
|
||||||
|
1171,
|
||||||
|
259,
|
||||||
|
747,
|
||||||
|
711
|
||||||
|
],
|
||||||
|
"window":1,
|
||||||
|
"url": "ws://localhost:8080/PropertyTreeMirror/canvas/by-index/texture[7]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configName": "738_captain",
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "First Window",
|
||||||
|
"rect":[100, 100, 500, 300]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Another Window",
|
||||||
|
"rect":[150, 400, 500, 300],
|
||||||
|
"screen":"Colour LCD"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ TARGET = fgqcanvas
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
SOURCES += main.cpp\
|
SOURCES += main.cpp\
|
||||||
|
WindowData.cpp \
|
||||||
fgcanvasgroup.cpp \
|
fgcanvasgroup.cpp \
|
||||||
fgcanvaselement.cpp \
|
fgcanvaselement.cpp \
|
||||||
fgcanvaspaintcontext.cpp \
|
fgcanvaspaintcontext.cpp \
|
||||||
|
@ -25,6 +26,7 @@ SOURCES += main.cpp\
|
||||||
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
WindowData.h \
|
||||||
fgcanvasgroup.h \
|
fgcanvasgroup.h \
|
||||||
fgcanvaselement.h \
|
fgcanvaselement.h \
|
||||||
fgcanvaspaintcontext.h \
|
fgcanvaspaintcontext.h \
|
||||||
|
@ -48,7 +50,8 @@ RESOURCES += \
|
||||||
|
|
||||||
OTHER_FILES += \
|
OTHER_FILES += \
|
||||||
qml/* \
|
qml/* \
|
||||||
doc/*
|
doc/* \
|
||||||
|
config/*
|
||||||
|
|
||||||
#Q_XCODE_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
|
#Q_XCODE_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
|
||||||
#Q_XCODE_DEVELOPMENT_TEAM.value = "James Turner"
|
#Q_XCODE_DEVELOPMENT_TEAM.value = "James Turner"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>qml/mainMenu.qml</file>
|
<file>qml/Window.qml</file>
|
||||||
<file>qml/Button.qml</file>
|
<file>qml/Button.qml</file>
|
||||||
<file>qml/InputLine.qml</file>
|
<file>qml/InputLine.qml</file>
|
||||||
<file alias="images/checkerboard">qml/checkerboard.png</file>
|
<file alias="images/checkerboard">qml/checkerboard.png</file>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "canvasdisplay.h"
|
#include "canvasdisplay.h"
|
||||||
#include "canvasconnection.h"
|
#include "canvasconnection.h"
|
||||||
#include "canvaspainteddisplay.h"
|
#include "canvaspainteddisplay.h"
|
||||||
|
#include "WindowData.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -38,12 +39,6 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.addPositionalArgument("config", QCoreApplication::translate("main", "JSON configuration to load"));
|
parser.addPositionalArgument("config", QCoreApplication::translate("main", "JSON configuration to load"));
|
||||||
QCommandLineOption framelessOption(QStringList() << "frameless",
|
|
||||||
QCoreApplication::translate("main", "Use a frameless window"));
|
|
||||||
QCommandLineOption screenOption(QStringList() << "screen",
|
|
||||||
QCoreApplication::translate("main", "Run full-screen on <scren>"), "screen");
|
|
||||||
parser.addOption(framelessOption);
|
|
||||||
parser.addOption(screenOption);
|
|
||||||
parser.process(a);
|
parser.process(a);
|
||||||
|
|
||||||
ApplicationController appController;
|
ApplicationController appController;
|
||||||
|
@ -52,48 +47,10 @@ int main(int argc, char *argv[])
|
||||||
qmlRegisterType<CanvasDisplay>("FlightGear", 1, 0, "CanvasDisplay");
|
qmlRegisterType<CanvasDisplay>("FlightGear", 1, 0, "CanvasDisplay");
|
||||||
qmlRegisterType<CanvasPaintedDisplay>("FlightGear", 1, 0, "PaintedCanvasDisplay");
|
qmlRegisterType<CanvasPaintedDisplay>("FlightGear", 1, 0, "PaintedCanvasDisplay");
|
||||||
|
|
||||||
|
qmlRegisterUncreatableType<WindowData>("FlightGear", 1, 0, "WindowData", "Don't create me");
|
||||||
qmlRegisterUncreatableType<CanvasConnection>("FlightGear", 1, 0, "CanvasConnection", "Don't create me");
|
qmlRegisterUncreatableType<CanvasConnection>("FlightGear", 1, 0, "CanvasConnection", "Don't create me");
|
||||||
qmlRegisterUncreatableType<ApplicationController>("FlightGear", 1, 0, "Application", "Can't create");
|
qmlRegisterUncreatableType<ApplicationController>("FlightGear", 1, 0, "Application", "Can't create");
|
||||||
|
|
||||||
QQuickView quickView;
|
|
||||||
appController.setWindow(&quickView);
|
|
||||||
if (parser.isSet(framelessOption)) {
|
|
||||||
quickView.setFlag(Qt::FramelessWindowHint, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool restoreWindowState = true;
|
|
||||||
if (parser.isSet(screenOption)) {
|
|
||||||
QString screenName = parser.value(screenOption);
|
|
||||||
QStringList allScreenNames;
|
|
||||||
|
|
||||||
QScreen* found = nullptr;
|
|
||||||
Q_FOREACH(QScreen* s, a.screens()) {
|
|
||||||
allScreenNames << s->name();
|
|
||||||
if (s->name() == screenName) {
|
|
||||||
found = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
qFatal("Unable to find screen: %s: screens are: %s", screenName.toLatin1().data(),
|
|
||||||
allScreenNames.join(",").toLatin1().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
quickView.setScreen(found);
|
|
||||||
quickView.setGeometry(found->geometry());
|
|
||||||
quickView.setFlag(Qt::FramelessWindowHint, true);
|
|
||||||
restoreWindowState = false;
|
|
||||||
|
|
||||||
qInfo() << "Requested to run on screen:" << screenName << "with geometry" << found->geometry();
|
|
||||||
} else {
|
|
||||||
// windows mode, default geeometry
|
|
||||||
quickView.setWidth(1024);
|
|
||||||
quickView.setHeight(768);
|
|
||||||
}
|
|
||||||
|
|
||||||
quickView.rootContext()->setContextProperty("_application", &appController);
|
|
||||||
|
|
||||||
const QStringList args = parser.positionalArguments();
|
const QStringList args = parser.positionalArguments();
|
||||||
|
|
||||||
if (!args.empty()) {
|
if (!args.empty()) {
|
||||||
|
@ -101,15 +58,8 @@ int main(int argc, char *argv[])
|
||||||
appController.loadFromFile(args.front());
|
appController.loadFromFile(args.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
quickView.setResizeMode(QQuickView::SizeRootObjectToView);
|
appController.createWindows();
|
||||||
quickView.setSource(QUrl("qrc:///qml/mainMenu.qml"));
|
|
||||||
quickView.show();
|
|
||||||
|
|
||||||
if (restoreWindowState) {
|
|
||||||
appController.restoreWindowState();
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = a.exec();
|
int result = a.exec();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import FlightGear 1.0 as FG
|
import QtQuick.Window 2.4 as QQ2W
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 1024
|
width: 1024
|
||||||
height: 768
|
height: 768
|
||||||
color: "black"
|
color: "black"
|
||||||
|
|
||||||
property double __uiOpacity: _application.showUI ? 1.0 : 0.0
|
// only show the UI on the main window
|
||||||
|
property double __uiOpacity: (isMainWindow && _application.showUI) ? 1.0 : 0.0
|
||||||
property bool __uiVisible: true
|
property bool __uiVisible: true
|
||||||
|
readonly property bool isMainWindow: (_windowNumber === 0)
|
||||||
|
|
||||||
Behavior on __uiOpacity {
|
Behavior on __uiOpacity {
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
|
@ -27,9 +29,25 @@ Rectangle {
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: _application.activeCanvases
|
model: _application.activeCanvases
|
||||||
delegate: CanvasFrame {
|
|
||||||
id: display
|
// we use a loader to only create canvases on the correct window
|
||||||
canvas: modelData
|
// by driving the 'active' property
|
||||||
|
delegate: Loader {
|
||||||
|
id: canvasLoader
|
||||||
|
sourceComponent: canvasFrame
|
||||||
|
active: modelData.windowIndex === _windowNumber
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: canvasLoader.item
|
||||||
|
property: "canvas"
|
||||||
|
value: model.modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: canvasFrame
|
||||||
|
CanvasFrame {
|
||||||
showUi: __uiVisible
|
showUi: __uiVisible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +76,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
GetStarted {
|
GetStarted {
|
||||||
|
visible: isMainWindow
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue