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
|
# OpenGL VND policy : use the old definition for now, until we can audit this
|
||||||
if(POLICY CMP0072)
|
if(POLICY CMP0072)
|
||||||
cmake_policy(SET CMP0071 OLD)
|
cmake_policy(SET CMP0072 OLD)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,8 @@ if (HAVE_QT)
|
||||||
QmlNavCacheWrapper.cxx
|
QmlNavCacheWrapper.cxx
|
||||||
QmlRadioButtonHelper.cxx
|
QmlRadioButtonHelper.cxx
|
||||||
QmlRadioButtonHelper.hxx
|
QmlRadioButtonHelper.hxx
|
||||||
|
QtHelpers.hxx
|
||||||
|
QtHelpers.cxx
|
||||||
UnitsModel.cxx
|
UnitsModel.cxx
|
||||||
UnitsModel.hxx
|
UnitsModel.hxx
|
||||||
NavaidSearchModel.hxx
|
NavaidSearchModel.hxx
|
||||||
|
@ -178,6 +180,8 @@ if (HAVE_QT)
|
||||||
ModelDataExtractor.hxx
|
ModelDataExtractor.hxx
|
||||||
HoverArea.cxx
|
HoverArea.cxx
|
||||||
HoverArea.hxx
|
HoverArea.hxx
|
||||||
|
FGQQWindowManager.cxx
|
||||||
|
FGQQWindowManager.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* open a URL using the system's web-browser
|
* open a URL using the system's web-browser
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include <Foundation/NSLocale.h>
|
#include <Foundation/NSLocale.h>
|
||||||
#include <AppKit/NSAlert.h>
|
#include <AppKit/NSAlert.h>
|
||||||
|
|
||||||
|
#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
|
||||||
|
|
||||||
// simgear
|
// simgear
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
@ -47,6 +49,9 @@
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT)
|
||||||
# include <GUI/QtLauncher.hxx>
|
# include <GUI/QtLauncher.hxx>
|
||||||
|
# include <QOpenGLContext>
|
||||||
|
# include <QtPlatformHeaders/QCocoaNativeContext>
|
||||||
|
# include <QWindow>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NSString* stdStringToCocoa(const std::string& s)
|
NSString* stdStringToCocoa(const std::string& s)
|
||||||
|
@ -152,6 +157,41 @@ SGPath Options::platformDefaultRoot() const
|
||||||
return dataDir;
|
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
|
} // of namespace flightgear
|
||||||
|
|
||||||
string_list FGLocale::getUserLanguage()
|
string_list FGLocale::getUserLanguage()
|
||||||
|
@ -222,3 +262,5 @@ void cocoaRegisterTerminateHandler()
|
||||||
name:NSApplicationWillTerminateNotification object:app];
|
name:NSApplicationWillTerminateNotification object:app];
|
||||||
#endif
|
#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
|
481
src/GUI/QQuickDrawable.cxx
Normal file → Executable file
481
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 "QQuickDrawable.hxx"
|
||||||
|
|
||||||
#include <QQuickRenderControl>
|
|
||||||
#include <QQmlComponent>
|
#include <QQmlComponent>
|
||||||
#include <QQmlEngine>
|
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickRenderControl>
|
||||||
|
|
||||||
#include <QThread>
|
#include <QCoreApplication>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QQuickWindow>
|
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
#include <QCoreApplication>
|
#include <QQuickWindow>
|
||||||
|
#include <QSurfaceFormat>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include <osg/GraphicsContext>
|
#include <osg/GraphicsContext>
|
||||||
|
#include <osgGA/GUIEventAdapter>
|
||||||
|
#include <osgGA/GUIEventHandler>
|
||||||
#include <osgViewer/GraphicsWindow>
|
#include <osgViewer/GraphicsWindow>
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
|
//#include <GUI/DialogStateController.hxx>
|
||||||
|
#include <GUI/FGQQWindowManager.hxx>
|
||||||
#include <GUI/FGQmlInstance.hxx>
|
#include <GUI/FGQmlInstance.hxx>
|
||||||
#include <GUI/FGQmlPropertyNode.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>
|
||||||
|
|
||||||
/**
|
#if defined(HAVE_PUI)
|
||||||
* Helper run on Graphics thread to retrive its Qt wrapper
|
#include <plib/pu.h>
|
||||||
*/
|
#endif
|
||||||
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
|
using namespace osgGA;
|
||||||
{
|
|
||||||
_result = QThread::currentThread();
|
struct QtKey {
|
||||||
_context = QOpenGLContext::currentContext();
|
QtKey(int _o, int _q, QString _s = {}) : osg(_o), qt(_q), s(_s) {}
|
||||||
if (!_context) {
|
|
||||||
qFatal("Use a Qt-based GraphicsWindow/Context");
|
int osg;
|
||||||
// TODO: use ability to create a QOpenGLContext from native
|
int qt;
|
||||||
// however, this will also need event-handling to be re-written
|
QString s;
|
||||||
//_context = qtContextFromOSG(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
QThread* _result = nullptr;
|
|
||||||
QOpenGLContext* _context = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
#if 0
|
||||||
class SyncPolishOperation : public osg::Operation
|
class SyncPolishOperation : public osg::Operation
|
||||||
{
|
{
|
||||||
|
@ -82,16 +136,17 @@ public:
|
||||||
CustomRenderControl(QWindow* win)
|
CustomRenderControl(QWindow* win)
|
||||||
: _qWindow(win)
|
: _qWindow(win)
|
||||||
{
|
{
|
||||||
Q_ASSERT(win);
|
// Q_ASSERT(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWindow* renderWindow(QPoint* offset) override
|
QWindow* renderWindow(QPoint* offset) override
|
||||||
{
|
{
|
||||||
if (offset) {
|
if (offset) {
|
||||||
*offset = QPoint(0,0);
|
*offset = QPoint(0, 0);
|
||||||
}
|
}
|
||||||
return _qWindow;
|
return _qWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWindow* _qWindow = nullptr;
|
QWindow* _qWindow = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -100,12 +155,8 @@ class QQuickDrawablePrivate : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CustomRenderControl* renderControl = nullptr;
|
CustomRenderControl* renderControl = nullptr;
|
||||||
|
|
||||||
// the window our osgViewer is using
|
|
||||||
flightgear::GraphicsWindowQt5* graphicsWindow = nullptr;
|
|
||||||
|
|
||||||
QQmlComponent* qmlComponent = nullptr;
|
QQmlComponent* qmlComponent = nullptr;
|
||||||
QQmlEngine* qmlEngine = nullptr;
|
QQmlEngine* qmlEngine = nullptr;
|
||||||
bool syncRequired = true;
|
bool syncRequired = true;
|
||||||
|
@ -117,29 +168,47 @@ public:
|
||||||
// inject events from the OSG side.
|
// inject events from the OSG side.
|
||||||
QQuickWindow* quickWindow = nullptr;
|
QQuickWindow* quickWindow = nullptr;
|
||||||
|
|
||||||
|
// window representing the OSG window, needed
|
||||||
|
// for making our adpoted context current
|
||||||
|
QWindow* foreignOSGWindow = nullptr;
|
||||||
QOpenGLContext* qtContext = 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()
|
void onComponentLoaded()
|
||||||
{
|
{
|
||||||
if (qmlComponent->isError()) {
|
if (qmlComponent->isError()) {
|
||||||
QList<QQmlError> errorList = qmlComponent->errors();
|
QList<QQmlError> errorList = qmlComponent->errors();
|
||||||
Q_FOREACH (const QQmlError &error, errorList) {
|
Q_FOREACH (const QQmlError& error, errorList) {
|
||||||
qWarning() << error.url() << error.line() << error;
|
qWarning() << error.url() << error.line() << error;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject *rootObject = qmlComponent->create();
|
QObject* rootObject = qmlComponent->create();
|
||||||
if (qmlComponent->isError()) {
|
if (qmlComponent->isError()) {
|
||||||
QList<QQmlError> errorList = qmlComponent->errors();
|
QList<QQmlError> errorList = qmlComponent->errors();
|
||||||
Q_FOREACH (const QQmlError &error, errorList) {
|
Q_FOREACH (const QQmlError& error, errorList) {
|
||||||
qWarning() << error.url() << error.line() << error;
|
qWarning() << error.url() << error.line() << error;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootItem = qobject_cast<QQuickItem *>(rootObject);
|
rootItem = qobject_cast<QQuickItem*>(rootObject);
|
||||||
if (!rootItem) {
|
if (!rootItem) {
|
||||||
qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject;
|
qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject;
|
||||||
delete rootObject;
|
delete rootObject;
|
||||||
|
@ -149,6 +218,8 @@ public:
|
||||||
// The root item is ready. Associate it with the window.
|
// The root item is ready. Associate it with the window.
|
||||||
rootItem->setParentItem(quickWindow->contentItem());
|
rootItem->setParentItem(quickWindow->contentItem());
|
||||||
syncRequired = true;
|
syncRequired = true;
|
||||||
|
rootItem->setWidth(quickWindow->width());
|
||||||
|
rootItem->setHeight(quickWindow->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSceneChanged()
|
void onSceneChanged()
|
||||||
|
@ -160,38 +231,9 @@ public:
|
||||||
{
|
{
|
||||||
qWarning() << Q_FUNC_INFO;
|
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(engine)
|
||||||
Q_UNUSED(scriptEngine)
|
Q_UNUSED(scriptEngine)
|
||||||
|
@ -200,70 +242,267 @@ static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngi
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickDrawable::QQuickDrawable() :
|
static QObject* fgqq_windowManager_provider(QQmlEngine* engine, QJSEngine* scriptEngine)
|
||||||
d(new QQuickDrawablePrivate)
|
{
|
||||||
|
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);
|
setUseDisplayList(false);
|
||||||
setDataVariance(Object::DYNAMIC);
|
setDataVariance(Object::DYNAMIC);
|
||||||
|
|
||||||
osg::StateSet* stateSet = getOrCreateStateSet();
|
osg::StateSet* stateSet = getOrCreateStateSet();
|
||||||
stateSet->setRenderBinDetails(1001, "RenderBin");
|
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));
|
QSurfaceFormat format;
|
||||||
|
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||||
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool doneQmlRegistration = false;
|
static bool doneQmlRegistration = false;
|
||||||
if (!doneQmlRegistration) {
|
if (!doneQmlRegistration) {
|
||||||
doneQmlRegistration = true;
|
doneQmlRegistration = true;
|
||||||
|
|
||||||
// singleton system object
|
// 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
|
// QML types
|
||||||
qmlRegisterType<FGQmlPropertyNode>("flightgear.org", 1, 0, "Property");
|
qmlRegisterType<FGQmlPropertyNode>("FlightGear", 1, 0, "Property");
|
||||||
|
//qmlRegisterType<DialogStateController>("FlightGear", 1, 0, "DialogStateController");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickDrawable::~QQuickDrawable()
|
void QQuickDrawable::setup(osgViewer::GraphicsWindow *gw, osgViewer::Viewer *viewer)
|
||||||
{
|
|
||||||
delete d->qmlEngine;
|
|
||||||
delete d->renderControl;
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
|
|
||||||
{
|
{
|
||||||
osg::GraphicsContext* gc = gw;
|
osg::GraphicsContext* gc = gw;
|
||||||
|
osg::ref_ptr<flightgear::RetriveGraphicsThreadOperation> op(new flightgear::RetriveGraphicsThreadOperation);
|
||||||
// install operations on the graphics context to get the context
|
|
||||||
osg::ref_ptr<RetriveGraphicsThreadOperation> op(new RetriveGraphicsThreadOperation);
|
|
||||||
|
|
||||||
gc->add(op);
|
gc->add(op);
|
||||||
gc->runOperations();
|
gc->runOperations();
|
||||||
|
|
||||||
// hopefully done now!
|
// hopefully done now!
|
||||||
|
|
||||||
d->qtContext = op->context();
|
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);
|
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->quickWindow->setClearBeforeRendering(false);
|
||||||
|
|
||||||
d->qmlEngine = new QQmlEngine;
|
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())
|
if (!d->qmlEngine->incubationController())
|
||||||
d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
|
d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
|
||||||
|
|
||||||
|
@ -279,23 +518,12 @@ void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
|
||||||
QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested,
|
QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested,
|
||||||
d, &QQuickDrawablePrivate::onRenderRequested);
|
d, &QQuickDrawablePrivate::onRenderRequested);
|
||||||
|
|
||||||
// insert ourselves as a filter on the GraphicsWindow, so we can intercept
|
|
||||||
// events directly and pass on to our window
|
viewer->getEventHandlers().push_front(new QuickEventHandler(d));
|
||||||
d->graphicsWindow->getGLWindow()->installEventFilter(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
|
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();
|
QOpenGLFunctions* glFuncs = d->qtContext->functions();
|
||||||
// prepare any state QQ2 needs
|
// prepare any state QQ2 needs
|
||||||
d->quickWindow->resetOpenGLState();
|
d->quickWindow->resetOpenGLState();
|
||||||
|
@ -308,12 +536,24 @@ void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
|
||||||
|
|
||||||
d->renderControl->render();
|
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)
|
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);
|
d->qmlComponent = new QQmlComponent(d->qmlEngine, url);
|
||||||
if (d->qmlComponent->isLoading()) {
|
if (d->qmlComponent->isLoading()) {
|
||||||
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
|
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
|
||||||
|
@ -326,7 +566,7 @@ void QQuickDrawable::setSource(QUrl url)
|
||||||
void QQuickDrawable::resize(int width, int height)
|
void QQuickDrawable::resize(int width, int height)
|
||||||
{
|
{
|
||||||
// we need to unscale from physical pixels back to logical, otherwise we end up double-scaled.
|
// 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 logicalWidth = static_cast<int>(width / currentPixelRatio);
|
||||||
const int logicalHeight = static_cast<int>(height / currentPixelRatio);
|
const int logicalHeight = static_cast<int>(height / currentPixelRatio);
|
||||||
|
|
||||||
|
@ -335,6 +575,7 @@ void QQuickDrawable::resize(int width, int height)
|
||||||
d->rootItem->setHeight(logicalHeight);
|
d->rootItem->setHeight(logicalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_GUI, SG_INFO, "Resize:, lw=" << logicalWidth << ", lh=" << logicalHeight);
|
||||||
d->quickWindow->setGeometry(0, 0, logicalWidth, 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
|
#ifndef FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||||
#define FG_VIEWER_QUICK_DRAWABLE_HXX
|
#define FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||||
|
|
||||||
|
@ -6,6 +24,10 @@
|
||||||
|
|
||||||
#include <osgViewer/GraphicsWindow>
|
#include <osgViewer/GraphicsWindow>
|
||||||
|
|
||||||
|
namespace osgViewer {
|
||||||
|
class Viewer;
|
||||||
|
}
|
||||||
|
|
||||||
class QQuickDrawablePrivate;
|
class QQuickDrawablePrivate;
|
||||||
|
|
||||||
class QQuickDrawable : public osg::Drawable
|
class QQuickDrawable : public osg::Drawable
|
||||||
|
@ -19,16 +41,14 @@ public:
|
||||||
|
|
||||||
void drawImplementation(osg::RenderInfo& renderInfo) const override;
|
void drawImplementation(osg::RenderInfo& renderInfo) const override;
|
||||||
|
|
||||||
/** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */
|
void setup(osgViewer::GraphicsWindow* gw, osgViewer::Viewer* viewer);
|
||||||
// virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
|
|
||||||
|
|
||||||
//virtual void accept(osg::PrimitiveFunctor& functor) const;
|
|
||||||
|
|
||||||
void setup(osgViewer::GraphicsWindow* gw);
|
|
||||||
|
|
||||||
void setSource(QUrl url);
|
void setSource(QUrl url);
|
||||||
|
|
||||||
|
void reload(QUrl url);
|
||||||
|
|
||||||
void resize(int width, int height);
|
void resize(int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QQuickDrawablePrivate* d;
|
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/Style.qml</file>
|
||||||
<file>qml/qmldir</file>
|
<file>qml/qmldir</file>
|
||||||
<file>qml/ClickableText.qml</file>
|
<file>qml/ClickableText.qml</file>
|
||||||
<file>qml/LauncherSettings.qml</file>
|
|
||||||
<file>qml/Section.qml</file>
|
<file>qml/Section.qml</file>
|
||||||
<file>qml/SettingControl.qml</file>
|
<file>qml/SettingControl.qml</file>
|
||||||
<file>qml/SettingCheckbox.qml</file>
|
<file>qml/SettingCheckbox.qml</file>
|
||||||
|
|
|
@ -149,3 +149,12 @@ if (MSVC)
|
||||||
set_property(TARGET fgfs PROPERTY
|
set_property(TARGET fgfs PROPERTY
|
||||||
VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}")
|
VS_GLOBAL_LocalDebuggerEnvironment "PATH=${_install_bin_dir};${_msvc_3rdparty_bin_dir};${_qt5_bin_dir_native}")
|
||||||
endif()
|
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)
|
if (Qt5Core_FOUND)
|
||||||
list(APPEND HEADERS GraphicsWindowQt5.hxx)
|
list(APPEND HEADERS GraphicsWindowQt5.hxx
|
||||||
list(APPEND SOURCES GraphicsWindowQt5.cpp)
|
OSGQtAdaption.hxx)
|
||||||
|
list(APPEND SOURCES GraphicsWindowQt5.cpp
|
||||||
|
OSGQtAdaption.cxx)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YES)
|
if (YES)
|
||||||
|
|
|
@ -788,6 +788,9 @@ public:
|
||||||
|
|
||||||
Qt5WindowingSystem()
|
Qt5WindowingSystem()
|
||||||
{
|
{
|
||||||
|
#if OSG_VERSION_GREATER_THAN(3,5,9)
|
||||||
|
setName("FlightGearQt5");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~Qt5WindowingSystem()
|
~Qt5WindowingSystem()
|
||||||
|
@ -899,6 +902,8 @@ void initQtWindowingSystem()
|
||||||
{
|
{
|
||||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||||
osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface());
|
osg::GraphicsContext::setWindowingSystemInterface(Qt5WindowingSystem::getInterface());
|
||||||
|
#else
|
||||||
|
osg::GraphicsContext::getWindowingSystemInterfaces()->addWindowingSystemInterface(Qt5WindowingSystem::getInterface());
|
||||||
#endif
|
#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/BlendFunc>
|
||||||
|
|
||||||
#include <osg/NodeVisitor>
|
#include <osg/NodeVisitor>
|
||||||
#include <osgUtil/CullVisitor>
|
|
||||||
#include <osgGA/GUIEventHandler>
|
|
||||||
#include <osgGA/GUIEventAdapter>
|
#include <osgGA/GUIEventAdapter>
|
||||||
|
#include <osgGA/GUIEventHandler>
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
#include <plib/pu.h>
|
#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");
|
setName("PUI FBO camera");
|
||||||
|
|
||||||
|
@ -363,11 +364,6 @@ void PUICamera::init(osg::Group* parent)
|
||||||
fsQuadGeode->addDrawable(_fullScreenQuad);
|
fsQuadGeode->addDrawable(_fullScreenQuad);
|
||||||
fsQuadGeode->setName("PUI fullscreen Geode");
|
fsQuadGeode->setName("PUI fullscreen Geode");
|
||||||
fsQuadGeode->setNodeMask(SG_NODEMASK_GUI_BIT);
|
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(this);
|
||||||
parent->addChild(fsQuadGeode);
|
parent->addChild(fsQuadGeode);
|
||||||
|
|
||||||
|
@ -376,6 +372,11 @@ void PUICamera::init(osg::Group* parent)
|
||||||
osg::Viewport* vport = camera->getViewport();
|
osg::Viewport* vport = camera->getViewport();
|
||||||
resizeUi(vport->width(), vport->height());
|
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
|
// remove once we require OSG 3.4
|
||||||
|
@ -400,12 +401,13 @@ void PUICamera::resizeUi(int width, int height)
|
||||||
resizeAttachments(scaledWidth, scaledHeight);
|
resizeAttachments(scaledWidth, scaledHeight);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const float puiZ = 1.0;
|
||||||
// resize the full-screen quad
|
// resize the full-screen quad
|
||||||
osg::Vec3Array* fsQuadVertices = static_cast<osg::Vec3Array*>(_fullScreenQuad->getVertexArray());
|
osg::Vec3Array* fsQuadVertices = static_cast<osg::Vec3Array*>(_fullScreenQuad->getVertexArray());
|
||||||
(*fsQuadVertices)[0] = osg::Vec3(0.0, height, 0.0);
|
(*fsQuadVertices)[0] = osg::Vec3(0.0, height, puiZ);
|
||||||
(*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, 0.0);
|
(*fsQuadVertices)[1] = osg::Vec3(0.0, 0.0, puiZ);
|
||||||
(*fsQuadVertices)[2] = osg::Vec3(width, 0.0, 0.0);
|
(*fsQuadVertices)[2] = osg::Vec3(width, 0.0, puiZ);
|
||||||
(*fsQuadVertices)[3] = osg::Vec3(width, height, 0.0);
|
(*fsQuadVertices)[3] = osg::Vec3(width, height, puiZ);
|
||||||
|
|
||||||
fsQuadVertices->dirty();
|
fsQuadVertices->dirty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,10 @@ namespace osg
|
||||||
class Geometry;
|
class Geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace osgViewer {
|
||||||
|
class Viewer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace flightgear
|
namespace flightgear
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -41,7 +45,9 @@ public:
|
||||||
|
|
||||||
// osg::Camera already defines a resize() so use this name
|
// osg::Camera already defines a resize() so use this name
|
||||||
void resizeUi(int width, int height);
|
void resizeUi(int width, int height);
|
||||||
void init(osg::Group *parent);
|
|
||||||
|
void init(osg::Group* parent, osgViewer::Viewer* viewer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if OSG_VERSION_LESS_THAN(3,4,0)
|
#if OSG_VERSION_LESS_THAN(3,4,0)
|
||||||
class UpdateViewportAndFBOAfterTextureResizeCallback;
|
class UpdateViewportAndFBOAfterTextureResizeCallback;
|
||||||
|
|
|
@ -14,14 +14,12 @@
|
||||||
// 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.
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include "config.h"
|
||||||
# include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "WindowBuilder.hxx"
|
#include "WindowBuilder.hxx"
|
||||||
|
|
||||||
#include "WindowSystemAdapter.hxx"
|
#include "WindowSystemAdapter.hxx"
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
#include <osg/Version>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -72,6 +70,14 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
|
||||||
{
|
{
|
||||||
GraphicsContext::WindowingSystemInterface* wsi
|
GraphicsContext::WindowingSystemInterface* wsi
|
||||||
= osg::GraphicsContext::getWindowingSystemInterface();
|
= 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;
|
defaultTraits = new osg::GraphicsContext::Traits;
|
||||||
auto traits = defaultTraits.get();
|
auto traits = defaultTraits.get();
|
||||||
|
|
||||||
|
@ -108,6 +114,9 @@ void WindowBuilder::makeDefaultTraits(bool stencil)
|
||||||
data->createFullscreen = wantFullscreen;
|
data->createFullscreen = wantFullscreen;
|
||||||
data->isPrimaryWindow = true;
|
data->isPrimaryWindow = true;
|
||||||
|
|
||||||
|
#if OSG_VERSION_GREATER_THAN(3, 5, 9)
|
||||||
|
traits->windowingSystemPreference = "FlightGearQt5";
|
||||||
|
#endif
|
||||||
traits->inheritedWindowData = data;
|
traits->inheritedWindowData = data;
|
||||||
traits->windowDecoration = true;
|
traits->windowDecoration = true;
|
||||||
traits->supportsResize = true;
|
traits->supportsResize = true;
|
||||||
|
|
|
@ -110,7 +110,6 @@
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT)
|
||||||
#include <GUI/QQuickDrawable.hxx>
|
#include <GUI/QQuickDrawable.hxx>
|
||||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_PUI)
|
#if defined(HAVE_PUI)
|
||||||
|
@ -609,23 +608,22 @@ FGRenderer::setupView( void )
|
||||||
|
|
||||||
#if defined(HAVE_PUI)
|
#if defined(HAVE_PUI)
|
||||||
_puiCamera = new flightgear::PUICamera;
|
_puiCamera = new flightgear::PUICamera;
|
||||||
_puiCamera->init(guiCamera);
|
_puiCamera->init(guiCamera, viewer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT)
|
||||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
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 = new QQuickDrawable;
|
||||||
_quickDrawable->setup(graphicsWindowQt);
|
_quickDrawable->setup(graphicsWindow, viewer);
|
||||||
|
|
||||||
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||||
geode->addDrawable(_quickDrawable);
|
geode->addDrawable(_quickDrawable);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Draw first (eg. before Canvas GUI)
|
guiCamera->addChild(geode);
|
||||||
guiCamera->insertChild(0, geode);
|
|
||||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,6 @@
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT)
|
||||||
#include <GUI/QQuickDrawable.hxx>
|
#include <GUI/QQuickDrawable.hxx>
|
||||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_PUI)
|
#if defined(HAVE_PUI)
|
||||||
|
@ -1504,23 +1503,22 @@ FGRenderer::setupView( void )
|
||||||
|
|
||||||
#if defined(HAVE_PUI)
|
#if defined(HAVE_PUI)
|
||||||
_puiCamera = new flightgear::PUICamera;
|
_puiCamera = new flightgear::PUICamera;
|
||||||
_puiCamera->init(guiCamera);
|
_puiCamera->init(guiCamera, viewer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_QT)
|
#if defined(HAVE_QT)
|
||||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
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 = new QQuickDrawable;
|
||||||
_quickDrawable->setup(graphicsWindowQt);
|
_quickDrawable->setup(graphicsWindow, viewer);
|
||||||
|
|
||||||
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||||
geode->addDrawable(_quickDrawable);
|
geode->addDrawable(_quickDrawable);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Draw first (eg. before Canvas GUI)
|
guiCamera->addChild(geode);
|
||||||
guiCamera->insertChild(0, geode);
|
|
||||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue