Migrate to a pure Quick-renderer solution, and QQ UI
This means everything is displayed via OpenGL, and there’s a basic menu system usable.
This commit is contained in:
parent
f13edf01fa
commit
117404979b
28 changed files with 1647 additions and 115 deletions
|
@ -6,17 +6,17 @@ include(GNUInstallDirs)
|
||||||
|
|
||||||
project(FGQCanvas)
|
project(FGQCanvas)
|
||||||
|
|
||||||
find_package(Qt5 5.4 COMPONENTS Widgets WebSockets Gui Quick QuickWidgets)
|
find_package(Qt5 5.4 COMPONENTS Widgets WebSockets Gui Quick)
|
||||||
|
|
||||||
if (NOT Qt5WebSockets_FOUND OR NOT Qt5QuickWidgets_FOUND)
|
if (NOT Qt5WebSockets_FOUND OR NOT Qt5Quick_FOUND)
|
||||||
message(WARNING "FGQCanvas utility requested, but QtWebSockets/QtQuickWidgets not found")
|
message(WARNING "FGQCanvas utility requested, but QtWebSockets not found")
|
||||||
message(STATUS "Check you have the development package for Qt5 WebSockets/QuickWidgets installed")
|
message(STATUS "Check you have the development package for Qt5 WebSockets installed")
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
temporarywidget.cpp
|
#temporarywidget.cpp
|
||||||
temporarywidget.h
|
#temporarywidget.h
|
||||||
main.cpp
|
main.cpp
|
||||||
localprop.cpp
|
localprop.cpp
|
||||||
localprop.h
|
localprop.h
|
||||||
|
@ -46,14 +46,22 @@ set(SOURCES
|
||||||
elementdatamodel.h
|
elementdatamodel.h
|
||||||
canvasitem.cpp
|
canvasitem.cpp
|
||||||
canvasitem.h
|
canvasitem.h
|
||||||
|
canvasconnection.cpp
|
||||||
|
canvasconnection.h
|
||||||
|
applicationcontroller.cpp
|
||||||
|
applicationcontroller.h
|
||||||
|
canvasdisplay.cpp
|
||||||
|
canvasdisplay.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt5_wrap_ui(uic_sources temporarywidget.ui)
|
qt5_add_resources(qrc_sources fgqcanvas_resources.qrc)
|
||||||
|
|
||||||
add_executable(fgqcanvas ${SOURCES} ${uic_sources})
|
#qt5_wrap_ui(uic_sources temporarywidget.ui)
|
||||||
|
|
||||||
|
add_executable(fgqcanvas ${SOURCES} ${qrc_sources})
|
||||||
|
|
||||||
set_property(TARGET fgqcanvas PROPERTY AUTOMOC ON)
|
set_property(TARGET fgqcanvas PROPERTY AUTOMOC ON)
|
||||||
target_link_libraries(fgqcanvas Qt5::Core Qt5::Widgets Qt5::WebSockets Qt5::Quick Qt5::QuickWidgets)
|
target_link_libraries(fgqcanvas Qt5::Core Qt5::Widgets Qt5::WebSockets Qt5::Quick)
|
||||||
|
|
||||||
target_include_directories(fgqcanvas PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(fgqcanvas PRIVATE ${PROJECT_SOURCE_DIR})
|
||||||
|
|
||||||
|
|
293
utils/fgqcanvas/applicationcontroller.cpp
Normal file
293
utils/fgqcanvas/applicationcontroller.cpp
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#include "applicationcontroller.h"
|
||||||
|
|
||||||
|
#include <QNetworkDiskCache>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include "canvasconnection.h"
|
||||||
|
|
||||||
|
ApplicationController::ApplicationController(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_status(Idle)
|
||||||
|
{
|
||||||
|
m_netAccess = new QNetworkAccessManager;
|
||||||
|
m_host = "localhost";
|
||||||
|
m_port = 8080;
|
||||||
|
|
||||||
|
QNetworkDiskCache* cache = new QNetworkDiskCache;
|
||||||
|
cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
m_netAccess->setCache(cache); // takes ownership
|
||||||
|
|
||||||
|
setStatus(Idle);
|
||||||
|
rebuildConfigData();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationController::~ApplicationController()
|
||||||
|
{
|
||||||
|
delete m_netAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::save(QString configName)
|
||||||
|
{
|
||||||
|
QDir d(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
if (!d.exists()) {
|
||||||
|
qWarning() << "creating" << d;
|
||||||
|
d.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert spaces to underscores
|
||||||
|
QString filesystemCleanName = configName.replace(QRegularExpression("[\\s-\\\"/]"), "_");
|
||||||
|
qDebug() << Q_FUNC_INFO << "FS clean name is " << filesystemCleanName;
|
||||||
|
|
||||||
|
QFile f(d.filePath(configName + ".json"));
|
||||||
|
if (f.exists()) {
|
||||||
|
qWarning() << "not over-writing" << f.fileName();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||||
|
f.write(saveState(configName));
|
||||||
|
|
||||||
|
QVariantMap m;
|
||||||
|
m["path"] = f.fileName();
|
||||||
|
m["name"] = configName;
|
||||||
|
m_configs.append(m);
|
||||||
|
emit configListChanged(m_configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::rebuildConfigData()
|
||||||
|
{
|
||||||
|
m_configs.clear();
|
||||||
|
QDir d(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
if (!d.exists()) {
|
||||||
|
emit configListChanged(m_configs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this requires parsing each config in its entirety just to extract
|
||||||
|
// the name, which is horrible.
|
||||||
|
Q_FOREACH (auto entry, d.entryList(QStringList() << "*.json")) {
|
||||||
|
QString path = d.filePath(entry);
|
||||||
|
QFile f(path);
|
||||||
|
f.open(QIODevice::ReadOnly);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(f.readAll());
|
||||||
|
|
||||||
|
QVariantMap m;
|
||||||
|
m["path"] = path;
|
||||||
|
m["name"] = doc.object().value("configName").toString();
|
||||||
|
m_configs.append(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit configListChanged(m_configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::query()
|
||||||
|
{
|
||||||
|
qWarning() << Q_FUNC_INFO << m_host << m_port;
|
||||||
|
|
||||||
|
if (m_host.isEmpty() || (m_port == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QUrl queryUrl;
|
||||||
|
queryUrl.setScheme("http");
|
||||||
|
queryUrl.setHost(m_host);
|
||||||
|
queryUrl.setPort(m_port);
|
||||||
|
queryUrl.setPath("/json/canvas/by-index");
|
||||||
|
queryUrl.setQuery("d=2");
|
||||||
|
|
||||||
|
QNetworkReply* reply = m_netAccess->get(QNetworkRequest(queryUrl));
|
||||||
|
connect(reply, &QNetworkReply::finished,
|
||||||
|
this, &ApplicationController::onFinishedGetCanvasList);
|
||||||
|
|
||||||
|
setStatus(Querying);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::restoreConfig(int index)
|
||||||
|
{
|
||||||
|
QString path = m_configs.at(index).toMap().value("path").toString();
|
||||||
|
QFile f(path);
|
||||||
|
if (!f.open(QIODevice::ReadOnly)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "failed to open the file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreState(f.readAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::openCanvas(QString path)
|
||||||
|
{
|
||||||
|
CanvasConnection* cc = new CanvasConnection(this);
|
||||||
|
|
||||||
|
cc->setNetworkAccess(m_netAccess);
|
||||||
|
m_activeCanvases.append(cc);
|
||||||
|
|
||||||
|
cc->setRootPropertyPath(path.toUtf8());
|
||||||
|
cc->connectWebSocket(m_host.toUtf8(), m_port);
|
||||||
|
|
||||||
|
emit activeCanvasesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApplicationController::host() const
|
||||||
|
{
|
||||||
|
return m_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ApplicationController::port() const
|
||||||
|
{
|
||||||
|
return m_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList ApplicationController::canvases() const
|
||||||
|
{
|
||||||
|
return m_canvases;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<CanvasConnection> ApplicationController::activeCanvases()
|
||||||
|
{
|
||||||
|
return QQmlListProperty<CanvasConnection>(this, m_activeCanvases);
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkAccessManager *ApplicationController::netAccess() const
|
||||||
|
{
|
||||||
|
return m_netAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::setHost(QString host)
|
||||||
|
{
|
||||||
|
if (m_host == host)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_host = host;
|
||||||
|
emit hostChanged(m_host);
|
||||||
|
setStatus(Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::setPort(unsigned int port)
|
||||||
|
{
|
||||||
|
if (m_port == port)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_port = port;
|
||||||
|
emit portChanged(m_port);
|
||||||
|
setStatus(Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject jsonPropNodeFindChild(QJsonObject obj, QByteArray name)
|
||||||
|
{
|
||||||
|
Q_FOREACH (QJsonValue v, obj.value("children").toArray()) {
|
||||||
|
QJsonObject vo = v.toObject();
|
||||||
|
if (vo.value("name").toString() == name) {
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::onFinishedGetCanvasList()
|
||||||
|
{
|
||||||
|
m_canvases.clear();
|
||||||
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
setStatus(QueryFailed);
|
||||||
|
emit canvasListChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
|
||||||
|
|
||||||
|
QJsonArray canvasArray = json.object().value("children").toArray();
|
||||||
|
Q_FOREACH (QJsonValue canvasValue, canvasArray) {
|
||||||
|
QJsonObject canvas = canvasValue.toObject();
|
||||||
|
QString canvasName = jsonPropNodeFindChild(canvas, "name").value("value").toString();
|
||||||
|
QString propPath = canvas.value("path").toString();
|
||||||
|
|
||||||
|
QVariantMap info;
|
||||||
|
info["name"] = canvasName;
|
||||||
|
info["path"] = propPath;
|
||||||
|
m_canvases.append(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit canvasListChanged();
|
||||||
|
setStatus(SuccessfulQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::setStatus(ApplicationController::Status newStatus)
|
||||||
|
{
|
||||||
|
if (newStatus == m_status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_status = newStatus;
|
||||||
|
emit statusChanged(m_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ApplicationController::saveState(QString name) const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
json["configName"] = name;
|
||||||
|
|
||||||
|
QJsonArray canvases;
|
||||||
|
Q_FOREACH (auto canvas, m_activeCanvases) {
|
||||||
|
canvases.append(canvas->saveState());
|
||||||
|
}
|
||||||
|
|
||||||
|
json["canvases"] = canvases;
|
||||||
|
// background color?
|
||||||
|
// window geometry and state?
|
||||||
|
|
||||||
|
QJsonDocument doc;
|
||||||
|
doc.setObject(json);
|
||||||
|
return doc.toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationController::restoreState(QByteArray bytes)
|
||||||
|
{
|
||||||
|
qDeleteAll(m_activeCanvases);
|
||||||
|
m_activeCanvases.clear();
|
||||||
|
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes);
|
||||||
|
QJsonObject json = jsonDoc.object();
|
||||||
|
|
||||||
|
// window size
|
||||||
|
// background color
|
||||||
|
|
||||||
|
for (auto c : json.value("canvases").toArray()) {
|
||||||
|
auto cc = new CanvasConnection(this);
|
||||||
|
cc->setNetworkAccess(m_netAccess);
|
||||||
|
m_activeCanvases.append(cc);
|
||||||
|
cc->restoreState(c.toObject());
|
||||||
|
cc->reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit activeCanvasesChanged();
|
||||||
|
}
|
124
utils/fgqcanvas/applicationcontroller.h
Normal file
124
utils/fgqcanvas/applicationcontroller.h
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifndef APPLICATIONCONTROLLER_H
|
||||||
|
#define APPLICATIONCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QQmlListProperty>
|
||||||
|
#include <QVariantList>
|
||||||
|
|
||||||
|
class CanvasConnection;
|
||||||
|
|
||||||
|
class ApplicationController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
||||||
|
Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
|
||||||
|
Q_PROPERTY(unsigned int port READ port WRITE setPort NOTIFY portChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariantList canvases READ canvases NOTIFY canvasListChanged)
|
||||||
|
Q_PROPERTY(QVariantList configs READ configs NOTIFY configListChanged)
|
||||||
|
|
||||||
|
|
||||||
|
Q_PROPERTY(QQmlListProperty<CanvasConnection> activeCanvases READ activeCanvases NOTIFY activeCanvasesChanged)
|
||||||
|
|
||||||
|
Q_ENUMS(Status)
|
||||||
|
|
||||||
|
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||||
|
public:
|
||||||
|
explicit ApplicationController(QObject *parent = nullptr);
|
||||||
|
~ApplicationController();
|
||||||
|
|
||||||
|
Q_INVOKABLE void save(QString configName);
|
||||||
|
Q_INVOKABLE void query();
|
||||||
|
|
||||||
|
Q_INVOKABLE void restoreConfig(int index);
|
||||||
|
|
||||||
|
Q_INVOKABLE void openCanvas(QString path);
|
||||||
|
|
||||||
|
// void restore();
|
||||||
|
QString host() const;
|
||||||
|
|
||||||
|
unsigned int port() const;
|
||||||
|
|
||||||
|
QVariantList canvases() const;
|
||||||
|
|
||||||
|
QQmlListProperty<CanvasConnection> activeCanvases();
|
||||||
|
|
||||||
|
QNetworkAccessManager* netAccess() const;
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
Idle,
|
||||||
|
Querying,
|
||||||
|
SuccessfulQuery,
|
||||||
|
QueryFailed
|
||||||
|
};
|
||||||
|
|
||||||
|
Status status() const
|
||||||
|
{
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList configs() const
|
||||||
|
{
|
||||||
|
return m_configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void hostChanged(QString host);
|
||||||
|
|
||||||
|
void portChanged(unsigned int port);
|
||||||
|
|
||||||
|
void activeCanvasesChanged();
|
||||||
|
|
||||||
|
void canvasListChanged();
|
||||||
|
void statusChanged(Status status);
|
||||||
|
|
||||||
|
void configListChanged(QVariantList configs);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setHost(QString host);
|
||||||
|
|
||||||
|
void setPort(unsigned int port);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onFinishedGetCanvasList();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setStatus(Status newStatus);
|
||||||
|
|
||||||
|
void rebuildConfigData();
|
||||||
|
|
||||||
|
QByteArray saveState(QString name) const;
|
||||||
|
void restoreState(QByteArray bytes);
|
||||||
|
|
||||||
|
QString m_host;
|
||||||
|
unsigned int m_port;
|
||||||
|
QVariantList m_canvases;
|
||||||
|
QList<CanvasConnection*> m_activeCanvases;
|
||||||
|
QNetworkAccessManager* m_netAccess;
|
||||||
|
Status m_status;
|
||||||
|
QVariantList m_configs;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPLICATIONCONTROLLER_H
|
256
utils/fgqcanvas/canvasconnection.cpp
Normal file
256
utils/fgqcanvas/canvasconnection.cpp
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#include "canvasconnection.h"
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include "localprop.h"
|
||||||
|
|
||||||
|
CanvasConnection::CanvasConnection(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
connect(&m_webSocket, &QWebSocket::connected, this, &CanvasConnection::onWebSocketConnected);
|
||||||
|
connect(&m_webSocket, &QWebSocket::disconnected, this, &CanvasConnection::onWebSocketClosed);
|
||||||
|
connect(&m_webSocket, &QWebSocket::textMessageReceived,
|
||||||
|
this, &CanvasConnection::onTextMessageReceived);
|
||||||
|
|
||||||
|
m_destRect = QRectF(50, 50, 400, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasConnection::~CanvasConnection()
|
||||||
|
{
|
||||||
|
disconnect(&m_webSocket, &QWebSocket::disconnected,
|
||||||
|
this, &CanvasConnection::onWebSocketClosed);
|
||||||
|
m_webSocket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setNetworkAccess(QNetworkAccessManager *dl)
|
||||||
|
{
|
||||||
|
m_netAccess = dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setRootPropertyPath(QByteArray path)
|
||||||
|
{
|
||||||
|
m_rootPropertyPath = path;
|
||||||
|
emit rootPathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject CanvasConnection::saveState() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
json["url"] = m_webSocketUrl.toString();
|
||||||
|
json["path"] = QString::fromUtf8(m_rootPropertyPath);
|
||||||
|
json["rect"] = QJsonArray{m_destRect.x(), m_destRect.y(), m_destRect.width(), m_destRect.height()};
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasConnection::restoreState(QJsonObject state)
|
||||||
|
{
|
||||||
|
m_webSocketUrl = state.value("url").toString();
|
||||||
|
m_rootPropertyPath = state.value("path").toString().toUtf8();
|
||||||
|
QJsonArray rect = state.value("rect").toArray();
|
||||||
|
if (rect.size() < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_destRect = QRectF(rect[0].toDouble(), rect[1].toDouble(),
|
||||||
|
rect[2].toDouble(), rect[3].toDouble());
|
||||||
|
|
||||||
|
emit sizeChanged(m_destRect.size());
|
||||||
|
emit centerChanged(m_destRect.center());
|
||||||
|
emit rootPathChanged();
|
||||||
|
emit webSocketUrlChanged();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::reconnect()
|
||||||
|
{
|
||||||
|
m_webSocket.open(m_webSocketUrl);
|
||||||
|
setStatus(Connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::showDebugTree()
|
||||||
|
{
|
||||||
|
qWarning() << Q_FUNC_INFO << "implement me!";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setCenter(QPointF c)
|
||||||
|
{
|
||||||
|
if (center() == c)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_destRect.moveCenter(c);
|
||||||
|
emit centerChanged(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setSize(QSizeF sz)
|
||||||
|
{
|
||||||
|
if (size() == sz)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_destRect.setSize(sz);
|
||||||
|
emit sizeChanged(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::connectWebSocket(QByteArray hostName, int port)
|
||||||
|
{
|
||||||
|
QUrl wsUrl;
|
||||||
|
wsUrl.setScheme("ws");
|
||||||
|
wsUrl.setHost(hostName);
|
||||||
|
wsUrl.setPort(port);
|
||||||
|
wsUrl.setPath("/PropertyTreeMirror" + m_rootPropertyPath);
|
||||||
|
|
||||||
|
m_webSocketUrl = wsUrl;
|
||||||
|
emit webSocketUrlChanged();
|
||||||
|
|
||||||
|
m_webSocket.open(wsUrl);
|
||||||
|
setStatus(Connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF CanvasConnection::center() const
|
||||||
|
{
|
||||||
|
return m_destRect.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF CanvasConnection::size() const
|
||||||
|
{
|
||||||
|
return m_destRect.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalProp *CanvasConnection::propertyRoot() const
|
||||||
|
{
|
||||||
|
return m_localPropertyRoot.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::onWebSocketConnected()
|
||||||
|
{
|
||||||
|
m_localPropertyRoot.reset(new LocalProp{nullptr, NameIndexTuple("")});
|
||||||
|
|
||||||
|
// ui->canvas->setRootProperty(m_localPropertyRoot);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
FGQCanvasFontCache::instance()->setHost(ui->hostName->text(),
|
||||||
|
ui->portEdit->text().toInt());
|
||||||
|
FGQCanvasImageLoader::instance()->setHost(ui->hostName->text(),
|
||||||
|
ui->portEdit->text().toInt());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setStatus(Connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::onTextMessageReceived(QString message)
|
||||||
|
{
|
||||||
|
QJsonDocument json = QJsonDocument::fromJson(message.toUtf8());
|
||||||
|
if (json.isObject()) {
|
||||||
|
// process new nodes
|
||||||
|
QJsonArray created = json.object().value("created").toArray();
|
||||||
|
Q_FOREACH (QJsonValue v, created) {
|
||||||
|
QJsonObject newProp = v.toObject();
|
||||||
|
|
||||||
|
QByteArray nodePath = newProp.value("path").toString().toUtf8();
|
||||||
|
if (nodePath.indexOf(m_rootPropertyPath) != 0) {
|
||||||
|
qWarning() << "not a property path we are mirroring:" << nodePath;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray localPath = nodePath.mid(m_rootPropertyPath.size() + 1);
|
||||||
|
LocalProp* newNode = propertyFromPath(localPath);
|
||||||
|
newNode->setPosition(newProp.value("position").toInt());
|
||||||
|
// store in the global dict
|
||||||
|
unsigned int propId = newProp.value("id").toInt();
|
||||||
|
if (idPropertyDict.contains(propId)) {
|
||||||
|
qWarning() << "duplicate add of:" << nodePath << "old is" << idPropertyDict.value(propId)->path();
|
||||||
|
} else {
|
||||||
|
idPropertyDict.insert(propId, newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initial value
|
||||||
|
newNode->processChange(newProp.value("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// process removes
|
||||||
|
QJsonArray removed = json.object().value("removed").toArray();
|
||||||
|
Q_FOREACH (QJsonValue v, removed) {
|
||||||
|
unsigned int propId = v.toInt();
|
||||||
|
if (idPropertyDict.contains(propId)) {
|
||||||
|
LocalProp* prop = idPropertyDict.value(propId);
|
||||||
|
idPropertyDict.remove(propId);
|
||||||
|
prop->parent()->removeChild(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process changes
|
||||||
|
QJsonArray changed = json.object().value("changed").toArray();
|
||||||
|
|
||||||
|
Q_FOREACH (QJsonValue v, changed) {
|
||||||
|
QJsonArray change = v.toArray();
|
||||||
|
if (change.size() != 2) {
|
||||||
|
qWarning() << "malformed change notification";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int propId = change.at(0).toInt();
|
||||||
|
if (!idPropertyDict.contains(propId)) {
|
||||||
|
qWarning() << "ignoring unknown prop ID " << propId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalProp* lp = idPropertyDict.value(propId);
|
||||||
|
lp->processChange(change.at(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit updated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::onWebSocketClosed()
|
||||||
|
{
|
||||||
|
qDebug() << "saw web-socket closed";
|
||||||
|
m_localPropertyRoot.reset();
|
||||||
|
idPropertyDict.clear();
|
||||||
|
|
||||||
|
setStatus(Closed);
|
||||||
|
|
||||||
|
// if we're in automatic mode, start reconnection timer
|
||||||
|
// update state
|
||||||
|
|
||||||
|
// ui->stack->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasConnection::setStatus(CanvasConnection::Status newStatus)
|
||||||
|
{
|
||||||
|
if (newStatus == m_status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_status = newStatus;
|
||||||
|
emit statusChanged(m_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalProp *CanvasConnection::propertyFromPath(QByteArray path) const
|
||||||
|
{
|
||||||
|
return m_localPropertyRoot->getOrCreateWithPath(path);
|
||||||
|
}
|
||||||
|
|
129
utils/fgqcanvas/canvasconnection.h
Normal file
129
utils/fgqcanvas/canvasconnection.h
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifndef CANVASCONNECTION_H
|
||||||
|
#define CANVASCONNECTION_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtWebSockets/QWebSocket>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QRectF>
|
||||||
|
|
||||||
|
class LocalProp;
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
|
||||||
|
class CanvasConnection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_ENUMS(Status)
|
||||||
|
|
||||||
|
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||||
|
|
||||||
|
// QML exposed versions of the destination rect
|
||||||
|
Q_PROPERTY(QPointF center READ center WRITE setCenter NOTIFY centerChanged)
|
||||||
|
Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY sizeChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QUrl webSocketUrl READ webSocketUrl NOTIFY webSocketUrlChanged)
|
||||||
|
Q_PROPERTY(QString rootPath READ rootPath NOTIFY rootPathChanged)
|
||||||
|
public:
|
||||||
|
explicit CanvasConnection(QObject *parent = nullptr);
|
||||||
|
~CanvasConnection();
|
||||||
|
|
||||||
|
void setNetworkAccess(QNetworkAccessManager *dl);
|
||||||
|
void setRootPropertyPath(QByteArray path);
|
||||||
|
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
NotConnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Closed,
|
||||||
|
Reconnecting,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
Status status() const
|
||||||
|
{
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject saveState() const;
|
||||||
|
bool restoreState(QJsonObject state);
|
||||||
|
|
||||||
|
void connectWebSocket(QByteArray hostName, int port);
|
||||||
|
QPointF center() const;
|
||||||
|
|
||||||
|
QSizeF size() const;
|
||||||
|
|
||||||
|
LocalProp* propertyRoot() const;
|
||||||
|
|
||||||
|
QUrl webSocketUrl() const
|
||||||
|
{
|
||||||
|
return m_webSocketUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString rootPath() const
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(m_rootPropertyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
// not on iOS / Android - requires widgets
|
||||||
|
void showDebugTree();
|
||||||
|
|
||||||
|
void setCenter(QPointF center);
|
||||||
|
|
||||||
|
void setSize(QSizeF size);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusChanged(Status status);
|
||||||
|
|
||||||
|
void centerChanged(QPointF center);
|
||||||
|
|
||||||
|
void sizeChanged(QSizeF size);
|
||||||
|
|
||||||
|
void rootPathChanged();
|
||||||
|
|
||||||
|
void webSocketUrlChanged();
|
||||||
|
|
||||||
|
void updated();
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onWebSocketConnected();
|
||||||
|
void onTextMessageReceived(QString message);
|
||||||
|
void onWebSocketClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setStatus(Status newStatus);
|
||||||
|
LocalProp *propertyFromPath(QByteArray path) const;
|
||||||
|
|
||||||
|
QUrl m_webSocketUrl;
|
||||||
|
QByteArray m_rootPropertyPath;
|
||||||
|
QRectF m_destRect;
|
||||||
|
|
||||||
|
QWebSocket m_webSocket;
|
||||||
|
QNetworkAccessManager* m_netAccess = nullptr;
|
||||||
|
std::unique_ptr<LocalProp> m_localPropertyRoot;
|
||||||
|
QHash<int, LocalProp*> idPropertyDict;
|
||||||
|
Status m_status = NotConnected;
|
||||||
|
QString m_rootPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CANVASCONNECTION_H
|
80
utils/fgqcanvas/canvasdisplay.cpp
Normal file
80
utils/fgqcanvas/canvasdisplay.cpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#include "canvasdisplay.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "canvasconnection.h"
|
||||||
|
#include "fgcanvasgroup.h"
|
||||||
|
#include "fgcanvaspaintcontext.h"
|
||||||
|
#include "canvasitem.h"
|
||||||
|
|
||||||
|
CanvasDisplay::CanvasDisplay(QQuickItem* parent) :
|
||||||
|
QQuickItem(parent)
|
||||||
|
{
|
||||||
|
setSize(QSizeF(400, 400));
|
||||||
|
qDebug() << "created a canvas display";
|
||||||
|
|
||||||
|
setFlag(ItemHasContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasDisplay::~CanvasDisplay()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasDisplay::updatePolish()
|
||||||
|
{
|
||||||
|
m_rootElement->polish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasDisplay::setCanvas(CanvasConnection *canvas)
|
||||||
|
{
|
||||||
|
if (m_connection == canvas)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_connection) {
|
||||||
|
disconnect(m_connection, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connection = canvas;
|
||||||
|
emit canvasChanged(m_connection);
|
||||||
|
|
||||||
|
// delete existing children
|
||||||
|
|
||||||
|
connect(m_connection, &CanvasConnection::statusChanged,
|
||||||
|
this, &CanvasDisplay::onConnectionStatusChanged);
|
||||||
|
connect(m_connection, &CanvasConnection::updated,
|
||||||
|
this, &CanvasDisplay::onConnectionUpdated);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasDisplay::onConnectionStatusChanged()
|
||||||
|
{
|
||||||
|
if (m_connection->status() == CanvasConnection::Connected) {
|
||||||
|
m_rootElement.reset(new FGCanvasGroup(nullptr, m_connection->propertyRoot()));
|
||||||
|
auto qq = m_rootElement->createQuickItem(this);
|
||||||
|
qq->setSize(QSizeF(400, 400));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasDisplay::onConnectionUpdated()
|
||||||
|
{
|
||||||
|
m_rootElement->polish();
|
||||||
|
update();
|
||||||
|
}
|
62
utils/fgqcanvas/canvasdisplay.h
Normal file
62
utils/fgqcanvas/canvasdisplay.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifndef CANVASDISPLAY_H
|
||||||
|
#define CANVASDISPLAY_H
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
class CanvasConnection;
|
||||||
|
class FGCanvasElement;
|
||||||
|
|
||||||
|
class CanvasDisplay : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(CanvasConnection* canvas READ canvas WRITE setCanvas NOTIFY canvasChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
CanvasDisplay(QQuickItem* parent = nullptr);
|
||||||
|
~CanvasDisplay();
|
||||||
|
|
||||||
|
CanvasConnection* canvas() const
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void canvasChanged(CanvasConnection* canvas);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void setCanvas(CanvasConnection* canvas);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updatePolish() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStatusChanged();
|
||||||
|
|
||||||
|
void onConnectionUpdated();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CanvasConnection* m_connection = nullptr;
|
||||||
|
std::unique_ptr<FGCanvasElement> m_rootElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CANVASDISPLAY_H
|
|
@ -1,51 +1,67 @@
|
||||||
#-------------------------------------------------
|
|
||||||
#
|
|
||||||
# Project created by QtCreator 2016-11-30T22:34:11
|
|
||||||
#
|
|
||||||
#-------------------------------------------------
|
|
||||||
|
|
||||||
QT += core gui websockets
|
QT += core gui gui-private quick websockets
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
!ios:!android
|
||||||
|
{
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
SOURCES += canvastreemodel.cpp \
|
||||||
|
elementdatamodel.cpp
|
||||||
|
|
||||||
|
HEADERS += canvastreemodel.h \
|
||||||
|
elementdatamodel.h
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TARGET = fgqcanvas
|
TARGET = fgqcanvas
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
|
|
||||||
SOURCES += main.cpp\
|
SOURCES += main.cpp\
|
||||||
temporarywidget.cpp \
|
|
||||||
fgcanvasgroup.cpp \
|
fgcanvasgroup.cpp \
|
||||||
fgcanvaselement.cpp \
|
fgcanvaselement.cpp \
|
||||||
fgcanvaspaintcontext.cpp \
|
fgcanvaspaintcontext.cpp \
|
||||||
localprop.cpp \
|
localprop.cpp \
|
||||||
fgcanvaswidget.cpp \
|
|
||||||
fgcanvaspath.cpp \
|
fgcanvaspath.cpp \
|
||||||
fgcanvastext.cpp \
|
fgcanvastext.cpp \
|
||||||
fgqcanvasmap.cpp \
|
fgqcanvasmap.cpp \
|
||||||
canvastreemodel.cpp \
|
fgqcanvasimage.cpp \
|
||||||
fgqcanvasimage.cpp
|
fgqcanvasfontcache.cpp \
|
||||||
|
fgqcanvasimageloader.cpp \
|
||||||
|
canvasitem.cpp \
|
||||||
|
canvasconnection.cpp \
|
||||||
|
applicationcontroller.cpp \
|
||||||
|
canvasdisplay.cpp
|
||||||
|
|
||||||
HEADERS += temporarywidget.h \
|
|
||||||
|
HEADERS += \
|
||||||
fgcanvasgroup.h \
|
fgcanvasgroup.h \
|
||||||
fgcanvaselement.h \
|
fgcanvaselement.h \
|
||||||
fgcanvaspaintcontext.h \
|
fgcanvaspaintcontext.h \
|
||||||
localprop.h \
|
localprop.h \
|
||||||
fgcanvaswidget.h \
|
|
||||||
fgcanvaspath.h \
|
fgcanvaspath.h \
|
||||||
fgcanvastext.h \
|
fgcanvastext.h \
|
||||||
fgqcanvasmap.h \
|
fgqcanvasmap.h \
|
||||||
canvastreemodel.h \
|
fgqcanvasimage.h \
|
||||||
fgqcanvasimage.h
|
canvasconnection.h \
|
||||||
|
applicationcontroller.h \
|
||||||
|
canvasdisplay.h \
|
||||||
|
canvasitem.h \
|
||||||
|
fgqcanvasfontcache.h \
|
||||||
|
fgqcanvasimageloader.h
|
||||||
|
|
||||||
FORMS += temporarywidget.ui
|
RESOURCES += \
|
||||||
|
fgqcanvas_resources.qrc
|
||||||
|
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
qml/*
|
||||||
|
|
||||||
|
|
||||||
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"
|
||||||
QMAKE_MAC_XCODE_SETTINGS += Q_XCODE_DEVELOPMENT_TEAM
|
QMAKE_MAC_XCODE_SETTINGS += Q_XCODE_DEVELOPMENT_TEAM
|
||||||
|
|
||||||
|
|
||||||
ios {
|
ios {
|
||||||
QMAKE_INFO_PLIST = ios/Info.plist
|
QMAKE_INFO_PLIST = ios/Info.plist
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,38 @@ FGCanvasElement::FGCanvasElement(FGCanvasGroup* pr, LocalProp* prop) :
|
||||||
if (pr) {
|
if (pr) {
|
||||||
pr->markChildZIndicesDirty();
|
pr->markChildZIndicesDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestPolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FGCanvasElement::requestPolish()
|
||||||
|
{
|
||||||
|
_polishRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGCanvasElement::polish()
|
||||||
|
{
|
||||||
|
if (!isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clipDirty) {
|
||||||
|
parseCSSClip(_propertyRoot->value("clip", QVariant()).toByteArray());
|
||||||
|
_clipDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quickItem()) {
|
||||||
|
quickItem()->setTransform(combinedTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_styleDirty) {
|
||||||
|
_fillColor = parseColorValue(getCascadedStyle("fill"));
|
||||||
|
_styleDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
doPolish();
|
||||||
|
_polishRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasElement::paint(FGCanvasPaintContext *context) const
|
void FGCanvasElement::paint(FGCanvasPaintContext *context) const
|
||||||
|
@ -101,12 +133,6 @@ void FGCanvasElement::paint(FGCanvasPaintContext *context) const
|
||||||
}
|
}
|
||||||
|
|
||||||
QPainter* p = context->painter();
|
QPainter* p = context->painter();
|
||||||
|
|
||||||
if (_clipDirty) {
|
|
||||||
parseCSSClip(_propertyRoot->value("clip", QVariant()).toByteArray());
|
|
||||||
_clipDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->save();
|
p->save();
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,14 +157,6 @@ void FGCanvasElement::paint(FGCanvasPaintContext *context) const
|
||||||
|
|
||||||
QTransform combined = combinedTransform();
|
QTransform combined = combinedTransform();
|
||||||
p->setTransform(combined, true /* combine */);
|
p->setTransform(combined, true /* combine */);
|
||||||
if (quickItem()) {
|
|
||||||
quickItem()->setTransform(combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_styleDirty) {
|
|
||||||
_fillColor = parseColorValue(getCascadedStyle("fill"));
|
|
||||||
_styleDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_fillColor.isValid()) {
|
if (!_fillColor.isValid()) {
|
||||||
p->setBrush(Qt::NoBrush);
|
p->setBrush(Qt::NoBrush);
|
||||||
|
@ -160,6 +178,10 @@ void FGCanvasElement::doPaint(FGCanvasPaintContext* context) const
|
||||||
Q_UNUSED(context);
|
Q_UNUSED(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGCanvasElement::doPolish()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
QTransform FGCanvasElement::combinedTransform() const
|
QTransform FGCanvasElement::combinedTransform() const
|
||||||
{
|
{
|
||||||
if (_transformsDirty) {
|
if (_transformsDirty) {
|
||||||
|
@ -277,16 +299,20 @@ void FGCanvasElement::onCenterChanged(QVariant value)
|
||||||
} else {
|
} else {
|
||||||
_center.setY(value.toFloat());
|
_center.setY(value.toFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasElement::markTransformsDirty()
|
void FGCanvasElement::markTransformsDirty()
|
||||||
{
|
{
|
||||||
_transformsDirty = true;
|
_transformsDirty = true;
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasElement::markClipDirty()
|
void FGCanvasElement::markClipDirty()
|
||||||
{
|
{
|
||||||
_clipDirty = true;
|
_clipDirty = true;
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
float FGCanvasElement::parseCSSValue(QByteArray value) const
|
float FGCanvasElement::parseCSSValue(QByteArray value) const
|
||||||
|
@ -376,6 +402,7 @@ QColor FGCanvasElement::parseColorValue(QVariant value) const
|
||||||
void FGCanvasElement::markStyleDirty()
|
void FGCanvasElement::markStyleDirty()
|
||||||
{
|
{
|
||||||
_styleDirty = true;
|
_styleDirty = true;
|
||||||
|
requestPolish();
|
||||||
// group will cascade
|
// group will cascade
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,9 +429,10 @@ void FGCanvasElement::markZIndexDirty(QVariant value)
|
||||||
void FGCanvasElement::onVisibleChanged(QVariant value)
|
void FGCanvasElement::onVisibleChanged(QVariant value)
|
||||||
{
|
{
|
||||||
_visible = value.toBool();
|
_visible = value.toBool();
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasElement::parseCSSClip(QByteArray value) const
|
void FGCanvasElement::parseCSSClip(QByteArray value)
|
||||||
{
|
{
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
_hasClip = false;
|
_hasClip = false;
|
||||||
|
@ -432,4 +460,6 @@ void FGCanvasElement::parseCSSClip(QByteArray value) const
|
||||||
_clipRect = QRectF(left, top, right - left, bottom - top);
|
_clipRect = QRectF(left, top, right - left, bottom - top);
|
||||||
qDebug() << "final clip rect:" << _clipRect << "from" << value;
|
qDebug() << "final clip rect:" << _clipRect << "from" << value;
|
||||||
_hasClip = true;
|
_hasClip = true;
|
||||||
|
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,12 @@ public:
|
||||||
virtual CanvasItem* createQuickItem(QQuickItem* parent);
|
virtual CanvasItem* createQuickItem(QQuickItem* parent);
|
||||||
virtual CanvasItem* quickItem() const;
|
virtual CanvasItem* quickItem() const;
|
||||||
|
|
||||||
|
void requestPolish();
|
||||||
|
|
||||||
|
void polish();
|
||||||
protected:
|
protected:
|
||||||
virtual void doPaint(FGCanvasPaintContext* context) const;
|
virtual void doPaint(FGCanvasPaintContext* context) const;
|
||||||
|
virtual void doPolish();
|
||||||
|
|
||||||
virtual bool onChildAdded(LocalProp* prop);
|
virtual bool onChildAdded(LocalProp* prop);
|
||||||
virtual bool onChildRemoved(LocalProp* prop);
|
virtual bool onChildRemoved(LocalProp* prop);
|
||||||
|
@ -88,6 +92,7 @@ private:
|
||||||
private:
|
private:
|
||||||
friend class FGCanvasGroup;
|
friend class FGCanvasGroup;
|
||||||
|
|
||||||
|
bool _polishRequired = false;
|
||||||
bool _visible = true;
|
bool _visible = true;
|
||||||
bool _highlighted = false;
|
bool _highlighted = false;
|
||||||
|
|
||||||
|
@ -106,7 +111,7 @@ private:
|
||||||
mutable bool _hasClip = false;
|
mutable bool _hasClip = false;
|
||||||
mutable QRectF _clipRect;
|
mutable QRectF _clipRect;
|
||||||
|
|
||||||
void parseCSSClip(QByteArray value) const;
|
void parseCSSClip(QByteArray value);
|
||||||
float parseCSSValue(QByteArray value) const;
|
float parseCSSValue(QByteArray value) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -79,18 +79,27 @@ CanvasItem *FGCanvasGroup::createQuickItem(QQuickItem *parent)
|
||||||
|
|
||||||
void FGCanvasGroup::doPaint(FGCanvasPaintContext *context) const
|
void FGCanvasGroup::doPaint(FGCanvasPaintContext *context) const
|
||||||
{
|
{
|
||||||
if (_zIndicesDirty) {
|
for (FGCanvasElement* element : _children) {
|
||||||
std::sort(_children.begin(), _children.end(), ChildOrderingFunction());
|
element->paint(context);
|
||||||
_zIndicesDirty = false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGCanvasGroup::doPolish()
|
||||||
|
{
|
||||||
if (_cachedSymbolDirty) {
|
if (_cachedSymbolDirty) {
|
||||||
qDebug() << _propertyRoot->path() << "should use symbol cache:" << _propertyRoot->value("symbol-type", QVariant()).toByteArray();
|
qDebug() << _propertyRoot->path() << "should use symbol cache:" << _propertyRoot->value("symbol-type", QVariant()).toByteArray();
|
||||||
_cachedSymbolDirty = false;
|
_cachedSymbolDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_zIndicesDirty) {
|
||||||
|
std::sort(_children.begin(), _children.end(), ChildOrderingFunction());
|
||||||
|
_zIndicesDirty = false;
|
||||||
|
|
||||||
|
qWarning() << Q_FUNC_INFO << "adjust z order of quick items";
|
||||||
|
}
|
||||||
|
|
||||||
for (FGCanvasElement* element : _children) {
|
for (FGCanvasElement* element : _children) {
|
||||||
element->paint(context);
|
element->polish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +144,6 @@ bool FGCanvasGroup::onChildAdded(LocalProp *prop)
|
||||||
markChildZIndicesDirty();
|
markChildZIndicesDirty();
|
||||||
|
|
||||||
if (_quick) {
|
if (_quick) {
|
||||||
qDebug() << "creating quick item for child";
|
|
||||||
_children.back()->createQuickItem(_quick);
|
_children.back()->createQuickItem(_quick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ signals:
|
||||||
protected:
|
protected:
|
||||||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||||
|
|
||||||
|
void doPolish() override;
|
||||||
|
|
||||||
bool onChildAdded(LocalProp *prop) override;
|
bool onChildAdded(LocalProp *prop) override;
|
||||||
bool onChildRemoved(LocalProp *prop) override;
|
bool onChildRemoved(LocalProp *prop) override;
|
||||||
|
|
||||||
|
@ -45,6 +47,7 @@ private:
|
||||||
mutable bool _zIndicesDirty = false;
|
mutable bool _zIndicesDirty = false;
|
||||||
mutable bool _cachedSymbolDirty = false;
|
mutable bool _cachedSymbolDirty = false;
|
||||||
|
|
||||||
|
|
||||||
CanvasItem* _quick = nullptr;
|
CanvasItem* _quick = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
: CanvasItem(parent)
|
: CanvasItem(parent)
|
||||||
{
|
{
|
||||||
setFlag(ItemHasContents);
|
setFlag(ItemHasContents);
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPath(QPainterPath pp)
|
void setPath(QPainterPath pp)
|
||||||
|
@ -374,22 +375,6 @@ FGCanvasPath::FGCanvasPath(FGCanvasGroup* pr, LocalProp* prop) :
|
||||||
|
|
||||||
void FGCanvasPath::doPaint(FGCanvasPaintContext *context) const
|
void FGCanvasPath::doPaint(FGCanvasPaintContext *context) const
|
||||||
{
|
{
|
||||||
if (_pathDirty) {
|
|
||||||
rebuildPath();
|
|
||||||
if (_quickPath) {
|
|
||||||
_quickPath->setPath(_painterPath);
|
|
||||||
}
|
|
||||||
_pathDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_penDirty) {
|
|
||||||
rebuildPen();
|
|
||||||
if (_quickPath) {
|
|
||||||
_quickPath->setStroke(_stroke);
|
|
||||||
}
|
|
||||||
_penDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->painter()->setPen(_stroke);
|
context->painter()->setPen(_stroke);
|
||||||
|
|
||||||
switch (_paintType) {
|
switch (_paintType) {
|
||||||
|
@ -422,6 +407,26 @@ void FGCanvasPath::doPaint(FGCanvasPaintContext *context) const
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGCanvasPath::doPolish()
|
||||||
|
{
|
||||||
|
if (_pathDirty) {
|
||||||
|
rebuildPath();
|
||||||
|
if (_quickPath) {
|
||||||
|
_quickPath->setPath(_painterPath);
|
||||||
|
}
|
||||||
|
_pathDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_penDirty) {
|
||||||
|
rebuildPen();
|
||||||
|
if (_quickPath) {
|
||||||
|
_quickPath->setStroke(_stroke);
|
||||||
|
}
|
||||||
|
_penDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void FGCanvasPath::markStyleDirty()
|
void FGCanvasPath::markStyleDirty()
|
||||||
{
|
{
|
||||||
_penDirty = true;
|
_penDirty = true;
|
||||||
|
@ -429,7 +434,6 @@ void FGCanvasPath::markStyleDirty()
|
||||||
|
|
||||||
CanvasItem *FGCanvasPath::createQuickItem(QQuickItem *parent)
|
CanvasItem *FGCanvasPath::createQuickItem(QQuickItem *parent)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
_quickPath = new PathQuickItem(parent);
|
_quickPath = new PathQuickItem(parent);
|
||||||
_quickPath->setPath(_painterPath);
|
_quickPath->setPath(_painterPath);
|
||||||
_quickPath->setStroke(_stroke);
|
_quickPath->setStroke(_stroke);
|
||||||
|
@ -444,11 +448,13 @@ CanvasItem *FGCanvasPath::quickItem() const
|
||||||
void FGCanvasPath::markPathDirty()
|
void FGCanvasPath::markPathDirty()
|
||||||
{
|
{
|
||||||
_pathDirty = true;
|
_pathDirty = true;
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasPath::markStrokeDirty()
|
void FGCanvasPath::markStrokeDirty()
|
||||||
{
|
{
|
||||||
_penDirty = true;
|
_penDirty = true;
|
||||||
|
requestPolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGCanvasPath::onChildAdded(LocalProp *prop)
|
bool FGCanvasPath::onChildAdded(LocalProp *prop)
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||||
|
|
||||||
|
void doPolish() override;
|
||||||
|
|
||||||
virtual void markStyleDirty() override;
|
virtual void markStyleDirty() override;
|
||||||
|
|
||||||
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
||||||
|
|
|
@ -55,17 +55,12 @@ CanvasItem *FGCanvasText::quickItem() const
|
||||||
|
|
||||||
void FGCanvasText::setEngine(QQmlEngine *engine)
|
void FGCanvasText::setEngine(QQmlEngine *engine)
|
||||||
{
|
{
|
||||||
static_textComponent = new QQmlComponent(engine, QUrl("text.qml"));
|
static_textComponent = new QQmlComponent(engine, QUrl("qrc:///qml/text.qml"));
|
||||||
qDebug() << static_textComponent->errorString();
|
qDebug() << static_textComponent->errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCanvasText::doPaint(FGCanvasPaintContext *context) const
|
void FGCanvasText::doPaint(FGCanvasPaintContext *context) const
|
||||||
{
|
{
|
||||||
if (_fontDirty) {
|
|
||||||
rebuildFont();
|
|
||||||
_fontDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->painter()->setFont(_font);
|
context->painter()->setFont(_font);
|
||||||
context->painter()->setPen(fillColor());
|
context->painter()->setPen(fillColor());
|
||||||
context->painter()->setBrush(Qt::NoBrush);
|
context->painter()->setBrush(Qt::NoBrush);
|
||||||
|
@ -92,6 +87,14 @@ void FGCanvasText::doPaint(FGCanvasPaintContext *context) const
|
||||||
context->painter()->drawText(rect, _alignment, _text);
|
context->painter()->drawText(rect, _alignment, _text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FGCanvasText::doPolish()
|
||||||
|
{
|
||||||
|
if (_fontDirty) {
|
||||||
|
rebuildFont();
|
||||||
|
_fontDirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FGCanvasText::markStyleDirty()
|
void FGCanvasText::markStyleDirty()
|
||||||
{
|
{
|
||||||
markFontDirty();
|
markFontDirty();
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
static void setEngine(QQmlEngine* engine);
|
static void setEngine(QQmlEngine* engine);
|
||||||
protected:
|
protected:
|
||||||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||||
|
void doPolish() override;
|
||||||
|
|
||||||
virtual void markStyleDirty() override;
|
virtual void markStyleDirty() override;
|
||||||
private:
|
private:
|
||||||
|
|
13
utils/fgqcanvas/fgqcanvas_resources.qrc
Normal file
13
utils/fgqcanvas/fgqcanvas_resources.qrc
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>qml/mainMenu.qml</file>
|
||||||
|
<file>qml/Button.qml</file>
|
||||||
|
<file>qml/InputLine.qml</file>
|
||||||
|
<file alias="images/checkerboard">qml/checkerboard.png</file>
|
||||||
|
<file>qml/CanvasFrame.qml</file>
|
||||||
|
<file>qml/BrowsePanel.qml</file>
|
||||||
|
<file>qml/LoadSavePanel.qml</file>
|
||||||
|
<file>qml/image.qml</file>
|
||||||
|
<file>qml/text.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -112,38 +112,48 @@ void FGQCanvasFontCache::onFontDownloadError(QNetworkReply::NetworkError)
|
||||||
void FGQCanvasFontCache::lookupFile(QByteArray name)
|
void FGQCanvasFontCache::lookupFile(QByteArray name)
|
||||||
{
|
{
|
||||||
QString path = QStandardPaths::locate(QStandardPaths::CacheLocation, name);
|
QString path = QStandardPaths::locate(QStandardPaths::CacheLocation, name);
|
||||||
if (path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
QUrl url;
|
|
||||||
url.setScheme("http");
|
|
||||||
url.setHost(m_hostName);
|
|
||||||
url.setPort(m_port);
|
|
||||||
url.setPath("/Fonts/" + name);
|
|
||||||
|
|
||||||
Q_FOREACH (QNetworkReply* transfer, m_transfers) {
|
|
||||||
if (transfer->url() == url) {
|
|
||||||
return; // transfer already active
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "reqeusting font" << url;
|
|
||||||
QNetworkReply* reply = m_downloader->get(QNetworkRequest(url));
|
|
||||||
reply->setProperty("font", name);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, &FGQCanvasFontCache::onFontDownloadFinished);
|
|
||||||
// connect(reply, &QNetworkReply::error, this, &FGQCanvasFontCache::onFontDownloadError);
|
|
||||||
|
|
||||||
m_transfers.append(reply);
|
|
||||||
} else {
|
|
||||||
qDebug() << "found font" << name << "at path" << path;
|
qDebug() << "found font" << name << "at path" << path;
|
||||||
|
|
||||||
int fontFamilyId = QFontDatabase::addApplicationFont(path);
|
int fontFamilyId = QFontDatabase::addApplicationFont(path);
|
||||||
QStringList families = QFontDatabase::applicationFontFamilies(fontFamilyId);
|
if (fontFamilyId >= 0) {
|
||||||
qDebug() << "families are:" << families;
|
QStringList families = QFontDatabase::applicationFontFamilies(fontFamilyId);
|
||||||
|
qDebug() << "families are:" << families;
|
||||||
|
|
||||||
// compute a QFont and cache
|
// compute a QFont and cache
|
||||||
QFont font(families.front());
|
QFont font(families.front());
|
||||||
m_cache.insert(name, font);
|
m_cache.insert(name, font);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to load font into QFontDatabase:" << path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_hostName.isEmpty()) {
|
||||||
|
qWarning() << "host name not specified";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl url;
|
||||||
|
url.setScheme("http");
|
||||||
|
url.setHost(m_hostName);
|
||||||
|
url.setPort(m_port);
|
||||||
|
url.setPath("/Fonts/" + name);
|
||||||
|
|
||||||
|
Q_FOREACH (QNetworkReply* transfer, m_transfers) {
|
||||||
|
if (transfer->url() == url) {
|
||||||
|
return; // transfer already active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "reqeusting font" << url;
|
||||||
|
QNetworkReply* reply = m_downloader->get(QNetworkRequest(url));
|
||||||
|
reply->setProperty("font", name);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &FGQCanvasFontCache::onFontDownloadFinished);
|
||||||
|
// connect(reply, &QNetworkReply::error, this, &FGQCanvasFontCache::onFontDownloadError);
|
||||||
|
|
||||||
|
m_transfers.append(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,45 +15,52 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#include "temporarywidget.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkDiskCache>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickView>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include "fgcanvastext.h"
|
||||||
|
#include "fgqcanvasimage.h"
|
||||||
#include "fgqcanvasfontcache.h"
|
#include "fgqcanvasfontcache.h"
|
||||||
#include "fgqcanvasimageloader.h"
|
#include "fgqcanvasimageloader.h"
|
||||||
#include "canvasitem.h"
|
#include "canvasitem.h"
|
||||||
|
#include "applicationcontroller.h"
|
||||||
|
#include "canvasdisplay.h"
|
||||||
|
#include "canvasconnection.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
a.setApplicationName("FGCanvas");
|
a.setApplicationName("FGCanvas");
|
||||||
a.setOrganizationDomain("flightgear.org");
|
a.setOrganizationDomain("flightgear.org");
|
||||||
a.setOrganizationName("FlightGear");
|
a.setOrganizationName("FlightGear");
|
||||||
|
|
||||||
QNetworkAccessManager* downloader = new QNetworkAccessManager;
|
ApplicationController appController;
|
||||||
|
// load saved history on the app controller?
|
||||||
|
|
||||||
QNetworkDiskCache* cache = new QNetworkDiskCache;
|
FGQCanvasFontCache::initialise(appController.netAccess());
|
||||||
cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
FGQCanvasImageLoader::initialise(appController.netAccess());
|
||||||
downloader->setCache(cache); // takes ownership
|
|
||||||
|
|
||||||
FGQCanvasFontCache::initialise(downloader);
|
|
||||||
FGQCanvasImageLoader::initialise(downloader);
|
|
||||||
|
|
||||||
qmlRegisterType<CanvasItem>("FlightGear", 1, 0, "CanvasItem");
|
qmlRegisterType<CanvasItem>("FlightGear", 1, 0, "CanvasItem");
|
||||||
|
qmlRegisterType<CanvasDisplay>("FlightGear", 1, 0, "CanvasDisplay");
|
||||||
|
qmlRegisterUncreatableType<CanvasConnection>("FlightGear", 1, 0, "CanvasConnection", "Don't create me");
|
||||||
|
qmlRegisterUncreatableType<ApplicationController>("FlightGear", 1, 0, "Application", "Can't create");
|
||||||
|
|
||||||
|
QQuickView quickView;
|
||||||
|
quickView.resize(1024, 768);
|
||||||
|
|
||||||
TemporaryWidget w;
|
quickView.rootContext()->setContextProperty("_application", &appController);
|
||||||
w.setNetworkAccess(downloader);
|
|
||||||
w.show();
|
FGCanvasText::setEngine(quickView.engine());
|
||||||
|
FGQCanvasImage::setEngine(quickView.engine());
|
||||||
|
|
||||||
|
quickView.setSource(QUrl("qrc:///qml/mainMenu.qml"));
|
||||||
|
quickView.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
|
quickView.show();
|
||||||
|
|
||||||
int result = a.exec();
|
int result = a.exec();
|
||||||
delete downloader;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
114
utils/fgqcanvas/qml/BrowsePanel.qml
Normal file
114
utils/fgqcanvas/qml/BrowsePanel.qml
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import FlightGear 1.0 as FG
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Rectangle {
|
||||||
|
id: hostPanel
|
||||||
|
height: hostPanelContent.childrenRect.height + 16
|
||||||
|
width: 300
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
border.color: "#9f9f9f"
|
||||||
|
border.width: 1
|
||||||
|
color: "#5f5f5f"
|
||||||
|
opacity: 0.8
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
id: hostPanelContent
|
||||||
|
width: parent.width - 30
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
InputLine {
|
||||||
|
id: hostInput
|
||||||
|
width: parent.width
|
||||||
|
label: "Hostname"
|
||||||
|
text: _application.host
|
||||||
|
onEditingFinished: {
|
||||||
|
_application.host = text
|
||||||
|
portInput.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyNavigation.tab: portInput
|
||||||
|
}
|
||||||
|
|
||||||
|
InputLine {
|
||||||
|
id: portInput
|
||||||
|
width: parent.width
|
||||||
|
label: "Port"
|
||||||
|
text: _application.port
|
||||||
|
KeyNavigation.tab: queryButton
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
_application.port = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: queryButton
|
||||||
|
label: "Query"
|
||||||
|
enabled: _application.host != ""
|
||||||
|
visible: (_application.status == FG.Application.Idle)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
_application.query();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: canvasListPanel
|
||||||
|
border.color: "#9f9f9f"
|
||||||
|
border.width: 1
|
||||||
|
color: "#5f5f5f"
|
||||||
|
opacity: 0.8
|
||||||
|
anchors.top: hostPanel.bottom
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: 300
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: canvasList
|
||||||
|
model: _application.canvases
|
||||||
|
visible: (_application.status == FG.Application.SuccessfulQuery)
|
||||||
|
width: parent.width - 30
|
||||||
|
height: parent.height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: canvasLabel.implicitWidth
|
||||||
|
height: canvasLabel.implicitHeight + 20
|
||||||
|
|
||||||
|
color: "#3f3f3f"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: canvasLabel
|
||||||
|
text: modelData['name']
|
||||||
|
|
||||||
|
// todo - different color if we already have a connection?
|
||||||
|
color: "white"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
_application.openCanvas(modelData['path']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
utils/fgqcanvas/qml/Button.qml
Normal file
32
utils/fgqcanvas/qml/Button.qml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias label: labelText.text
|
||||||
|
property bool enabled: true
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
border.width: 2
|
||||||
|
border.color: enabled ? "orange" : "9f9f9f"
|
||||||
|
|
||||||
|
color: "#3f3f3f"
|
||||||
|
implicitWidth: 100
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: enabled ? "white" : "9f9f9f"
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.enabled
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
utils/fgqcanvas/qml/CanvasFrame.qml
Normal file
116
utils/fgqcanvas/qml/CanvasFrame.qml
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import FlightGear 1.0 as FG
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property bool showDecorations: true
|
||||||
|
property alias canvas: canvasDisplay.canvas
|
||||||
|
|
||||||
|
readonly property var centerPoint: Qt.point(width / 2, height / 2)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
width = canvas.size.width
|
||||||
|
height = canvas.size.height
|
||||||
|
x = canvas.center.x - (width / 2)
|
||||||
|
y = canvas.center.y - (height / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveGeometry()
|
||||||
|
{
|
||||||
|
canvas.center = Qt.point(x + (width / 2), y + (height / 2))
|
||||||
|
canvas.size = Qt.size(root.width / 2, root.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
FG.CanvasDisplay {
|
||||||
|
id: canvasDisplay
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onCanvasChanged: {
|
||||||
|
root.width = canvas.size.width
|
||||||
|
root.height = canvas.size.height
|
||||||
|
|
||||||
|
root.x = canvas.center.x - (root.width / 2)
|
||||||
|
root.y = canvas.center.y - (root.height / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
border.width: 1
|
||||||
|
border.color: "orange"
|
||||||
|
color: "transparent"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width + 2
|
||||||
|
height: parent.height + 2
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
drag.target: root
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
root.saveGeometry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
color: "orange"
|
||||||
|
opacity: 0.5
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// resizing
|
||||||
|
|
||||||
|
onPositionChanged: {
|
||||||
|
var rootPos = mapToItem(root, mouse.x, mouse.y);
|
||||||
|
var rootDiff = Qt.point(rootPos.x - root.centerPoint.x,
|
||||||
|
rootPos.y - root.centerPoint.y);
|
||||||
|
|
||||||
|
root.width = rootDiff.x * 2;
|
||||||
|
root.height = rootDiff.y * 2;
|
||||||
|
root.x = canvas.center.x - (root.width / 2)
|
||||||
|
root.y = canvas.center.y - (root.height / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
saveGeometry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: "orange"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Canvas"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
|
||||||
|
function statusAsString(status)
|
||||||
|
{
|
||||||
|
switch (status) {
|
||||||
|
case FG.CanvasConnection.NotConnected: return "Not connected";
|
||||||
|
case FG.CanvasConnection.Connecting: return "Connecting";
|
||||||
|
case FG.CanvasConnection.Connected: return "Connected";
|
||||||
|
case FG.CanvasConnection.Reconnecting: return "Re-connecting";
|
||||||
|
case FG.CanvasConnection.Error: return "Error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text: "WS: " + canvas.webSocketUrl + "\n"
|
||||||
|
+ "Root:" + canvas.rootPath + "\n"
|
||||||
|
+ "Status:" + statusAsString(canvas.status);
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
52
utils/fgqcanvas/qml/InputLine.qml
Normal file
52
utils/fgqcanvas/qml/InputLine.qml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias text: input.text
|
||||||
|
property alias label: labelText.text
|
||||||
|
|
||||||
|
signal editingFinished
|
||||||
|
|
||||||
|
implicitHeight: 30
|
||||||
|
implicitWidth: 200
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelText
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: inputFrame.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: inputFrame
|
||||||
|
border.width: 2
|
||||||
|
border.color: input.focus ? "orange" : "#9f9f9f"
|
||||||
|
color: "#3f3f3f"
|
||||||
|
width: parent.width * 0.5
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: root.height
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: input
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 8
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 8
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
onEditingFinished: {
|
||||||
|
root.editingFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
color: "#9f9f9f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
utils/fgqcanvas/qml/LoadSavePanel.qml
Normal file
104
utils/fgqcanvas/qml/LoadSavePanel.qml
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import FlightGear 1.0 as FG
|
||||||
|
|
||||||
|
Item {
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: savePanel
|
||||||
|
width: parent.width - 8
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
border.color: "#9f9f9f"
|
||||||
|
border.width: 1
|
||||||
|
color: "#5f5f5f"
|
||||||
|
opacity: 0.8
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
id: savePanelContent
|
||||||
|
width: parent.width - 30
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
InputLine {
|
||||||
|
id: saveTitleInput
|
||||||
|
width: parent.width
|
||||||
|
label: "Title"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: saveButton
|
||||||
|
label: "Save"
|
||||||
|
enabled: (saveTitleInput.text != "")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
_application.save(saveTitleInput.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: savedList
|
||||||
|
model: _application.configs
|
||||||
|
width: parent.width - 30
|
||||||
|
anchors.top: savePanelContent.bottom
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: parent.width
|
||||||
|
height:delegateFrame.height + 8
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: delegateFrame
|
||||||
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
height: configLabel.implicitHeight + 20
|
||||||
|
|
||||||
|
color: "#3f3f3f"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: configLabel
|
||||||
|
text: modelData['name']
|
||||||
|
color: "white"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
_application.restoreConfig(model.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
label: "X"
|
||||||
|
width: height
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of visible rect
|
||||||
|
} // of delegate item
|
||||||
|
|
||||||
|
}
|
||||||
|
} // of frame rect
|
||||||
|
}
|
BIN
utils/fgqcanvas/qml/checkerboard.png
Normal file
BIN
utils/fgqcanvas/qml/checkerboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 740 B |
58
utils/fgqcanvas/qml/mainMenu.qml
Normal file
58
utils/fgqcanvas/qml/mainMenu.qml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import FlightGear 1.0 as FG
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property bool __uiVisible: true
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
opacity: visible ? 0.5 : 0.0
|
||||||
|
source: "qrc:///images/checkerboard"
|
||||||
|
fillMode: Image.Tile
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: __uiVisible
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 400 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
__uiVisible = !__uiVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: _application.activeCanvases
|
||||||
|
delegate: CanvasFrame {
|
||||||
|
id: display
|
||||||
|
canvas: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowsePanel {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
width: 400
|
||||||
|
visible: __uiVisible
|
||||||
|
opacity: visible ? 1.0: 0.0
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 400 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadSavePanel {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
width: 400
|
||||||
|
visible: __uiVisible
|
||||||
|
opacity: visible ? 1.0: 0.0
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 400 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue