UI handling tweaks
- add class to handle top-level windows - remove requirement to use custom graphics-window - order alongside PUI UI
This commit is contained in:
parent
af00b5e304
commit
a9e5a27e55
22 changed files with 1060 additions and 200 deletions
|
@ -25,7 +25,7 @@ if(COMMAND cmake_policy)
|
|||
|
||||
# OpenGL VND policy : use the old definition for now, until we can audit this
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0071 OLD)
|
||||
cmake_policy(SET CMP0072 OLD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -166,6 +166,8 @@ if (HAVE_QT)
|
|||
QmlNavCacheWrapper.cxx
|
||||
QmlRadioButtonHelper.cxx
|
||||
QmlRadioButtonHelper.hxx
|
||||
QtHelpers.hxx
|
||||
QtHelpers.cxx
|
||||
UnitsModel.cxx
|
||||
UnitsModel.hxx
|
||||
NavaidSearchModel.hxx
|
||||
|
@ -178,6 +180,8 @@ if (HAVE_QT)
|
|||
ModelDataExtractor.hxx
|
||||
HoverArea.cxx
|
||||
HoverArea.hxx
|
||||
FGQQWindowManager.cxx
|
||||
FGQQWindowManager.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <string>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
/**
|
||||
* open a URL using the system's web-browser
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include <Foundation/NSLocale.h>
|
||||
#include <AppKit/NSAlert.h>
|
||||
|
||||
#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
|
||||
|
||||
// simgear
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
@ -47,6 +49,9 @@
|
|||
|
||||
#if defined(HAVE_QT)
|
||||
# include <GUI/QtLauncher.hxx>
|
||||
# include <QOpenGLContext>
|
||||
# include <QtPlatformHeaders/QCocoaNativeContext>
|
||||
# include <QWindow>
|
||||
#endif
|
||||
|
||||
NSString* stdStringToCocoa(const std::string& s)
|
||||
|
@ -151,6 +156,41 @@ SGPath Options::platformDefaultRoot() const
|
|||
dataDir.append("data");
|
||||
return dataDir;
|
||||
}
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
|
||||
{
|
||||
osgViewer::GraphicsWindowCocoa* win = dynamic_cast<osgViewer::GraphicsWindowCocoa*>(context);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto cnc = QCocoaNativeContext{win->getContext()};
|
||||
QVariant nativeHandle = QVariant::fromValue(cnc);
|
||||
|
||||
if (nativeHandle.isNull())
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
|
||||
qtOpenGLContext->setNativeHandle(nativeHandle);
|
||||
if (!qtOpenGLContext->create()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qtOpenGLContext.release();
|
||||
}
|
||||
|
||||
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
|
||||
{
|
||||
osgViewer::GraphicsWindowCocoa* win = dynamic_cast<osgViewer::GraphicsWindowCocoa*>(graphicsWindow);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NSView* osgView = [win->getContext() view];
|
||||
return QWindow::fromWinId(reinterpret_cast<qlonglong>(osgView));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
|
@ -222,3 +262,5 @@ void cocoaRegisterTerminateHandler()
|
|||
name:NSApplicationWillTerminateNotification object:app];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
259
src/GUI/FGQQWindowManager.cxx
Normal file
259
src/GUI/FGQQWindowManager.cxx
Normal file
|
@ -0,0 +1,259 @@
|
|||
#include "FGQQWindowManager.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlComponent>
|
||||
#include <QRectF>
|
||||
#include <QSettings>
|
||||
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
#include "QtHelpers.hxx"
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief information on all the dialogs / windows we have registered
|
||||
*/
|
||||
struct DialogInfo {
|
||||
DialogInfo(SGPropertyNode* node)
|
||||
{
|
||||
id = QString::fromStdString(node->getStringValue("id"));
|
||||
if (id.isEmpty()) {
|
||||
throw std::runtime_error("missing ID");
|
||||
}
|
||||
|
||||
title = QString::fromStdString(node->getStringValue("title"));
|
||||
|
||||
SGPath p = globals->get_fg_root() / "gui" / "quick" / node->getStringValue("file");
|
||||
if (!p.exists()) {
|
||||
qWarning() << "missing dialog QML file: " << p;
|
||||
}
|
||||
resource = QUrl::fromLocalFile(QString::fromStdString(p.utf8Str()));
|
||||
}
|
||||
|
||||
QString id;
|
||||
QString title;
|
||||
QUrl resource;
|
||||
// flags?
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const int WindowContentUrl = Qt::UserRole + 1;
|
||||
const int WindowSize = Qt::UserRole + 2;
|
||||
const int WindowPosition = Qt::UserRole + 3;
|
||||
const int WindowId = Qt::UserRole + 4;
|
||||
const int WindowTitle = Qt::UserRole + 5;
|
||||
|
||||
/**
|
||||
* @brief data about an active window
|
||||
*/
|
||||
struct WindowData {
|
||||
QString windowId;
|
||||
QUrl source;
|
||||
QRectF geometry;
|
||||
QString title;
|
||||
|
||||
// frame flags (close, popout, etc)
|
||||
};
|
||||
|
||||
class WindowsModel : public QAbstractListModel
|
||||
{
|
||||
public:
|
||||
WindowsModel(FGQQWindowManager* wm) : QAbstractListModel(wm)
|
||||
{
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex&) const override
|
||||
{
|
||||
return static_cast<int>(_windowData.size());
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override
|
||||
{
|
||||
QHash<int, QByteArray> r;
|
||||
r[WindowId] = "windowId";
|
||||
r[WindowContentUrl] = "windowContentSource";
|
||||
r[WindowSize] = "windowSize";
|
||||
r[WindowPosition] = "windowPosition";
|
||||
r[WindowTitle] = "windowTitle";
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||
|
||||
void show(const DialogInfo& dinfo)
|
||||
{
|
||||
QSettings settings;
|
||||
QRectF geom = settings.value(dinfo.id + "-geometry").toRectF();
|
||||
if (!geom.isValid()) {
|
||||
// fixme: define default window geometry
|
||||
geom = QRectF{100, 100, 400, 400};
|
||||
}
|
||||
|
||||
WindowData winfo = {dinfo.id, dinfo.resource, geom};
|
||||
|
||||
beginInsertRows({}, _windowData.size(), _windowData.size());
|
||||
_windowData.push_back(winfo);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void close(QString windowId)
|
||||
{
|
||||
auto it = findWindow(windowId);
|
||||
if (it == _windowData.end()) {
|
||||
SG_LOG(SG_GUI, SG_DEV_WARN, "trying to close non-open window:" << windowId.toStdString());
|
||||
return;
|
||||
}
|
||||
|
||||
const int index = std::distance(_windowData.begin(), it);
|
||||
beginRemoveRows({}, index, index);
|
||||
_windowData.erase(it);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
std::vector<WindowData>::const_iterator findWindow(QString windowId) const
|
||||
{
|
||||
return std::find_if(_windowData.begin(), _windowData.end(),
|
||||
[windowId](const WindowData& d) { return d.windowId == windowId; });
|
||||
}
|
||||
|
||||
std::vector<WindowData>::iterator findWindow(QString windowId)
|
||||
{
|
||||
return std::find_if(_windowData.begin(), _windowData.end(),
|
||||
[windowId](const WindowData& d) { return d.windowId == windowId; });
|
||||
}
|
||||
|
||||
std::vector<WindowData> _windowData;
|
||||
};
|
||||
|
||||
QVariant WindowsModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if ((index.row() < 0) || (index.row() >= _windowData.size()))
|
||||
return {};
|
||||
|
||||
const auto& d = _windowData.at(static_cast<size_t>(index.row()));
|
||||
switch (role) {
|
||||
case WindowId:
|
||||
return d.windowId;
|
||||
case WindowContentUrl:
|
||||
return d.source;
|
||||
case WindowSize:
|
||||
return d.geometry.size();
|
||||
case WindowPosition:
|
||||
return d.geometry.topLeft();
|
||||
case WindowTitle:
|
||||
return d.title;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WindowsModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if ((index.row() < 0) || (index.row() >= _windowData.size()))
|
||||
return false;
|
||||
|
||||
auto& d = _windowData[static_cast<size_t>(index.row())];
|
||||
QSettings settings;
|
||||
if (role == WindowSize) {
|
||||
d.geometry.setSize(value.toSizeF());
|
||||
settings.setValue(d.windowId + "-geometry", d.geometry);
|
||||
emit dataChanged(index, index, {role});
|
||||
return true;
|
||||
} else if (role == WindowPosition) {
|
||||
d.geometry.setTopLeft(value.toPointF());
|
||||
settings.setValue(d.windowId + "-geometry", d.geometry);
|
||||
emit dataChanged(index, index, {role});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FGQQWindowManager::WindowManagerPrivate
|
||||
{
|
||||
public:
|
||||
WindowManagerPrivate(FGQQWindowManager* outer) : _p(outer),
|
||||
_windows(new WindowsModel{outer})
|
||||
{
|
||||
// scan dir to find built-in dialogs for now
|
||||
SGPath p = globals->get_fg_root() / "gui" / "quick" / "dialogs.xml";
|
||||
if (p.exists()) {
|
||||
SGPropertyNode_ptr dialogProps(new SGPropertyNode);
|
||||
readProperties(p, dialogProps);
|
||||
for (auto nd : dialogProps->getChildren("dialog")) {
|
||||
try {
|
||||
DialogInfo dinfo(nd);
|
||||
_dialogDefinitions.push_back(dinfo);
|
||||
} catch (std::exception&) {
|
||||
// skip this dialog
|
||||
SG_LOG(SG_GUI, SG_DEV_WARN, "Skipped bad QML dialog definition");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_GUI, SG_DEV_WARN, "No QML dialog definitions found");
|
||||
}
|
||||
}
|
||||
|
||||
FGQQWindowManager* _p;
|
||||
WindowsModel* _windows;
|
||||
std::vector<DialogInfo> _dialogDefinitions;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGQQWindowManager::FGQQWindowManager(QQmlEngine* engine, QObject* parent) : QObject(parent),
|
||||
_d(new WindowManagerPrivate(this)),
|
||||
_engine(engine)
|
||||
{
|
||||
// register commands to open / close / etc
|
||||
}
|
||||
|
||||
FGQQWindowManager::~FGQQWindowManager()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel* FGQQWindowManager::windows() const
|
||||
{
|
||||
return _d->_windows;
|
||||
}
|
||||
|
||||
bool FGQQWindowManager::show(QString windowId)
|
||||
{
|
||||
// find QML file corresponding to windowId
|
||||
auto it = std::find_if(_d->_dialogDefinitions.begin(),
|
||||
_d->_dialogDefinitions.end(),
|
||||
[windowId](const DialogInfo& dd) { return dd.id == windowId; });
|
||||
if (it == _d->_dialogDefinitions.end()) {
|
||||
SG_LOG(SG_GUI, SG_DEV_WARN, "Unknown dialog ID:" << windowId.toStdString());
|
||||
return false;
|
||||
}
|
||||
|
||||
_d->_windows->show(*it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGQQWindowManager::requestPopout(QString windowId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FGQQWindowManager::requestClose(QString windowId)
|
||||
{
|
||||
_d->_windows->close(windowId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FGQQWindowManager::requestPopin(QString windowId)
|
||||
{
|
||||
return false;
|
||||
}
|
40
src/GUI/FGQQWindowManager.hxx
Normal file
40
src/GUI/FGQQWindowManager.hxx
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef FGQQWINDOWMANAGER_HXX
|
||||
#define FGQQWINDOWMANAGER_HXX
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
// forward decls
|
||||
class QAbstractItemModel;
|
||||
class QQmlEngine;
|
||||
|
||||
class FGQQWindowManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FGQQWindowManager(QQmlEngine* engine, QObject* parent = nullptr);
|
||||
~FGQQWindowManager();
|
||||
|
||||
Q_PROPERTY(QAbstractItemModel* windows READ windows CONSTANT)
|
||||
|
||||
QAbstractItemModel* windows() const;
|
||||
|
||||
Q_INVOKABLE bool show(QString windowId);
|
||||
|
||||
Q_INVOKABLE bool requestPopout(QString windowId);
|
||||
|
||||
Q_INVOKABLE bool requestClose(QString windowId);
|
||||
|
||||
Q_INVOKABLE bool requestPopin(QString windowId);
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
class WindowManagerPrivate;
|
||||
|
||||
std::unique_ptr<WindowManagerPrivate> _d;
|
||||
QQmlEngine* _engine = nullptr;
|
||||
};
|
||||
|
||||
#endif // FGQQWINDOWMANAGER_HXX
|
537
src/GUI/QQuickDrawable.cxx
Normal file → Executable file
537
src/GUI/QQuickDrawable.cxx
Normal file → Executable file
|
@ -1,56 +1,110 @@
|
|||
// QQuickDrawable.cxx - OSG Drawable using a QQuickRenderControl to draw
|
||||
//
|
||||
// Copyright (C) 2019 James Turner <james@flightgear.org>
|
||||
//
|
||||
// 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 "config.h"
|
||||
|
||||
#include "QQuickDrawable.hxx"
|
||||
|
||||
#include <QQuickRenderControl>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickRenderControl>
|
||||
|
||||
#include <QThread>
|
||||
#include <QCoreApplication>
|
||||
#include <QOpenGLContext>
|
||||
#include <QQuickWindow>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QQuickItem>
|
||||
#include <QCoreApplication>
|
||||
#include <QQuickWindow>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QThread>
|
||||
|
||||
#include <osg/GraphicsContext>
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
#include <osgGA/GUIEventHandler>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
//#include <GUI/DialogStateController.hxx>
|
||||
#include <GUI/FGQQWindowManager.hxx>
|
||||
#include <GUI/FGQmlInstance.hxx>
|
||||
#include <GUI/FGQmlPropertyNode.hxx>
|
||||
#include <Viewer/OSGQtAdaption.hxx>
|
||||
|
||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
/**
|
||||
* Helper run on Graphics thread to retrive its Qt wrapper
|
||||
*/
|
||||
class RetriveGraphicsThreadOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
RetriveGraphicsThreadOperation()
|
||||
: osg::GraphicsOperation("RetriveGraphcisThread", false)
|
||||
{}
|
||||
|
||||
QThread* thread() const { return _result; }
|
||||
QOpenGLContext* context() const { return _context; }
|
||||
|
||||
virtual void operator () (osg::GraphicsContext* context) override
|
||||
{
|
||||
_result = QThread::currentThread();
|
||||
_context = QOpenGLContext::currentContext();
|
||||
if (!_context) {
|
||||
qFatal("Use a Qt-based GraphicsWindow/Context");
|
||||
// TODO: use ability to create a QOpenGLContext from native
|
||||
// however, this will also need event-handling to be re-written
|
||||
//_context = qtContextFromOSG(context);
|
||||
}
|
||||
}
|
||||
private:
|
||||
QThread* _result = nullptr;
|
||||
QOpenGLContext* _context = nullptr;
|
||||
#if defined(HAVE_PUI)
|
||||
#include <plib/pu.h>
|
||||
#endif
|
||||
|
||||
|
||||
using namespace osgGA;
|
||||
|
||||
struct QtKey {
|
||||
QtKey(int _o, int _q, QString _s = {}) : osg(_o), qt(_q), s(_s) {}
|
||||
|
||||
int osg;
|
||||
int qt;
|
||||
QString s;
|
||||
};
|
||||
|
||||
const std::initializer_list<QtKey> keymapInit = {
|
||||
// contains all the key mappings except 0..9 and A..Z which are generated
|
||||
// programatically
|
||||
|
||||
{GUIEventAdapter::KEY_Space, Qt::Key_Space, " "},
|
||||
{GUIEventAdapter::KEY_Escape, Qt::Key_Escape, "\x1B"},
|
||||
{GUIEventAdapter::KEY_Return, Qt::Key_Return, "\r"},
|
||||
{GUIEventAdapter::KEY_Tab, Qt::Key_Tab, "\t"},
|
||||
{GUIEventAdapter::KEY_BackSpace, Qt::Key_Backspace, "\x08"},
|
||||
{GUIEventAdapter::KEY_Delete, Qt::Key_Delete, "\x7f"},
|
||||
|
||||
{GUIEventAdapter::KEY_Period, Qt::Key_Period, "."},
|
||||
{GUIEventAdapter::KEY_Comma, Qt::Key_Comma, ","},
|
||||
{GUIEventAdapter::KEY_Colon, Qt::Key_Colon, ":"},
|
||||
{GUIEventAdapter::KEY_Quote, Qt::Key_QuoteLeft, "'"},
|
||||
{GUIEventAdapter::KEY_Quotedbl, Qt::Key_QuoteDbl, "\""},
|
||||
{GUIEventAdapter::KEY_Underscore, Qt::Key_Underscore, "_"},
|
||||
{GUIEventAdapter::KEY_Plus, Qt::Key_Plus, "+"},
|
||||
{GUIEventAdapter::KEY_Minus, Qt::Key_Minus, "-"},
|
||||
{GUIEventAdapter::KEY_Asterisk, Qt::Key_Asterisk, "*"},
|
||||
{GUIEventAdapter::KEY_Equals, Qt::Key_Equal, "="},
|
||||
{GUIEventAdapter::KEY_Slash, Qt::Key_Slash, "/"},
|
||||
|
||||
{GUIEventAdapter::KEY_Left, Qt::Key_Left},
|
||||
{GUIEventAdapter::KEY_Right, Qt::Key_Right},
|
||||
{GUIEventAdapter::KEY_Up, Qt::Key_Up},
|
||||
{GUIEventAdapter::KEY_Down, Qt::Key_Down},
|
||||
|
||||
{GUIEventAdapter::KEY_Shift_L, Qt::Key_Shift},
|
||||
{GUIEventAdapter::KEY_Shift_R, Qt::Key_Shift},
|
||||
{GUIEventAdapter::KEY_Control_L, Qt::Key_Control},
|
||||
{GUIEventAdapter::KEY_Control_R, Qt::Key_Control},
|
||||
{GUIEventAdapter::KEY_Meta_L, Qt::Key_Meta},
|
||||
{GUIEventAdapter::KEY_Meta_R, Qt::Key_Meta},
|
||||
|
||||
|
||||
};
|
||||
|
||||
std::vector<QtKey> global_keymap;
|
||||
|
||||
|
||||
#if 0
|
||||
class SyncPolishOperation : public osg::Operation
|
||||
{
|
||||
|
@ -80,18 +134,19 @@ class CustomRenderControl : public QQuickRenderControl
|
|||
{
|
||||
public:
|
||||
CustomRenderControl(QWindow* win)
|
||||
: _qWindow(win)
|
||||
: _qWindow(win)
|
||||
{
|
||||
Q_ASSERT(win);
|
||||
// Q_ASSERT(win);
|
||||
}
|
||||
|
||||
|
||||
QWindow* renderWindow(QPoint* offset) override
|
||||
{
|
||||
if (offset) {
|
||||
*offset = QPoint(0,0);
|
||||
*offset = QPoint(0, 0);
|
||||
}
|
||||
return _qWindow;
|
||||
}
|
||||
|
||||
private:
|
||||
QWindow* _qWindow = nullptr;
|
||||
};
|
||||
|
@ -100,98 +155,85 @@ class QQuickDrawablePrivate : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
CustomRenderControl* renderControl = nullptr;
|
||||
|
||||
// the window our osgViewer is using
|
||||
flightgear::GraphicsWindowQt5* graphicsWindow = nullptr;
|
||||
|
||||
|
||||
QQmlComponent* qmlComponent = nullptr;
|
||||
QQmlEngine* qmlEngine = nullptr;
|
||||
bool syncRequired = true;
|
||||
|
||||
|
||||
QQuickItem* rootItem = nullptr;
|
||||
|
||||
|
||||
// this window is neither created()-ed nor shown but is needed by
|
||||
// QQuickRenderControl for historical reasons, and gives us a place to
|
||||
// inject events from the OSG side.
|
||||
QQuickWindow* quickWindow = nullptr;
|
||||
|
||||
|
||||
// window representing the OSG window, needed
|
||||
// for making our adpoted context current
|
||||
QWindow* foreignOSGWindow = nullptr;
|
||||
QOpenGLContext* qtContext = nullptr;
|
||||
|
||||
public slots:
|
||||
|
||||
void frameEvent()
|
||||
{
|
||||
if (syncRequired) {
|
||||
renderControl->polishItems();
|
||||
syncRequired = false;
|
||||
|
||||
if (QOpenGLContext::currentContext() != qtContext) {
|
||||
bool ok = qtContext->makeCurrent(foreignOSGWindow);
|
||||
if (!ok) {
|
||||
SG_LOG(SG_GUI, SG_ALERT, "make current failed");
|
||||
}
|
||||
}
|
||||
renderControl->sync();
|
||||
}
|
||||
}
|
||||
public slots:
|
||||
void onComponentLoaded()
|
||||
{
|
||||
if (qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = qmlComponent->errors();
|
||||
Q_FOREACH (const QQmlError &error, errorList) {
|
||||
Q_FOREACH (const QQmlError& error, errorList) {
|
||||
qWarning() << error.url() << error.line() << error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QObject *rootObject = qmlComponent->create();
|
||||
|
||||
QObject* rootObject = qmlComponent->create();
|
||||
if (qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = qmlComponent->errors();
|
||||
Q_FOREACH (const QQmlError &error, errorList) {
|
||||
Q_FOREACH (const QQmlError& error, errorList) {
|
||||
qWarning() << error.url() << error.line() << error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
rootItem = qobject_cast<QQuickItem *>(rootObject);
|
||||
|
||||
rootItem = qobject_cast<QQuickItem*>(rootObject);
|
||||
if (!rootItem) {
|
||||
qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject;
|
||||
delete rootObject;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// The root item is ready. Associate it with the window.
|
||||
rootItem->setParentItem(quickWindow->contentItem());
|
||||
syncRequired = true;
|
||||
rootItem->setWidth(quickWindow->width());
|
||||
rootItem->setHeight(quickWindow->height());
|
||||
}
|
||||
|
||||
|
||||
void onSceneChanged()
|
||||
{
|
||||
syncRequired = true;
|
||||
}
|
||||
|
||||
|
||||
void onRenderRequested()
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* event) override
|
||||
{
|
||||
if (obj != graphicsWindow->getGLWindow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto eventType = event->type();
|
||||
switch (eventType) {
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
bool result = QCoreApplication::sendEvent(quickWindow, event);
|
||||
// result always seems to be true here, not useful
|
||||
if (result && event->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
static QObject* fgqmlinstance_provider(QQmlEngine* engine, QJSEngine* scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine)
|
||||
Q_UNUSED(scriptEngine)
|
||||
|
@ -200,73 +242,270 @@ static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngi
|
|||
return instance;
|
||||
}
|
||||
|
||||
QQuickDrawable::QQuickDrawable() :
|
||||
d(new QQuickDrawablePrivate)
|
||||
static QObject* fgqq_windowManager_provider(QQmlEngine* engine, QJSEngine* scriptEngine)
|
||||
{
|
||||
Q_UNUSED(scriptEngine)
|
||||
|
||||
FGQQWindowManager* instance = new FGQQWindowManager(engine);
|
||||
return instance;
|
||||
}
|
||||
|
||||
class ReloadCommand : public SGCommandMgr::Command
|
||||
{
|
||||
public:
|
||||
ReloadCommand(QQuickDrawable* qq) : _drawable(qq)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const SGPropertyNode* aNode, SGPropertyNode* root) override
|
||||
{
|
||||
SG_UNUSED(aNode);
|
||||
SG_UNUSED(root);
|
||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
||||
_drawable->reload(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QQuickDrawable* _drawable;
|
||||
};
|
||||
|
||||
class QuickEventHandler : public osgGA::GUIEventHandler
|
||||
{
|
||||
public:
|
||||
QuickEventHandler(QQuickDrawablePrivate* p) : _drawable(p)
|
||||
{
|
||||
populateKeymap();
|
||||
}
|
||||
|
||||
bool handle(const GUIEventAdapter& ea, GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor*) override
|
||||
{
|
||||
Q_UNUSED(aa);
|
||||
|
||||
if (ea.getHandled()) return false;
|
||||
|
||||
// frame event, ho hum ...
|
||||
if (ea.getEventType() == GUIEventAdapter::FRAME) {
|
||||
_drawable->frameEvent();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Qt expects increasing downward mouse coords
|
||||
const float fixedY = (ea.getMouseYOrientation() == GUIEventAdapter::Y_INCREASING_UPWARDS) ? ea.getWindowHeight() - ea.getY() : ea.getY();
|
||||
const double pixelRatio = _drawable->foreignOSGWindow->devicePixelRatio();
|
||||
|
||||
QPointF pointInWindow{ea.getX() / pixelRatio, fixedY / pixelRatio};
|
||||
QPointF screenPt = pointInWindow +
|
||||
QPointF{ea.getWindowX() / pixelRatio, ea.getWindowY() / pixelRatio};
|
||||
|
||||
// const int scaledX = static_cast<int>(ea.getX() / static_pixelRatio);
|
||||
// const int scaledY = static_cast<int>(fixedY / static_pixelRatio);
|
||||
|
||||
switch (ea.getEventType()) {
|
||||
case (GUIEventAdapter::DRAG):
|
||||
case (GUIEventAdapter::MOVE): {
|
||||
QMouseEvent m(QEvent::MouseMove, pointInWindow, pointInWindow, screenPt,
|
||||
Qt::NoButton,
|
||||
osgButtonMaskToQt(ea), osgModifiersToQt(ea));
|
||||
QCoreApplication::sendEvent(_drawable->quickWindow, &m);
|
||||
return m.isAccepted();
|
||||
}
|
||||
|
||||
case (GUIEventAdapter::PUSH):
|
||||
case (GUIEventAdapter::RELEASE): {
|
||||
const bool isUp = (ea.getEventType() == GUIEventAdapter::RELEASE);
|
||||
QMouseEvent m(isUp ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress,
|
||||
pointInWindow, pointInWindow, screenPt,
|
||||
osgButtonToQt(ea),
|
||||
osgButtonMaskToQt(ea), osgModifiersToQt(ea));
|
||||
QCoreApplication::sendEvent(_drawable->quickWindow, &m);
|
||||
|
||||
if (!isUp) {
|
||||
if (m.isAccepted()) {
|
||||
// deactivate PUI
|
||||
auto active = puActiveWidget();
|
||||
if (active) {
|
||||
active->invokeDownCallback();
|
||||
}
|
||||
}
|
||||
|
||||
// on mouse downs which we don't accept, take focus back
|
||||
// qInfo() << "Clearing QQ focus";
|
||||
// auto focused = _drawable->quickWindow->activeFocusItem();
|
||||
// if (focused) {
|
||||
// focused->setFocus(false, Qt::MouseFocusReason);
|
||||
// }
|
||||
}
|
||||
|
||||
return m.isAccepted();
|
||||
}
|
||||
|
||||
case (GUIEventAdapter::KEYDOWN):
|
||||
case (GUIEventAdapter::KEYUP): {
|
||||
const bool isKeyRelease = (ea.getEventType() == GUIEventAdapter::KEYUP);
|
||||
const auto& key = osgKeyToQt(ea.getKey());
|
||||
QString s = key.s;
|
||||
|
||||
QKeyEvent k(isKeyRelease ? QEvent::KeyRelease : QEvent::KeyPress,
|
||||
key.qt, osgModifiersToQt(ea), s);
|
||||
QCoreApplication::sendEvent(_drawable->quickWindow, &k);
|
||||
return k.isAccepted();
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
Qt::MouseButtons osgButtonMaskToQt(const osgGA::GUIEventAdapter& ea) const
|
||||
{
|
||||
const int mask = ea.getButtonMask();
|
||||
Qt::MouseButtons result = Qt::NoButton;
|
||||
if (mask & osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
|
||||
result |= Qt::LeftButton;
|
||||
|
||||
if (mask & osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON)
|
||||
result |= Qt::MiddleButton;
|
||||
|
||||
if (mask & osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)
|
||||
result |= Qt::RightButton;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Qt::MouseButton osgButtonToQt(const osgGA::GUIEventAdapter& ea) const
|
||||
{
|
||||
switch (ea.getButton()) {
|
||||
case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON: return Qt::LeftButton;
|
||||
case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON: return Qt::MiddleButton;
|
||||
case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON: return Qt::RightButton;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Qt::NoButton;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers osgModifiersToQt(const osgGA::GUIEventAdapter& ea) const
|
||||
{
|
||||
Qt::KeyboardModifiers result = Qt::NoModifier;
|
||||
const int mask = ea.getModKeyMask();
|
||||
if (mask & osgGA::GUIEventAdapter::MODKEY_ALT) result |= Qt::AltModifier;
|
||||
if (mask & osgGA::GUIEventAdapter::MODKEY_CTRL) result |= Qt::ControlModifier;
|
||||
if (mask & osgGA::GUIEventAdapter::MODKEY_META) result |= Qt::MetaModifier;
|
||||
if (mask & osgGA::GUIEventAdapter::MODKEY_SHIFT) result |= Qt::ShiftModifier;
|
||||
return result;
|
||||
}
|
||||
|
||||
QtKey osgKeyToQt(int code) const
|
||||
{
|
||||
auto it = std::lower_bound(global_keymap.begin(), global_keymap.end(),
|
||||
QtKey{code, 0, {}},
|
||||
[](const QtKey& a, const QtKey& b) { return a.osg < b.osg; });
|
||||
if ((it == global_keymap.end()) || (it->osg != code)) {
|
||||
qWarning() << "no mapping defined for OSG key:" << code;
|
||||
return {0, 0, ""};
|
||||
}
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
void populateKeymap()
|
||||
{
|
||||
if (!global_keymap.empty())
|
||||
return;
|
||||
|
||||
// regular keymappsing for A..Z and 0..9
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
global_keymap.emplace_back(GUIEventAdapter::KEY_0 + i, Qt::Key_0 + i, QString::number(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 26; ++i) {
|
||||
global_keymap.emplace_back(GUIEventAdapter::KEY_A + i, Qt::Key_A + i, QChar::fromLatin1('a' + i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 26; ++i) {
|
||||
global_keymap.emplace_back('A' + i, Qt::Key_A + i, QChar::fromLatin1('A' + i));
|
||||
}
|
||||
|
||||
// custom key mappsing
|
||||
global_keymap.insert(global_keymap.end(), keymapInit);
|
||||
|
||||
// sort by OSG code for fast lookups
|
||||
std::sort(global_keymap.begin(), global_keymap.end(),
|
||||
[](const QtKey& a, const QtKey& b) { return a.osg < b.osg; });
|
||||
}
|
||||
|
||||
QQuickDrawablePrivate* _drawable;
|
||||
};
|
||||
|
||||
QQuickDrawable::QQuickDrawable() : d(new QQuickDrawablePrivate)
|
||||
{
|
||||
setUseDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
|
||||
|
||||
osg::StateSet* stateSet = getOrCreateStateSet();
|
||||
stateSet->setRenderBinDetails(1001, "RenderBin");
|
||||
#if 0
|
||||
// speed optimization?
|
||||
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
// We can do translucent menus, so why not. :-)
|
||||
stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
||||
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
|
||||
stateSet->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE));
|
||||
|
||||
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
#endif
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
static bool doneQmlRegistration = false;
|
||||
if (!doneQmlRegistration) {
|
||||
doneQmlRegistration = true;
|
||||
|
||||
// singleton system object
|
||||
qmlRegisterSingletonType<FGQmlInstance>("flightgear.org", 1, 0, "System", fgqmlinstance_provider);
|
||||
qmlRegisterSingletonType<FGQmlInstance>("FlightGear", 1, 0, "System", fgqmlinstance_provider);
|
||||
qmlRegisterSingletonType<FGQmlInstance>("FlightGear", 1, 0, "WindowManager", fgqq_windowManager_provider);
|
||||
|
||||
// QML types
|
||||
qmlRegisterType<FGQmlPropertyNode>("flightgear.org", 1, 0, "Property");
|
||||
qmlRegisterType<FGQmlPropertyNode>("FlightGear", 1, 0, "Property");
|
||||
//qmlRegisterType<DialogStateController>("FlightGear", 1, 0, "DialogStateController");
|
||||
}
|
||||
}
|
||||
|
||||
QQuickDrawable::~QQuickDrawable()
|
||||
{
|
||||
delete d->qmlEngine;
|
||||
delete d->renderControl;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
|
||||
void QQuickDrawable::setup(osgViewer::GraphicsWindow *gw, osgViewer::Viewer *viewer)
|
||||
{
|
||||
osg::GraphicsContext* gc = gw;
|
||||
|
||||
// install operations on the graphics context to get the context
|
||||
osg::ref_ptr<RetriveGraphicsThreadOperation> op(new RetriveGraphicsThreadOperation);
|
||||
|
||||
osg::ref_ptr<flightgear::RetriveGraphicsThreadOperation> op(new flightgear::RetriveGraphicsThreadOperation);
|
||||
gc->add(op);
|
||||
gc->runOperations();
|
||||
|
||||
|
||||
// hopefully done now!
|
||||
|
||||
|
||||
d->qtContext = op->context();
|
||||
d->graphicsWindow = dynamic_cast<flightgear::GraphicsWindowQt5*>(gw);
|
||||
if (!d->graphicsWindow) {
|
||||
qFatal("QQuick drawable requires a GraphicsWindowQt5 for now");
|
||||
}
|
||||
d->renderControl = new CustomRenderControl(d->graphicsWindow->getGLWindow());
|
||||
|
||||
|
||||
// d->qtContext->setFormat(format);
|
||||
|
||||
d->foreignOSGWindow = flightgear::qtWindowFromOSG(gw);
|
||||
// d->foreignOSGWindow->setFormat(format);
|
||||
d->foreignOSGWindow->setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
d->renderControl = new CustomRenderControl(d->foreignOSGWindow);
|
||||
d->quickWindow = new QQuickWindow(d->renderControl);
|
||||
|
||||
|
||||
bool ok = d->qtContext->makeCurrent(d->foreignOSGWindow);
|
||||
if (!ok) {
|
||||
SG_LOG(SG_GUI, SG_ALERT, "context not current");
|
||||
}
|
||||
|
||||
d->quickWindow->setClearBeforeRendering(false);
|
||||
|
||||
|
||||
d->qmlEngine = new QQmlEngine;
|
||||
|
||||
SGPath rootQMLPath = SGPath::fromUtf8(fgGetString("/sim/gui/qml-root-path"));
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Root QML dir:" << rootQMLPath.dir());
|
||||
d->qmlEngine->addImportPath(QString::fromStdString(rootQMLPath.dir()));
|
||||
// d->qmlEngine->addImportPath(QStringLiteral("qrc:///"));
|
||||
|
||||
if (!d->qmlEngine->incubationController())
|
||||
d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
|
||||
|
||||
|
||||
d->renderControl->initialize(d->qtContext);
|
||||
|
||||
#if QT_VERSION < 0x050600
|
||||
|
@ -278,42 +517,43 @@ void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
|
|||
d, &QQuickDrawablePrivate::onSceneChanged);
|
||||
QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested,
|
||||
d, &QQuickDrawablePrivate::onRenderRequested);
|
||||
|
||||
// insert ourselves as a filter on the GraphicsWindow, so we can intercept
|
||||
// events directly and pass on to our window
|
||||
d->graphicsWindow->getGLWindow()->installEventFilter(d);
|
||||
|
||||
|
||||
viewer->getEventHandlers().push_front(new QuickEventHandler(d));
|
||||
}
|
||||
|
||||
void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
if (d->syncRequired) {
|
||||
// should happen on the main thread
|
||||
d->renderControl->polishItems();
|
||||
|
||||
d->syncRequired = false;
|
||||
// must ensure the GUI thread is blocked for this
|
||||
d->renderControl->sync();
|
||||
// can unblock now
|
||||
}
|
||||
|
||||
QOpenGLFunctions* glFuncs = d->qtContext->functions();
|
||||
// prepare any state QQ2 needs
|
||||
d->quickWindow->resetOpenGLState();
|
||||
|
||||
|
||||
// and reset these manually
|
||||
glFuncs->glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glFuncs->glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glFuncs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
|
||||
d->renderControl->render();
|
||||
|
||||
glFuncs->glFlush();
|
||||
|
||||
|
||||
// otherwise the PUI camera gets confused
|
||||
d->quickWindow->resetOpenGLState();
|
||||
}
|
||||
|
||||
void QQuickDrawable::reload(QUrl url)
|
||||
{
|
||||
d->qmlEngine->clearComponentCache();
|
||||
setSource(url);
|
||||
}
|
||||
|
||||
void QQuickDrawable::setSource(QUrl url)
|
||||
{
|
||||
if (d->rootItem)
|
||||
delete d->rootItem;
|
||||
if (d->qmlComponent)
|
||||
delete d->qmlComponent;
|
||||
d->rootItem = nullptr;
|
||||
|
||||
d->qmlComponent = new QQmlComponent(d->qmlEngine, url);
|
||||
if (d->qmlComponent->isLoading()) {
|
||||
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
|
||||
|
@ -326,15 +566,16 @@ void QQuickDrawable::setSource(QUrl url)
|
|||
void QQuickDrawable::resize(int width, int height)
|
||||
{
|
||||
// we need to unscale from physical pixels back to logical, otherwise we end up double-scaled.
|
||||
const float currentPixelRatio = d->graphicsWindow->getGLWindow()->devicePixelRatio();
|
||||
const float currentPixelRatio = static_cast<float>(d->foreignOSGWindow->devicePixelRatio());
|
||||
const int logicalWidth = static_cast<int>(width / currentPixelRatio);
|
||||
const int logicalHeight = static_cast<int>(height / currentPixelRatio);
|
||||
|
||||
|
||||
if (d->rootItem) {
|
||||
d->rootItem->setWidth(logicalWidth);
|
||||
d->rootItem->setHeight(logicalHeight);
|
||||
}
|
||||
|
||||
|
||||
SG_LOG(SG_GUI, SG_INFO, "Resize:, lw=" << logicalWidth << ", lh=" << logicalHeight);
|
||||
d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight);
|
||||
}
|
||||
|
||||
|
|
32
src/GUI/QQuickDrawable.hxx
Normal file → Executable file
32
src/GUI/QQuickDrawable.hxx
Normal file → Executable file
|
@ -1,3 +1,21 @@
|
|||
// QQuickDrawable.hxx - OSG Drawable using a QQuickRenderControl to draw
|
||||
//
|
||||
// Copyright (C) 2019 James Turner <james@flightgear.org>
|
||||
//
|
||||
// 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 FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||
#define FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||
|
||||
|
@ -6,6 +24,10 @@
|
|||
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
namespace osgViewer {
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
class QQuickDrawablePrivate;
|
||||
|
||||
class QQuickDrawable : public osg::Drawable
|
||||
|
@ -19,16 +41,14 @@ public:
|
|||
|
||||
void drawImplementation(osg::RenderInfo& renderInfo) const override;
|
||||
|
||||
/** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */
|
||||
// virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
|
||||
|
||||
//virtual void accept(osg::PrimitiveFunctor& functor) const;
|
||||
|
||||
void setup(osgViewer::GraphicsWindow* gw);
|
||||
void setup(osgViewer::GraphicsWindow* gw, osgViewer::Viewer* viewer);
|
||||
|
||||
void setSource(QUrl url);
|
||||
|
||||
void reload(QUrl url);
|
||||
|
||||
void resize(int width, int height);
|
||||
|
||||
private:
|
||||
QQuickDrawablePrivate* d;
|
||||
};
|
||||
|
|
29
src/GUI/QtHelpers.cxx
Normal file
29
src/GUI/QtHelpers.cxx
Normal file
|
@ -0,0 +1,29 @@
|
|||
// QtHelpers.cxx - make Qt and SimGear/FlightGear place nicely
|
||||
//
|
||||
// Copyright (C) 2019 James Turner <james@flightgear.org>
|
||||
//
|
||||
// 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 "config.h"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "QtHelpers.hxx"
|
||||
|
||||
QDebug operator<<(QDebug debug, const SGPath& c)
|
||||
{
|
||||
debug << QString::fromStdString(c.utf8Str());
|
||||
return debug;
|
||||
}
|
28
src/GUI/QtHelpers.hxx
Normal file
28
src/GUI/QtHelpers.hxx
Normal file
|
@ -0,0 +1,28 @@
|
|||
// QtHelpers.hxx - make Qt and SimGear/FlightGear place nicely
|
||||
////
|
||||
// Copyright (C) 2019 James Turner <james@flightgear.org>
|
||||
//
|
||||
// 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 QTGUI_HELPERS_H
|
||||
#define QTGUI_HELPERS_H
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class SGPath;
|
||||
|
||||
QDebug operator<<(QDebug debug, const SGPath& c);
|
||||
|
||||
#endif // QTGUI_HELPERS_H
|
|
@ -1,9 +0,0 @@
|
|||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
|
||||
RenderSettings
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,6 @@
|
|||
<file>qml/Style.qml</file>
|
||||
<file>qml/qmldir</file>
|
||||
<file>qml/ClickableText.qml</file>
|
||||
<file>qml/LauncherSettings.qml</file>
|
||||
<file>qml/Section.qml</file>
|
||||
<file>qml/SettingControl.qml</file>
|
||||
<file>qml/SettingCheckbox.qml</file>
|
||||
|
|
|
@ -149,3 +149,12 @@ if (MSVC)
|
|||
set_property(TARGET fgfs PROPERTY
|
||||
VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}")
|
||||
endif()
|
||||
|
||||
if (TARGET Qt5::qmake)
|
||||
get_target_property(_qt5_qmake_path Qt5::qmake LOCATION)
|
||||
get_filename_component(_qt5_bin_dir ${_qt5_qmake_path} DIRECTORY)
|
||||
get_filename_component(_qt5_root_dir ${_qt5_bin_dir} DIRECTORY)
|
||||
|
||||
# for QtPlatformHeaders
|
||||
target_include_directories(fgfs PRIVATE ${_qt5_root_dir}/include)
|
||||
endif()
|
||||
|
|
|
@ -25,8 +25,10 @@ set(HEADERS
|
|||
)
|
||||
|
||||
if (Qt5Core_FOUND)
|
||||
list(APPEND HEADERS GraphicsWindowQt5.hxx)
|
||||
list(APPEND SOURCES GraphicsWindowQt5.cpp)
|
||||
list(APPEND HEADERS GraphicsWindowQt5.hxx
|
||||
OSGQtAdaption.hxx)
|
||||
list(APPEND SOURCES GraphicsWindowQt5.cpp
|
||||
OSGQtAdaption.cxx)
|
||||
endif()
|
||||
|
||||
if (YES)
|
||||
|
|
|
@ -788,6 +788,9 @@ public:
|
|||
|
||||
Qt5WindowingSystem()
|
||||
{
|
||||
#if OSG_VERSION_GREATER_THAN(3,5,9)
|
||||
setName("FlightGearQt5");
|
||||
#endif
|
||||
}
|
||||
|
||||
~Qt5WindowingSystem()
|
||||
|
@ -899,6 +902,8 @@ void initQtWindowingSystem()
|
|||
{
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface());
|
||||
#else
|
||||
osg::GraphicsContext::getWindowingSystemInterfaces()->addWindowingSystemInterface(Qt5WindowingSystem::getInterface());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
126
src/Viewer/OSGQtAdaption.cxx
Normal file
126
src/Viewer/OSGQtAdaption.cxx
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright (C) 2020 James Turner
|
||||
|
||||
// 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 "config.h"
|
||||
|
||||
#include "OSGQtAdaption.hxx"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QWindow>
|
||||
#include <QThread>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#if defined(SG_MAC)
|
||||
// defined in CocoaUtils.mm
|
||||
#elif defined(SG_WINDOWS)
|
||||
# include <QtPlatformHeaders/QWGLNativeContext>
|
||||
# include <osgViewer/api/Win32/GraphicsWindowWin32>
|
||||
#else
|
||||
// assume X11 for now
|
||||
# include <osgViewer/api/X11/GraphicsWindowX11>
|
||||
# include <QtPlatformHeaders/QGLXNativeContext>
|
||||
#endif
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
#if defined(SG_MAC)
|
||||
|
||||
// defined in CocoaUtils.mm
|
||||
|
||||
#elif defined(SG_WINDOWS)
|
||||
|
||||
|
||||
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
|
||||
{
|
||||
auto win = dynamic_cast<osgViewer::GraphicsWindowWin32*>(context);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wglnc = QWGLNativeContext{win->getWGLContext(), win->getHWND()};
|
||||
QVariant nativeHandle = QVariant::fromValue(wglnc);
|
||||
|
||||
if (nativeHandle.isNull())
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
|
||||
qtOpenGLContext->setNativeHandle(nativeHandle);
|
||||
if (!qtOpenGLContext->create()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qtOpenGLContext.release();
|
||||
}
|
||||
|
||||
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
|
||||
{
|
||||
auto win = dynamic_cast<osgViewer::GraphicsWindowWin32*>(graphicsWindow);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return QWindow::fromWinId(reinterpret_cast<qlonglong>(win->getHWND()));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// assume X11 for now
|
||||
|
||||
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context)
|
||||
{
|
||||
auto win = dynamic_cast<osgViewer::GraphicsWindowX11*>(context);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto glxnc = QGLXNativeContext{win->getContext(), win->getDisplay(), win->getWindow(), 0};
|
||||
QVariant nativeHandle = QVariant::fromValue(glxnc);
|
||||
|
||||
if (nativeHandle.isNull())
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<QOpenGLContext> qtOpenGLContext(new QOpenGLContext);
|
||||
qtOpenGLContext->setNativeHandle(nativeHandle);
|
||||
if (!qtOpenGLContext->create()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qtOpenGLContext.release();
|
||||
}
|
||||
|
||||
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
|
||||
{
|
||||
auto win = dynamic_cast<osgViewer::GraphicsWindowX11*>(graphicsWindow);
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return QWindow::fromWinId(static_cast<qlonglong>(win->getWindow()));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void RetriveGraphicsThreadOperation::operator()(osg::GraphicsContext* context)
|
||||
{
|
||||
_result = QThread::currentThread();
|
||||
_context = qtContextFromOSG(context);
|
||||
}
|
||||
|
||||
} // of namespace
|
53
src/Viewer/OSGQtAdaption.hxx
Normal file
53
src/Viewer/OSGQtAdaption.hxx
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright (C) 2020 James Turner
|
||||
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
#include <osg/GraphicsContext>
|
||||
|
||||
class QWindow;
|
||||
class QOpenGLContext;
|
||||
class QThread;
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
QOpenGLContext* qtContextFromOSG(osg::GraphicsContext* context);
|
||||
QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow);
|
||||
|
||||
/**
|
||||
* Helper run on Graphics thread to retrive its Qt wrapper
|
||||
*/
|
||||
class RetriveGraphicsThreadOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
RetriveGraphicsThreadOperation()
|
||||
: osg::GraphicsOperation("RetriveGraphicsThread", false)
|
||||
{
|
||||
}
|
||||
|
||||
QThread* thread() const { return _result; }
|
||||
QOpenGLContext* context() const { return _context; }
|
||||
|
||||
void operator()(osg::GraphicsContext* context) override;
|
||||
private:
|
||||
QThread* _result = nullptr;
|
||||
QOpenGLContext* _context = nullptr;
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -29,9 +29,10 @@
|
|||
#include <osg/BlendFunc>
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osgUtil/CullVisitor>
|
||||
#include <osgGA/GUIEventHandler>
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
#include <osgGA/GUIEventHandler>
|
||||
#include <osgUtil/CullVisitor>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <plib/pu.h>
|
||||
|
||||
|
@ -301,7 +302,7 @@ PUICamera::PUICamera() :
|
|||
{
|
||||
}
|
||||
|
||||
void PUICamera::init(osg::Group* parent)
|
||||
void PUICamera::init(osg::Group* parent, osgViewer::Viewer* viewer)
|
||||
{
|
||||
setName("PUI FBO camera");
|
||||
|
||||
|
@ -363,11 +364,6 @@ void PUICamera::init(osg::Group* parent)
|
|||
fsQuadGeode->addDrawable(_fullScreenQuad);
|
||||
fsQuadGeode->setName("PUI fullscreen Geode");
|
||||
fsQuadGeode->setNodeMask(SG_NODEMASK_GUI_BIT);
|
||||
|
||||
// set the event callback on the Geode; if we set it on the Drawable,
|
||||
// osgGA calls it twice (as a NodeCallback and also a Drawable::EventCallback)
|
||||
fsQuadGeode->setEventCallback(new PUIEventHandler(this));
|
||||
|
||||
parent->addChild(this);
|
||||
parent->addChild(fsQuadGeode);
|
||||
|
||||
|
@ -376,6 +372,11 @@ void PUICamera::init(osg::Group* parent)
|
|||
osg::Viewport* vport = camera->getViewport();
|
||||
resizeUi(vport->width(), vport->height());
|
||||
}
|
||||
|
||||
// push_front so we keep the order of event handlers the opposite of
|
||||
// the rendering order (i.e top-most UI layer has the front-most event
|
||||
// handler)
|
||||
viewer->getEventHandlers().push_front(new PUIEventHandler(this));
|
||||
}
|
||||
|
||||
// remove once we require OSG 3.4
|
||||
|
@ -400,12 +401,13 @@ void PUICamera::resizeUi(int width, int height)
|
|||
resizeAttachments(scaledWidth, scaledHeight);
|
||||
#endif
|
||||
|
||||
const float puiZ = 1.0;
|
||||
// resize the full-screen quad
|
||||
osg::Vec3Array* fsQuadVertices = static_cast<osg::Vec3Array*>(_fullScreenQuad->getVertexArray());
|
||||
(*fsQuadVertices)[0] = osg::Vec3(0.0, height, 0.0);
|
||||
(*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, 0.0);
|
||||
(*fsQuadVertices)[2] = osg::Vec3(width, 0.0, 0.0);
|
||||
(*fsQuadVertices)[3] = osg::Vec3(width, height, 0.0);
|
||||
(*fsQuadVertices)[0] = osg::Vec3(0.0, height, puiZ);
|
||||
(*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, puiZ);
|
||||
(*fsQuadVertices)[2] = osg::Vec3(width, 0.0, puiZ);
|
||||
(*fsQuadVertices)[3] = osg::Vec3(width, height, puiZ);
|
||||
|
||||
fsQuadVertices->dirty();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ namespace osg
|
|||
class Geometry;
|
||||
}
|
||||
|
||||
namespace osgViewer {
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
|
@ -41,7 +45,9 @@ public:
|
|||
|
||||
// osg::Camera already defines a resize() so use this name
|
||||
void resizeUi(int width, int height);
|
||||
void init(osg::Group *parent);
|
||||
|
||||
void init(osg::Group* parent, osgViewer::Viewer* viewer);
|
||||
|
||||
private:
|
||||
#if OSG_VERSION_LESS_THAN(3,4,0)
|
||||
class UpdateViewportAndFBOAfterTextureResizeCallback;
|
||||
|
|
|
@ -14,14 +14,12 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
|
||||
#include "WindowBuilder.hxx"
|
||||
|
||||
#include "WindowSystemAdapter.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
@ -72,6 +70,14 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
|
|||
{
|
||||
GraphicsContext::WindowingSystemInterface* wsi
|
||||
= osg::GraphicsContext::getWindowingSystemInterface();
|
||||
#if defined(HAVE_QT) && OSG_VERSION_GREATER_THAN(3, 5, 9)
|
||||
if (usingQtGraphicsWindow) {
|
||||
// use the correct WSI for OpenSceneGraph >= 3.6
|
||||
wsi = osg::GraphicsContext::getWindowingSystemInterface("FlightGearQt5");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
defaultTraits = new osg::GraphicsContext::Traits;
|
||||
auto traits = defaultTraits.get();
|
||||
|
||||
|
@ -107,7 +113,10 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
|
|||
auto data = new GraphicsWindowQt5::WindowData;
|
||||
data->createFullscreen = wantFullscreen;
|
||||
data->isPrimaryWindow = true;
|
||||
|
||||
|
||||
#if OSG_VERSION_GREATER_THAN(3, 5, 9)
|
||||
traits->windowingSystemPreference = "FlightGearQt5";
|
||||
#endif
|
||||
traits->inheritedWindowData = data;
|
||||
traits->windowDecoration = true;
|
||||
traits->supportsResize = true;
|
||||
|
|
|
@ -110,7 +110,6 @@
|
|||
|
||||
#if defined(HAVE_QT)
|
||||
#include <GUI/QQuickDrawable.hxx>
|
||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PUI)
|
||||
|
@ -609,23 +608,22 @@ FGRenderer::setupView( void )
|
|||
|
||||
#if defined(HAVE_PUI)
|
||||
_puiCamera = new flightgear::PUICamera;
|
||||
_puiCamera->init(guiCamera);
|
||||
_puiCamera->init(guiCamera, viewer);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
||||
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext());
|
||||
auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
|
||||
|
||||
if (graphicsWindowQt && !rootQMLPath.empty()) {
|
||||
if (!rootQMLPath.empty()) {
|
||||
_quickDrawable = new QQuickDrawable;
|
||||
_quickDrawable->setup(graphicsWindowQt);
|
||||
_quickDrawable->setup(graphicsWindow, viewer);
|
||||
|
||||
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||
geode->addDrawable(_quickDrawable);
|
||||
}
|
||||
#endif
|
||||
// Draw first (eg. before Canvas GUI)
|
||||
guiCamera->insertChild(0, geode);
|
||||
guiCamera->addChild(geode);
|
||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@
|
|||
|
||||
#if defined(HAVE_QT)
|
||||
#include <GUI/QQuickDrawable.hxx>
|
||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PUI)
|
||||
|
@ -1504,23 +1503,22 @@ FGRenderer::setupView( void )
|
|||
|
||||
#if defined(HAVE_PUI)
|
||||
_puiCamera = new flightgear::PUICamera;
|
||||
_puiCamera->init(guiCamera);
|
||||
_puiCamera->init(guiCamera, viewer);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
||||
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext());
|
||||
auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
|
||||
|
||||
if (graphicsWindowQt && !rootQMLPath.empty()) {
|
||||
if (!rootQMLPath.empty()) {
|
||||
_quickDrawable = new QQuickDrawable;
|
||||
_quickDrawable->setup(graphicsWindowQt);
|
||||
_quickDrawable->setup(graphicsWindow, viewer);
|
||||
|
||||
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||
geode->addDrawable(_quickDrawable);
|
||||
}
|
||||
#endif
|
||||
// Draw first (eg. before Canvas GUI)
|
||||
guiCamera->insertChild(0, geode);
|
||||
guiCamera->addChild(geode);
|
||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue