Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/flightgear into next
This commit is contained in:
commit
f13edf01fa
75 changed files with 3269 additions and 387 deletions
|
@ -318,11 +318,12 @@ endif (USE_DBUS)
|
|||
## Qt5 setup setup
|
||||
if (ENABLE_QT)
|
||||
message(STATUS "Qt launcher enabled, checking for Qt 5.4 / qmake")
|
||||
find_package(Qt5 5.4 COMPONENTS Widgets Network Qml QuickWidgets)
|
||||
find_package(Qt5 5.4 COMPONENTS Widgets Network Qml Quick QuickWidgets)
|
||||
if (Qt5Widgets_FOUND)
|
||||
message(STATUS "Will enable Qt launcher GUI")
|
||||
message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}")
|
||||
message(STATUS " Qt5Widgets include dir: ${Qt5Widgets_INCLUDE_DIRS}")
|
||||
message(STATUS " QtQuick private header: ${Qt5Quick_PRIVATE_INCLUDE_DIRS}")
|
||||
set(HAVE_QT 1)
|
||||
else()
|
||||
# don't try to build FGQCanvas if Qt wasn't found correctly
|
||||
|
|
|
@ -42,6 +42,7 @@ class CanvasMgr:
|
|||
*/
|
||||
unsigned int getCanvasTexId(const simgear::canvas::CanvasPtr& canvas) const;
|
||||
|
||||
static const char* subsystemName() { return "Canvas"; }
|
||||
protected:
|
||||
|
||||
osg::observer_ptr<osg::Camera> _gui_camera;
|
||||
|
|
|
@ -66,9 +66,6 @@ if (APPLE)
|
|||
CocoaHelpers.mm)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
if (HAVE_QT)
|
||||
qt5_wrap_ui(uic_sources Launcher.ui
|
||||
EditRatingsFilterDialog.ui
|
||||
|
@ -159,10 +156,24 @@ if (HAVE_QT)
|
|||
|
||||
set_property(TARGET fglauncher PROPERTY AUTOMOC ON)
|
||||
target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network Qt5::Qml Qt5::Quick Qt5::QuickWidgets SimGearCore)
|
||||
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
|
||||
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI
|
||||
${Qt5Quick_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
# needed for scrollbar helper, unfortunately
|
||||
target_include_directories(fglauncher PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS})
|
||||
add_library(fgqmlui QQuickDrawable.cxx
|
||||
QQuickDrawable.hxx
|
||||
QtQuickFGCanvasItem.cxx
|
||||
QtQuickFGCanvasItem.hxx
|
||||
PropertyItemModel.cxx
|
||||
PropertyItemModel.hxx
|
||||
FGQmlInstance.cxx
|
||||
FGQmlInstance.hxx
|
||||
FGQmlPropertyNode.cxx
|
||||
FGQmlPropertyNode.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
target_link_libraries(fgqmlui Qt5::Quick Qt5::Network Qt5::Qml SimGearCore)
|
||||
target_include_directories(fgqmlui PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <osg/GLExtensions>
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
|
@ -35,7 +37,7 @@ CanvasWidget::CanvasWidget( int x, int y,
|
|||
SGPropertyNode* props,
|
||||
const std::string& module ):
|
||||
puObject(x, y, width, height),
|
||||
_canvas_mgr( dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas")) ),
|
||||
_canvas_mgr(globals->get_subsystem<CanvasMgr>()),
|
||||
_last_x(0),
|
||||
_last_y(0),
|
||||
// automatically resize viewport of canvas if no size is given
|
||||
|
@ -196,14 +198,19 @@ void CanvasWidget::setSize(int w, int h)
|
|||
//------------------------------------------------------------------------------
|
||||
void CanvasWidget::draw(int dx, int dy)
|
||||
{
|
||||
osg::GLExtensions* extensions = osg::GLExtensions::Get(0, true);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
extensions->glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _canvas_mgr->getCanvasTexId(_canvas));
|
||||
glBegin( GL_QUADS );
|
||||
glColor3f(1,1,1);
|
||||
glColor4f(1,1,1, 1.0f);
|
||||
glTexCoord2f(0,0); glVertex2f(dx + abox.min[0], dy + abox.min[1]);
|
||||
glTexCoord2f(1,0); glVertex2f(dx + abox.max[0], dy + abox.min[1]);
|
||||
glTexCoord2f(1,1); glVertex2f(dx + abox.max[0], dy + abox.max[1]);
|
||||
glTexCoord2f(0,1); glVertex2f(dx + abox.min[0], dy + abox.max[1]);
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
|
|
@ -796,6 +796,9 @@ FGPUIDialog::display (SGPropertyNode *props)
|
|||
|
||||
SGPropertyNode *fontnode = props->getNode("font");
|
||||
if (fontnode) {
|
||||
SGPropertyNode *property_node = fontnode->getChild("property");
|
||||
if (property_node)
|
||||
fontnode = globals->get_props()->getNode(property_node->getStringValue());
|
||||
_font = FGFontCache::instance()->get(fontnode);
|
||||
} else {
|
||||
_font = _gui->getDefaultFont();
|
||||
|
@ -1061,6 +1064,7 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh
|
|||
|
||||
setupObject(obj, props);
|
||||
_activeWidgets.push_back(obj);
|
||||
setColor(obj, props, FOREGROUND | LABEL);
|
||||
return obj;
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -1091,9 +1095,18 @@ FGPUIDialog::setupObject (puObject *object, SGPropertyNode *props)
|
|||
object->setBorderThickness( props->getIntValue("border", 2) );
|
||||
|
||||
if (SGPropertyNode *nft = props->getNode("font", false)) {
|
||||
puFont *lfnt = FGFontCache::instance()->get(nft);
|
||||
object->setLabelFont(*lfnt);
|
||||
object->setLegendFont(*lfnt);
|
||||
if (nft) {
|
||||
SGPropertyNode *property_node = nft->getChild("property");
|
||||
if (property_node)
|
||||
nft = globals->get_props()->getNode(property_node->getStringValue());
|
||||
_font = FGFontCache::instance()->get(nft);
|
||||
}
|
||||
else {
|
||||
_font = _gui->getDefaultFont();
|
||||
}
|
||||
puFont *lfnt = FGFontCache::instance()->get(nft);
|
||||
object->setLabelFont(*lfnt);
|
||||
object->setLegendFont(*lfnt);
|
||||
} else {
|
||||
object->setLabelFont(*_font);
|
||||
}
|
||||
|
|
106
src/GUI/FGQmlInstance.cxx
Normal file
106
src/GUI/FGQmlInstance.cxx
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "FGQmlInstance.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJSValueIterator>
|
||||
|
||||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
namespace {
|
||||
|
||||
void convertJSObjectToPropertyNode(QJSValue js, SGPropertyNode *node);
|
||||
|
||||
void convertSingleJSValue(QJSValue v, SGPropertyNode* node)
|
||||
{
|
||||
if (v.isNumber()) {
|
||||
node->setDoubleValue(v.toNumber());
|
||||
} else if (v.isBool()) {
|
||||
node->setBoolValue(v.toBool());
|
||||
} else if (v.isString()) {
|
||||
node->setStringValue(v.toString().toStdString());
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "unhandld JS type";
|
||||
}
|
||||
}
|
||||
|
||||
void convertJSObjectToPropertyNode(QJSValue js, SGPropertyNode* node)
|
||||
{
|
||||
QJSValueIterator it(js);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
const auto propName = it.name().toStdString();
|
||||
QJSValue v = it.value();
|
||||
if (v.isObject()) {
|
||||
// recursion is fun :)
|
||||
SGPropertyNode_ptr childNode = node->getChild(propName, true);
|
||||
convertJSObjectToPropertyNode(v, childNode);
|
||||
} else if (v.isArray()) {
|
||||
// mapping arbitrary JS arrays is slightly uncomfortable, but
|
||||
// this makes the common case work:
|
||||
// foobar: [1,4, "apples", false]
|
||||
// becomes foobar[0] = 1; foobar[1] = 4; foobar[2] = "apples";
|
||||
// foobar[3] = false;
|
||||
// not perfect but useful enough to be worth supporting.
|
||||
QJSValueIterator arrayIt(v);
|
||||
int propIndex = 0;
|
||||
while (arrayIt.hasNext()) {
|
||||
arrayIt.next();
|
||||
SGPropertyNode_ptr childNode = node->getChild(propName, propIndex++, true);
|
||||
if (arrayIt.value().isArray()) {
|
||||
// there is no sensible mapping of this to SGPropertyNode
|
||||
qWarning() << Q_FUNC_INFO <<"arrays of array not possible";
|
||||
} else if (arrayIt.value().isObject()) {
|
||||
convertJSObjectToPropertyNode(arrayIt.value(), childNode);
|
||||
} else {
|
||||
// simple case, just convert the value in place
|
||||
convertSingleJSValue(arrayIt.value(), childNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
convertSingleJSValue(v, node->getChild(propName, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // of anonymous namespace
|
||||
|
||||
FGQmlInstance::FGQmlInstance(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FGQmlInstance::command(QString name, QJSValue args)
|
||||
{
|
||||
const std::string cmdName = name.toStdString();
|
||||
|
||||
SGCommandMgr* mgr = SGCommandMgr::instance();
|
||||
if (!mgr->getCommand(cmdName)) {
|
||||
qWarning() << "No such command" << name;
|
||||
return false;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr propArgs(new SGPropertyNode);
|
||||
|
||||
// convert JSValue args into a property tree.
|
||||
if (args.isObject()) {
|
||||
convertJSObjectToPropertyNode(args, propArgs);
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
return mgr->execute(cmdName, propArgs, globals->get_props());
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *FGQmlInstance::property(QString path, bool create) const
|
||||
{
|
||||
SGPropertyNode_ptr node = fgGetNode(path.toStdString(), create);
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
FGQmlPropertyNode* result = new FGQmlPropertyNode;
|
||||
result->setNode(node);
|
||||
return result;
|
||||
}
|
28
src/GUI/FGQmlInstance.hxx
Normal file
28
src/GUI/FGQmlInstance.hxx
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef FGQMLINSTANCE_HXX
|
||||
#define FGQMLINSTANCE_HXX
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QJSValue>
|
||||
|
||||
// forward decls
|
||||
class FGQmlPropertyNode;
|
||||
|
||||
class FGQmlInstance : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FGQmlInstance(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE bool command(QString name, QJSValue args);
|
||||
|
||||
/**
|
||||
* retrieve a property by its global path
|
||||
*/
|
||||
Q_INVOKABLE FGQmlPropertyNode* property(QString path, bool create = false) const;
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // FGQMLINSTANCE_HXX
|
143
src/GUI/FGQmlPropertyNode.cxx
Normal file
143
src/GUI/FGQmlPropertyNode.cxx
Normal file
|
@ -0,0 +1,143 @@
|
|||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
#include <QDebug>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGQmlPropertyNode::FGQmlPropertyNode(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FGQmlPropertyNode::set(QVariant newValue)
|
||||
{
|
||||
if (!_prop || !_prop->getAttribute(SGPropertyNode::WRITE))
|
||||
return false;
|
||||
|
||||
// we still need to short circuit setting
|
||||
if (newValue == value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_prop->getType()) {
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
_prop->setIntValue(newValue.toInt());
|
||||
case simgear::props::BOOL: _prop->setBoolValue(newValue.toBool());
|
||||
case simgear::props::DOUBLE: _prop->setDoubleValue(newValue.toDouble());
|
||||
case simgear::props::FLOAT: _prop->setFloatValue(newValue.toFloat());
|
||||
case simgear::props::STRING: _prop->setStringValue(newValue.toString().toStdString());
|
||||
|
||||
case simgear::props::VEC3D: {
|
||||
QVector3D v = newValue.value<QVector3D>();
|
||||
_prop->setValue(SGVec3d(v.x(), v.y(), v.z()));
|
||||
}
|
||||
|
||||
case simgear::props::VEC4D: {
|
||||
QVector4D v = newValue.value<QVector4D>();
|
||||
_prop->setValue(SGVec4d(v.x(), v.y(), v.z(), v.w()));
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning() << Q_FUNC_INFO << "handle untyped property writes";
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant FGQmlPropertyNode::value() const
|
||||
{
|
||||
if (!_prop)
|
||||
return {};
|
||||
|
||||
switch (_prop->getType()) {
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
return _prop->getIntValue();
|
||||
case simgear::props::BOOL: return _prop->getBoolValue();
|
||||
case simgear::props::DOUBLE: return _prop->getDoubleValue();
|
||||
case simgear::props::FLOAT: return _prop->getFloatValue();
|
||||
case simgear::props::STRING: return QString::fromStdString(_prop->getStringValue());
|
||||
|
||||
case simgear::props::VEC3D: {
|
||||
const SGVec3d v3 = _prop->getValue<SGVec3d>();
|
||||
return QVariant::fromValue(QVector3D(v3.x(), v3.y(), v3.z()));
|
||||
}
|
||||
|
||||
case simgear::props::VEC4D: {
|
||||
const SGVec4d v4 = _prop->getValue<SGVec4d>();
|
||||
return QVariant::fromValue(QVector4D(v4.x(), v4.y(), v4.z(), v4.w()));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {}; // null qvariant
|
||||
}
|
||||
|
||||
QString FGQmlPropertyNode::path() const
|
||||
{
|
||||
if (!_prop)
|
||||
return QString();
|
||||
|
||||
return QString::fromStdString(_prop->getPath());
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *FGQmlPropertyNode::parentProp() const
|
||||
{
|
||||
if (!_prop || !_prop->getParent())
|
||||
return nullptr;
|
||||
|
||||
auto pp = new FGQmlPropertyNode;
|
||||
pp->setNode(_prop->getParent());
|
||||
return pp;
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::setNode(SGPropertyNode_ptr node)
|
||||
{
|
||||
if (node == _prop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_prop) {
|
||||
_prop->removeChangeListener(this);
|
||||
}
|
||||
|
||||
_prop = node;
|
||||
if (_prop) {
|
||||
_prop->addChangeListener(this, false);
|
||||
}
|
||||
|
||||
emit parentPropChanged(parentProp());
|
||||
emit pathChanged(path());
|
||||
emit valueChangedNotify(value());
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr FGQmlPropertyNode::node() const
|
||||
{
|
||||
return _prop;
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
if (node != _prop) {
|
||||
return;
|
||||
}
|
||||
emit valueChangedNotify(value());
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::setPath(QString path)
|
||||
{
|
||||
SGPropertyNode_ptr node = fgGetNode(path.toStdString(), false /* don't create */);
|
||||
setNode(node);
|
||||
}
|
||||
|
54
src/GUI/FGQmlPropertyNode.hxx
Normal file
54
src/GUI/FGQmlPropertyNode.hxx
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef FGQMLPROPERTYNODE_HXX
|
||||
#define FGQMLPROPERTYNODE_HXX
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class FGQmlPropertyNode : public QObject,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FGQmlPropertyNode(QObject *parent = nullptr);
|
||||
|
||||
Q_PROPERTY(QVariant value READ value NOTIFY valueChangedNotify)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(FGQmlPropertyNode* parentProp READ parentProp NOTIFY parentPropChanged)
|
||||
|
||||
Q_INVOKABLE bool set(QVariant newValue);
|
||||
|
||||
// children accessor
|
||||
QVariant value() const;
|
||||
|
||||
QString path() const;
|
||||
|
||||
FGQmlPropertyNode* parentProp() const;
|
||||
|
||||
// C++ API, not accessible from QML
|
||||
void setNode(SGPropertyNode_ptr node);
|
||||
|
||||
SGPropertyNode_ptr node() const;
|
||||
protected:
|
||||
// SGPropertyChangeListener API
|
||||
|
||||
void valueChanged(SGPropertyNode * node) override;
|
||||
|
||||
signals:
|
||||
|
||||
void valueChangedNotify(QVariant value);
|
||||
|
||||
void pathChanged(QString path);
|
||||
|
||||
void parentPropChanged(FGQmlPropertyNode* parentProp);
|
||||
|
||||
public slots:
|
||||
|
||||
void setPath(QString path);
|
||||
|
||||
private:
|
||||
SGPropertyNode_ptr _prop;
|
||||
};
|
||||
|
||||
#endif // FGQMLPROPERTYNODE_HXX
|
|
@ -988,7 +988,7 @@ void MapWidget::drawLatLonGrid()
|
|||
int ix = 0;
|
||||
int iy = 0;
|
||||
|
||||
glColor4f(0.8, 0.8, 0.8, 0.4);
|
||||
glColor4f(0.8, 0.8, 0.8, 1.0);
|
||||
glBegin(GL_LINES);
|
||||
bool didDraw;
|
||||
do {
|
||||
|
|
257
src/GUI/PropertyItemModel.cxx
Normal file
257
src/GUI/PropertyItemModel.cxx
Normal file
|
@ -0,0 +1,257 @@
|
|||
#include "PropertyItemModel.hxx"
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
class PropertyItemModelRoleMapping : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
|
||||
public:
|
||||
|
||||
QString path() const
|
||||
{
|
||||
return QString::fromStdString(_propertyPath);
|
||||
}
|
||||
|
||||
QString roleName() const
|
||||
{
|
||||
return QString::fromLatin1(_roleName);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setPath(QString path)
|
||||
{
|
||||
if (_propertyPath == path.toStdString());
|
||||
return;
|
||||
|
||||
_propertyPath = path.toStdString();
|
||||
emit pathChanged(path);
|
||||
}
|
||||
|
||||
void setRoleName(QString roleName)
|
||||
{
|
||||
if (_roleName == roleName.toLatin1())
|
||||
return;
|
||||
|
||||
_roleName = roleName.toLatin1();
|
||||
emit roleNameChanged(roleName);
|
||||
}
|
||||
|
||||
signals:
|
||||
void pathChanged(QString path);
|
||||
|
||||
void roleNameChanged(QString roleName);
|
||||
|
||||
private:
|
||||
friend class PropertyItemModel;
|
||||
std::string _propertyPath;
|
||||
QByteArray _roleName;
|
||||
};
|
||||
|
||||
PropertyItemModel::PropertyItemModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int PropertyItemModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return _nodes.size();
|
||||
}
|
||||
|
||||
QVariant PropertyItemModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
if (index.row() >= _nodes.size())
|
||||
return {};
|
||||
|
||||
SGPropertyNode_ptr n = _nodes.at(index.row());
|
||||
Q_ASSERT(n);
|
||||
int mappingIndex = role - Qt::UserRole;
|
||||
if ((mappingIndex < 0) || (mappingIndex >= _roleMappings.size()))
|
||||
return {};
|
||||
|
||||
const std::string& nodePath = _roleMappings.at(mappingIndex)->_propertyPath;
|
||||
SGPropertyNode_ptr valueNode = n->getNode(nodePath);
|
||||
|
||||
// convert property value to a variant
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PropertyItemModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result;
|
||||
int count = 0;
|
||||
for (auto m : _roleMappings) {
|
||||
result[Qt::UserRole + count++] = m->_roleName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString PropertyItemModel::rootPath() const
|
||||
{
|
||||
return QString::fromStdString(_modelRoot->getPath());
|
||||
}
|
||||
|
||||
QString PropertyItemModel::childNameFilter() const
|
||||
{
|
||||
return _childNameFilter;
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *PropertyItemModel::root() const
|
||||
{
|
||||
auto n = new FGQmlPropertyNode;
|
||||
n->setNode(_modelRoot);
|
||||
return n;
|
||||
}
|
||||
|
||||
void PropertyItemModel::setRootPath(QString rootPath)
|
||||
{
|
||||
if (_modelRoot->getPath() == rootPath.toStdString())
|
||||
return;
|
||||
|
||||
innerSetRoot(fgGetNode(rootPath.toStdString()));
|
||||
}
|
||||
|
||||
void PropertyItemModel::setChildNameFilter(QString childNameFilter)
|
||||
{
|
||||
if (_childNameFilter == childNameFilter)
|
||||
return;
|
||||
|
||||
_childNameFilter = childNameFilter;
|
||||
emit childNameFilterChanged(_childNameFilter);
|
||||
|
||||
beginResetModel();
|
||||
rebuild();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PropertyItemModel::setRoot(FGQmlPropertyNode *root)
|
||||
{
|
||||
if (root->node() == _modelRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
innerSetRoot(root->node());
|
||||
}
|
||||
|
||||
void PropertyItemModel::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
// if node's parent is one of our nodes, emit data changed
|
||||
}
|
||||
|
||||
void PropertyItemModel::childAdded(SGPropertyNode *parent, SGPropertyNode *child)
|
||||
{
|
||||
if (parent == _modelRoot) {
|
||||
if (_childNameFilter.isEmpty() || (child->getNameString() == _childNameFilter.toStdString())) {
|
||||
// insert
|
||||
// need to find appropriate index based on position
|
||||
}
|
||||
}
|
||||
|
||||
// if parent is one of our nodes, emit data changed for it
|
||||
}
|
||||
|
||||
void PropertyItemModel::childRemoved(SGPropertyNode *parent, SGPropertyNode *child)
|
||||
{
|
||||
if (parent == _modelRoot) {
|
||||
// remove row
|
||||
}
|
||||
|
||||
// if parent is one of our nodes, emit data changed for it
|
||||
|
||||
}
|
||||
|
||||
QQmlListProperty<PropertyItemModelRoleMapping> PropertyItemModel::mappings()
|
||||
{
|
||||
return QQmlListProperty<PropertyItemModelRoleMapping>(this, nullptr,
|
||||
&PropertyItemModel::append_mapping,
|
||||
&PropertyItemModel::count_mapping,
|
||||
&PropertyItemModel::at_mapping,
|
||||
&PropertyItemModel::clear_mapping
|
||||
);
|
||||
}
|
||||
|
||||
void PropertyItemModel::rebuild()
|
||||
{
|
||||
if (!_modelRoot) {
|
||||
_nodes.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_childNameFilter.isEmpty()) {
|
||||
// all children
|
||||
_nodes.clear();
|
||||
const int nChildren = _modelRoot->nChildren();
|
||||
_nodes.resize(nChildren);
|
||||
for (int c=0; c < nChildren; ++c) {
|
||||
_nodes[c] = _modelRoot->getChild(c);
|
||||
}
|
||||
} else {
|
||||
_nodes = _modelRoot->getChildren(_childNameFilter.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyItemModel::innerSetRoot(SGPropertyNode_ptr root)
|
||||
{
|
||||
if (_modelRoot) {
|
||||
_modelRoot->removeChangeListener(this);;
|
||||
}
|
||||
beginResetModel();
|
||||
|
||||
_modelRoot = root;
|
||||
if (_modelRoot) {
|
||||
_modelRoot->addChangeListener(this);
|
||||
}
|
||||
|
||||
rebuild();
|
||||
emit rootChanged();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PropertyItemModel::append_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list,
|
||||
PropertyItemModelRoleMapping *mapping)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
model->_roleMappings.append(mapping);
|
||||
model->mappingsChanged();
|
||||
model->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyItemModelRoleMapping* PropertyItemModel::at_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list, int index)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
return model->_roleMappings.at(index);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PropertyItemModel::count_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
return model->_roleMappings.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PropertyItemModel::clear_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
model->_roleMappings.clear();
|
||||
model->mappingsChanged();
|
||||
model->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
#include "PropertyItemModel.moc"
|
79
src/GUI/PropertyItemModel.hxx
Normal file
79
src/GUI/PropertyItemModel.hxx
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef PROPERTYITEMMODEL_HXX
|
||||
#define PROPERTYITEMMODEL_HXX
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlListProperty>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class PropertyItemModelRoleMapping;
|
||||
class FGQmlPropertyNode;
|
||||
|
||||
class PropertyItemModel : public QAbstractListModel,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString rootPath READ rootPath WRITE setRootPath NOTIFY rootChanged)
|
||||
|
||||
Q_PROPERTY(FGQmlPropertyNode* root READ root WRITE setRoot NOTIFY rootChanged)
|
||||
|
||||
Q_PROPERTY(QString childNameFilter READ childNameFilter WRITE setChildNameFilter NOTIFY childNameFilterChanged)
|
||||
Q_PROPERTY(QQmlListProperty<PropertyItemModelRoleMapping> mappings READ mappings NOTIFY mappingsChanged)
|
||||
// list property storing child/role mapping
|
||||
|
||||
QQmlListProperty<PropertyItemModelRoleMapping> mappings();
|
||||
public:
|
||||
PropertyItemModel();
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
QString rootPath() const;
|
||||
|
||||
QString childNameFilter() const;
|
||||
|
||||
FGQmlPropertyNode* root() const;
|
||||
|
||||
public slots:
|
||||
void setRootPath(QString rootPath);
|
||||
|
||||
void setChildNameFilter(QString childNameFilter);
|
||||
|
||||
void setRoot(FGQmlPropertyNode* root);
|
||||
|
||||
signals:
|
||||
void rootChanged();
|
||||
|
||||
void childNameFilterChanged(QString childNameFilter);
|
||||
|
||||
void mappingsChanged();
|
||||
protected:
|
||||
// implement property-change listener to watch for changes to the
|
||||
// underlying nodes
|
||||
void valueChanged(SGPropertyNode *node) override;
|
||||
void childAdded(SGPropertyNode *parent, SGPropertyNode *child) override;
|
||||
void childRemoved(SGPropertyNode *parent, SGPropertyNode *child) override;
|
||||
|
||||
private:
|
||||
void rebuild();
|
||||
|
||||
void innerSetRoot(SGPropertyNode_ptr root);
|
||||
|
||||
static void append_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list,
|
||||
PropertyItemModelRoleMapping *mapping);
|
||||
static PropertyItemModelRoleMapping* at_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list, int index);
|
||||
static int count_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list);
|
||||
static void clear_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list);
|
||||
|
||||
SGPropertyNode_ptr _modelRoot;
|
||||
QVector<PropertyItemModelRoleMapping*> _roleMappings;
|
||||
QString _childNameFilter;
|
||||
|
||||
mutable std::vector<SGPropertyNode_ptr> _nodes;
|
||||
};
|
||||
|
||||
#endif // PROPERTYITEMMODEL_HXX
|
341
src/GUI/QQuickDrawable.cxx
Normal file
341
src/GUI/QQuickDrawable.cxx
Normal file
|
@ -0,0 +1,341 @@
|
|||
|
||||
|
||||
#include "QQuickDrawable.hxx"
|
||||
|
||||
#include <QQuickRenderControl>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include <QThread>
|
||||
#include <QOpenGLContext>
|
||||
#include <QQuickWindow>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QQuickItem>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <osg/GraphicsContext>
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
#include <GUI/FGQmlInstance.hxx>
|
||||
#include <GUI/FGQmlPropertyNode.hxx>
|
||||
|
||||
#include <Viewer/GraphicsWindowQt5.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 0
|
||||
class SyncPolishOperation : public osg::Operation
|
||||
{
|
||||
public:
|
||||
SyncPolishOperation(QQuickRenderControl* rc) :
|
||||
renderControl(rc)
|
||||
{
|
||||
setKeep(true);
|
||||
}
|
||||
|
||||
virtual void operator () (osg::Object*) override
|
||||
{
|
||||
if (syncRequired) {
|
||||
//syncRequired = false;
|
||||
|
||||
}
|
||||
|
||||
renderControl->polishItems();
|
||||
}
|
||||
|
||||
bool syncRequired = true;
|
||||
QQuickRenderControl* renderControl;
|
||||
};
|
||||
#endif
|
||||
|
||||
class CustomRenderControl : public QQuickRenderControl
|
||||
{
|
||||
public:
|
||||
CustomRenderControl(QWindow* win)
|
||||
: _qWindow(win)
|
||||
{
|
||||
Q_ASSERT(win);
|
||||
}
|
||||
|
||||
QWindow* renderWindow(QPoint* offset) override
|
||||
{
|
||||
if (offset) {
|
||||
*offset = QPoint(0,0);
|
||||
}
|
||||
return _qWindow;
|
||||
}
|
||||
private:
|
||||
QWindow* _qWindow = nullptr;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
QOpenGLContext* qtContext = nullptr;
|
||||
|
||||
public slots:
|
||||
void onComponentLoaded()
|
||||
{
|
||||
if (qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = qmlComponent->errors();
|
||||
Q_FOREACH (const QQmlError &error, errorList) {
|
||||
qWarning() << error.url() << error.line() << error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QObject *rootObject = qmlComponent->create();
|
||||
if (qmlComponent->isError()) {
|
||||
QList<QQmlError> errorList = qmlComponent->errors();
|
||||
Q_FOREACH (const QQmlError &error, errorList) {
|
||||
qWarning() << error.url() << error.line() << error;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Q_UNUSED(engine)
|
||||
Q_UNUSED(scriptEngine)
|
||||
|
||||
FGQmlInstance* instance = new FGQmlInstance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
static bool doneQmlRegistration = false;
|
||||
if (!doneQmlRegistration) {
|
||||
doneQmlRegistration = true;
|
||||
|
||||
// singleton system object
|
||||
qmlRegisterSingletonType<FGQmlInstance>("flightgear.org", 1, 0, "System", fgqmlinstance_provider);
|
||||
|
||||
// QML types
|
||||
qmlRegisterType<FGQmlPropertyNode>("flightgear.org", 1, 0, "Property");
|
||||
}
|
||||
}
|
||||
|
||||
QQuickDrawable::~QQuickDrawable()
|
||||
{
|
||||
delete d->qmlEngine;
|
||||
delete d->renderControl;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
|
||||
{
|
||||
osg::GraphicsContext* gc = gw;
|
||||
|
||||
// install operations on the graphics context to get the context
|
||||
osg::ref_ptr<RetriveGraphicsThreadOperation> op(new 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->quickWindow = new QQuickWindow(d->renderControl);
|
||||
d->quickWindow->setClearBeforeRendering(false);
|
||||
|
||||
d->qmlEngine = new QQmlEngine;
|
||||
if (!d->qmlEngine->incubationController())
|
||||
d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
|
||||
|
||||
d->renderControl->initialize(d->qtContext);
|
||||
|
||||
#if QT_VERSION < 0x050600
|
||||
SG_LOG(SG_GUI, SG_ALERT, "Qt < 5.6 was used to build FlightGear, multi-threading of QtQuick is not safe");
|
||||
#else
|
||||
d->renderControl->prepareThread(op->thread());
|
||||
#endif
|
||||
QObject::connect(d->renderControl, &QQuickRenderControl::sceneChanged,
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
void QQuickDrawable::setSource(QUrl url)
|
||||
{
|
||||
d->qmlComponent = new QQmlComponent(d->qmlEngine, url);
|
||||
if (d->qmlComponent->isLoading()) {
|
||||
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
|
||||
d, &QQuickDrawablePrivate::onComponentLoaded);
|
||||
} else {
|
||||
d->onComponentLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight);
|
||||
}
|
||||
|
||||
#include "QQuickDrawable.moc"
|
36
src/GUI/QQuickDrawable.hxx
Normal file
36
src/GUI/QQuickDrawable.hxx
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||
#define FG_VIEWER_QUICK_DRAWABLE_HXX
|
||||
|
||||
#include <osg/Drawable>
|
||||
#include <QUrl>
|
||||
|
||||
#include <osgViewer/GraphicsWindow>
|
||||
|
||||
class QQuickDrawablePrivate;
|
||||
|
||||
class QQuickDrawable : public osg::Drawable
|
||||
{
|
||||
public:
|
||||
QQuickDrawable();
|
||||
virtual ~QQuickDrawable();
|
||||
|
||||
virtual osg::Object* cloneType() const { return 0; }
|
||||
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return 0; }
|
||||
|
||||
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 setSource(QUrl url);
|
||||
|
||||
void resize(int width, int height);
|
||||
private:
|
||||
QQuickDrawablePrivate* d;
|
||||
};
|
||||
|
||||
#endif
|
258
src/GUI/QtQuickFGCanvasItem.cxx
Normal file
258
src/GUI/QtQuickFGCanvasItem.cxx
Normal file
|
@ -0,0 +1,258 @@
|
|||
//
|
||||
// QtQuickFGCanvasItem.cxx
|
||||
//
|
||||
// Copyright (C) 2017 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "QtQuickFGCanvasItem.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSGSimpleTextureNode>
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
namespace {
|
||||
|
||||
class CanvasTexture : public QSGDynamicTexture
|
||||
{
|
||||
public:
|
||||
CanvasTexture(sc::CanvasPtr canvas)
|
||||
{
|
||||
_manager = globals->get_subsystem<CanvasMgr>();
|
||||
setHorizontalWrapMode(QSGTexture::ClampToEdge);
|
||||
setVerticalWrapMode(QSGTexture::ClampToEdge);
|
||||
}
|
||||
|
||||
bool updateTexture() override
|
||||
{
|
||||
// no-op, canvas is updated by OSG before QtQuick rendering occurs
|
||||
// this would change if we wanted QtQuick to drive Canvas
|
||||
// updating, which we don't
|
||||
// but do return true so QQ knows to repaint / re-cache
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasMipmaps() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasAlphaChannel() const override
|
||||
{
|
||||
return false; // for now, can Canvas have an alpha channel?
|
||||
}
|
||||
|
||||
void bind() override
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, _manager->getCanvasTexId(_canvas));
|
||||
}
|
||||
|
||||
int textureId() const override
|
||||
{
|
||||
return _manager->getCanvasTexId(_canvas);
|
||||
}
|
||||
|
||||
QSize textureSize() const override
|
||||
{
|
||||
SGPropertyNode* cprops = _canvas->getProps();
|
||||
return QSize(cprops->getIntValue("value[0]"),
|
||||
cprops->getIntValue("value[1]"));
|
||||
}
|
||||
private:
|
||||
CanvasMgr* _manager;
|
||||
sc::CanvasPtr _canvas;
|
||||
|
||||
};
|
||||
|
||||
} // of anonymous namespace
|
||||
|
||||
static int convertQtButtonToCanvas(Qt::MouseButton button)
|
||||
{
|
||||
switch (button) {
|
||||
case Qt::LeftButton: return 0;
|
||||
case Qt::MiddleButton: return 1;
|
||||
case Qt::RightButton: return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "unmapped Qt mouse button" << button;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QtQuickFGCanvasItem::QtQuickFGCanvasItem(QQuickItem *parent)
|
||||
{
|
||||
}
|
||||
|
||||
QtQuickFGCanvasItem::~QtQuickFGCanvasItem()
|
||||
{
|
||||
if ( _canvas ) {
|
||||
_canvas->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
QSGNode *QtQuickFGCanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
|
||||
{
|
||||
QSGSimpleTextureNode* texNode = static_cast<QSGSimpleTextureNode*>(oldNode);
|
||||
if (!texNode) {
|
||||
texNode = new QSGSimpleTextureNode;
|
||||
|
||||
CanvasTexture* texture = new CanvasTexture(_canvas);
|
||||
texNode->setTexture(texture);
|
||||
}
|
||||
|
||||
// or should this simply be the geometry?
|
||||
texNode->setRect(QRectF(0.0, 0.0, width(), height()));
|
||||
|
||||
return texNode;
|
||||
}
|
||||
|
||||
sc::MouseEventPtr QtQuickFGCanvasItem::convertMouseEvent(QMouseEvent* event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
|
||||
canvasEvent->time = event->timestamp() / 1000.0;
|
||||
canvasEvent->screen_pos.set(event->screenPos().x(),
|
||||
event->screenPos().y());
|
||||
canvasEvent->client_pos.set(event->pos().x(),
|
||||
event->pos().y());
|
||||
|
||||
// synthesise delta values
|
||||
QPointF delta = event->screenPos() - _lastMouseEventScreenPosition;
|
||||
canvasEvent->delta.set(delta.x(), delta.y());
|
||||
_lastMouseEventScreenPosition = event->screenPos();
|
||||
|
||||
return canvasEvent;
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
|
||||
canvasEvent->button = convertQtButtonToCanvas(event->button());
|
||||
canvasEvent->type = sc::Event::MOUSE_DOWN;
|
||||
_canvas->handleMouseEvent(canvasEvent);
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
|
||||
canvasEvent->button = convertQtButtonToCanvas(event->button());
|
||||
canvasEvent->type = sc::Event::MOUSE_UP;
|
||||
_canvas->handleMouseEvent(canvasEvent);
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
canvasEvent->type = sc::Event::MOUSE_MOVE;
|
||||
} else {
|
||||
canvasEvent->button = convertQtButtonToCanvas(event->button());
|
||||
canvasEvent->type = sc::Event::DRAG;
|
||||
}
|
||||
|
||||
_canvas->handleMouseEvent(canvasEvent);
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
|
||||
canvasEvent->button = convertQtButtonToCanvas(event->button());
|
||||
canvasEvent->type = sc::Event::DBL_CLICK;
|
||||
_canvas->handleMouseEvent(canvasEvent);
|
||||
}
|
||||
|
||||
|
||||
void QtQuickFGCanvasItem::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
|
||||
canvasEvent->time = event->timestamp() / 1000.0;
|
||||
canvasEvent->type = sc::Event::WHEEL;
|
||||
|
||||
// TODO - check if using pixelDelta is beneficial at all.
|
||||
canvasEvent->delta.set(event->angleDelta().x(),
|
||||
event->angleDelta().y());
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||
{
|
||||
QQuickItem::geometryChanged(newGeometry, oldGeometry);
|
||||
updateCanvasGeometry();
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::setCanvas(QString canvas)
|
||||
{
|
||||
if (_canvasName == canvas)
|
||||
return;
|
||||
|
||||
if (_canvas) {
|
||||
// release it
|
||||
_canvas->destroy();
|
||||
_canvas.clear();
|
||||
}
|
||||
_canvasName = canvas;
|
||||
|
||||
if (!_canvasName.isEmpty()) {
|
||||
CanvasMgr* canvasManager = globals->get_subsystem<CanvasMgr>();
|
||||
_canvas = canvasManager->createCanvas("");
|
||||
|
||||
SGPropertyNode* cprops = _canvas->getProps();
|
||||
cprops->setBoolValue("render-always", true);
|
||||
|
||||
initCanvasNasalModules();
|
||||
updateCanvasGeometry();
|
||||
}
|
||||
|
||||
emit canvasChanged(_canvasName);
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::initCanvasNasalModules()
|
||||
{
|
||||
#if 0
|
||||
SGPropertyNode *nasal = props->getNode("nasal");
|
||||
if( !nasal )
|
||||
return;
|
||||
|
||||
FGNasalSys *nas = globals->get_subsystem<FGNasalSys>();
|
||||
if( !nas )
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_ALERT,
|
||||
"CanvasWidget: Failed to get nasal subsystem!" );
|
||||
|
||||
const std::string file = std::string("__canvas:")
|
||||
+ cprops->getStringValue("name");
|
||||
|
||||
SGPropertyNode *load = nasal->getNode("load");
|
||||
if( load )
|
||||
{
|
||||
const char *s = load->getStringValue();
|
||||
nas->handleCommand(module.c_str(), file.c_str(), s, cprops);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void QtQuickFGCanvasItem::updateCanvasGeometry()
|
||||
{
|
||||
SGPropertyNode* cprops = _canvas->getProps();
|
||||
cprops->setIntValue("size[0]", width());
|
||||
cprops->setIntValue("size[1]", height());
|
||||
}
|
79
src/GUI/QtQuickFGCanvasItem.hxx
Normal file
79
src/GUI/QtQuickFGCanvasItem.hxx
Normal file
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// QtQuickFGCanvasItem.hxx
|
||||
//
|
||||
// Copyright (C) 2017 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef QtQuickFGCanvasItem_hpp
|
||||
#define QtQuickFGCanvasItem_hpp
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
#include <simgear/canvas/canvas_fwd.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <Canvas/canvas_mgr.hxx>
|
||||
|
||||
/**
|
||||
* Custom QtQuick item for displaying a FlightGear canvas (and interacting with it)
|
||||
*
|
||||
* Note this unrelated to the QtQuick 'native' canvas (HTML5 canvas 2D implementation)!
|
||||
*/
|
||||
class QtQuickFGCanvasItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString canvas READ canvas WRITE setCanvas NOTIFY canvasChanged)
|
||||
public:
|
||||
QtQuickFGCanvasItem(QQuickItem* parent = nullptr);
|
||||
virtual ~QtQuickFGCanvasItem();
|
||||
|
||||
QString canvas() const
|
||||
{
|
||||
return _canvasName;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setCanvas(QString canvas);
|
||||
|
||||
signals:
|
||||
void canvasChanged(QString canvas);
|
||||
|
||||
protected:
|
||||
|
||||
QSGNode* updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
|
||||
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||
|
||||
private:
|
||||
void initCanvasNasalModules();
|
||||
void updateCanvasGeometry();
|
||||
|
||||
simgear::canvas::MouseEventPtr convertMouseEvent(QMouseEvent *event);
|
||||
|
||||
simgear::canvas::CanvasPtr _canvas;
|
||||
QString _canvasName;
|
||||
QPointF _lastMouseEventScreenPosition;
|
||||
};
|
||||
|
||||
#endif /* QtQuickFGCanvasItem_hpp */
|
|
@ -48,7 +48,8 @@ public:
|
|||
ThumbnailImageItem* owner;
|
||||
};
|
||||
|
||||
ThumbnailImageItem::ThumbnailImageItem() :
|
||||
ThumbnailImageItem::ThumbnailImageItem(QQuickItem* parent) :
|
||||
QQuickItem(parent),
|
||||
m_delegate(new ThumbnailPackageDelegate(this)),
|
||||
m_maximumSize(9999, 9999)
|
||||
{
|
||||
|
@ -66,18 +67,20 @@ ThumbnailImageItem::~ThumbnailImageItem()
|
|||
QSGNode *ThumbnailImageItem::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *)
|
||||
{
|
||||
if (m_image.isNull()) {
|
||||
delete oldNode;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSGSimpleTextureNode* textureNode = static_cast<QSGSimpleTextureNode*>(oldNode);
|
||||
if (!textureNode) {
|
||||
textureNode = new QSGSimpleTextureNode;
|
||||
textureNode->setOwnsTexture(true);
|
||||
}
|
||||
if (m_imageDirty || !textureNode) {
|
||||
if (!textureNode) {
|
||||
textureNode = new QSGSimpleTextureNode;
|
||||
textureNode->setOwnsTexture(true);
|
||||
}
|
||||
|
||||
if (m_imageDirty && !m_image.isNull()) {
|
||||
QSGTexture* tex = window()->createTextureFromImage(m_image);
|
||||
textureNode->setTexture(tex);
|
||||
textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial);
|
||||
m_imageDirty = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class ThumbnailImageItem : public QQuickItem
|
|||
|
||||
Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged)
|
||||
public:
|
||||
ThumbnailImageItem();
|
||||
ThumbnailImageItem(QQuickItem* parent = nullptr);
|
||||
~ThumbnailImageItem();
|
||||
|
||||
QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include <Viewer/CameraGroup.hxx>
|
||||
#include <GUI/new_gui.hxx>
|
||||
#include <GUI/FGFontCache.hxx>
|
||||
#include <Viewer/PUICamera.hxx>
|
||||
|
||||
#include "gui.h"
|
||||
#include "layout.hxx"
|
||||
|
@ -74,8 +75,8 @@ public:
|
|||
}
|
||||
void run(osg::GraphicsContext* gc)
|
||||
{
|
||||
WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA();
|
||||
wsa->puInitialize();
|
||||
flightgear::PUICamera::initPUI();
|
||||
|
||||
puSetDefaultStyle ( PUSTYLE_SMALL_SHADED ); //PUSTYLE_DEFAULT
|
||||
puSetDefaultColourScheme (0.8, 0.8, 0.9, 1);
|
||||
|
||||
|
|
|
@ -413,6 +413,13 @@ NewGUI::setStyle (void)
|
|||
SGPropertyNode *n = sim->getChild("style", which);
|
||||
if (!n)
|
||||
n = sim->getChild("style", 0, true);
|
||||
|
||||
SGPropertyNode *selected_style = globals->get_props()->getNode("sim/gui/selected-style", true);
|
||||
|
||||
n->copy(selected_style);
|
||||
|
||||
//if (selected_style && n)
|
||||
// n->alias(selected_style);
|
||||
|
||||
setupFont(n->getNode("fonts/gui", true));
|
||||
n = n->getNode("colors", true);
|
||||
|
|
|
@ -54,3 +54,5 @@
|
|||
#cmakedefine HAVE_QT
|
||||
|
||||
#define FG_BUILD_TYPE "@FG_BUILD_TYPE@"
|
||||
|
||||
#define HAVE_PUI
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "FGKeyboardInput.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <plib/pu.h>
|
||||
|
||||
using simgear::PropertyList;
|
||||
|
||||
|
@ -248,10 +247,6 @@ void FGKeyboardInput::doKey (int k, int modifiers, int x, int y)
|
|||
|
||||
void FGKeyboardInput::keyHandler(int key, int keymod, int mousex, int mousey)
|
||||
{
|
||||
if((keymod & KEYMOD_RELEASED) == 0)
|
||||
if(puKeyboard(key, PU_DOWN))
|
||||
return;
|
||||
|
||||
if(keyboardInput)
|
||||
keyboardInput->doKey(key, keymod, mousex, mousey);
|
||||
if( keyboardInput)
|
||||
keyboardInput->doKey(key, keymod, mousex, mousey);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "FGButton.hxx"
|
||||
#include "Main/globals.hxx"
|
||||
#include <Viewer/renderer.hxx>
|
||||
#include <plib/pu.h>
|
||||
#include <Model/panelnode.hxx>
|
||||
#include <Cockpit/panel.hxx>
|
||||
#include <Viewer/FGEventHandler.hxx>
|
||||
|
@ -598,11 +597,7 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
|
|||
}
|
||||
|
||||
if (mode.pass_through) {
|
||||
// remove once PUI uses standard picking mechanism
|
||||
if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
|
||||
return; // pui handled it
|
||||
|
||||
// pui didn't want the click event so compute a
|
||||
// compute a
|
||||
// scenegraph intersection point corresponding to the mouse click
|
||||
if (updown == MOUSE_BUTTON_DOWN) {
|
||||
d->activePickCallbacks.init( b, ea );
|
||||
|
@ -619,7 +614,6 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
|
|||
} // mouse button was released
|
||||
} // of pass-through mode
|
||||
|
||||
// OK, PUI and the panel didn't want the click
|
||||
if (b >= MAX_MOUSE_BUTTONS) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
|
||||
<< " where only " << MAX_MOUSE_BUTTONS << " expected");
|
||||
|
@ -655,13 +649,7 @@ void FGMouseInput::processMotion(int x, int y, const osgGA::GUIEventAdapter* ea)
|
|||
}
|
||||
|
||||
mouse_mode &mode = m.modes[modeIndex];
|
||||
|
||||
// Pass on to PUI if requested, and return
|
||||
// if PUI consumed the event.
|
||||
if (mode.pass_through && puMouse(x, y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (d->haveWarped)
|
||||
{
|
||||
// don't fire mouse-movement events at the first update after warping the mouse,
|
||||
|
|
|
@ -184,7 +184,7 @@ if(ENABLE_FLITE)
|
|||
endif()
|
||||
|
||||
if (Qt5Core_FOUND)
|
||||
target_link_libraries(fgfs Qt5::Widgets fglauncher)
|
||||
target_link_libraries(fgfs Qt5::Core Qt5::Widgets fglauncher fgqmlui)
|
||||
set_property(TARGET fgfs PROPERTY AUTOMOC ON)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -726,7 +726,7 @@ static void tryAutosaveMigration(const SGPath& userDataPath, SGPropertyNode* pro
|
|||
if (matches != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (currentVersion < v) {
|
||||
// ignore autosaves from more recent versions; this happens when
|
||||
// running unsable and stable at the same time
|
||||
|
@ -772,13 +772,13 @@ static void tryAutosaveMigration(const SGPath& userDataPath, SGPropertyNode* pro
|
|||
|
||||
// copy remaining props out
|
||||
copyProperties(&oldProps, props);
|
||||
|
||||
|
||||
// inform the user
|
||||
#if !defined(FG_TESTLIB)
|
||||
flightgear::modalMessageBox("Settings migrated",
|
||||
"Saved settings were migrated from a previous version of FlightGear. "
|
||||
"If you encounter any problems when using the system, try restoring "
|
||||
"the default settings, before reporting a problem."
|
||||
"the default settings, before reporting a problem. "
|
||||
"Saved settings can affect the appearance, performance and features of the simulator.");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -694,6 +694,49 @@ clearLocation ()
|
|||
fgSetString("/sim/presets/fix", "");
|
||||
}
|
||||
|
||||
/*
|
||||
Using --addon=/foo/bar does:
|
||||
- load /foo/bar/config.xml into the Global Property Tree;
|
||||
- add /foo/bar to the list of aircraft paths to provide read access:
|
||||
- set property /addons/addon[n]/path = "/foo/bar".
|
||||
|
||||
Addons get initialized from addons.nas in FGData/Nasal.
|
||||
*/
|
||||
static int
|
||||
fgOptAddon(const char *arg)
|
||||
{
|
||||
const SGPath path(SGPath::fromLocal8Bit(arg));
|
||||
const SGPath config_xml = path / "config.xml";
|
||||
|
||||
if (config_xml.exists()) {
|
||||
try {
|
||||
readProperties(config_xml, globals->get_props());
|
||||
} catch (const sg_exception &e) {
|
||||
const string msg = "Unable to load '" + config_xml.utf8Str() + "'. "
|
||||
"Please check that this file exists and is readable.\n\n" +
|
||||
"Exception information: " + e.getFormattedMessage();
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, msg);
|
||||
flightgear::fatalMessageBoxThenExit(
|
||||
"FlightGear", "Unable to load an addon's config.xml file.", msg);
|
||||
}
|
||||
|
||||
globals->append_aircraft_path(path);
|
||||
fgGetNode("addons", true)
|
||||
->addChild("addon")
|
||||
->getNode("path", true)
|
||||
->setStringValue(path.utf8Str());
|
||||
} else {
|
||||
flightgear::fatalMessageBoxThenExit(
|
||||
"FlightGear",
|
||||
"Path specified with --addon does not exist or no config.xml found in "
|
||||
"that path.",
|
||||
"Unable to find the file '" + config_xml.utf8Str() + "'."
|
||||
);
|
||||
}
|
||||
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptVOR( const char * arg )
|
||||
{
|
||||
|
@ -1770,7 +1813,7 @@ struct OptionDesc {
|
|||
{"wp", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWp },
|
||||
{"flight-plan", true, OPTION_STRING, "/autopilot/route-manager/file-path", false, "", NULL },
|
||||
{"config", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
|
||||
{"addon", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
|
||||
{"addon", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptAddon },
|
||||
{"aircraft", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
||||
{"vehicle", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
||||
{"failure", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFailure },
|
||||
|
@ -2320,32 +2363,6 @@ int Options::parseOption(const string& s, bool fromConfigFile)
|
|||
}
|
||||
|
||||
return FG_OPTIONS_OK;
|
||||
|
||||
} else if ( s.find("--addon=") == 0) {
|
||||
/*
|
||||
using --addon=/foo/bar does
|
||||
* add /foo/bar/config.xml as propertyfile
|
||||
* add /foo/bar to aircraft_paths to provide read-access
|
||||
* sets property /addons/addon[n]/path = "/foo/bar"
|
||||
* addons get initialized from addons.nas in FGDATA/Nasal
|
||||
*/
|
||||
SGPath path = s.substr(8);
|
||||
SGPath config_xml = path / "config.xml";
|
||||
if( config_xml.exists() ) {
|
||||
p->propertyFiles.push_back(config_xml);
|
||||
globals->append_aircraft_path(path);
|
||||
fgGetNode("addons",true)
|
||||
->addChild("addon")
|
||||
->getNode("path",true)
|
||||
->setStringValue(path.str());
|
||||
} else {
|
||||
flightgear::fatalMessageBoxThenExit(
|
||||
"FlightGear",
|
||||
"Path specified with --addon does not exist or no config.xml found in that path"
|
||||
);
|
||||
}
|
||||
return FG_OPTIONS_OK;
|
||||
|
||||
} else if ( s.find( "--" ) == 0 ) {
|
||||
size_t eqPos = s.find( '=' );
|
||||
string key, value;
|
||||
|
|
|
@ -118,6 +118,10 @@ FlightPlan* FlightPlan::clone(const string& newIdent) const
|
|||
c->setSTAR(_star);
|
||||
c->setSID(_sid);
|
||||
|
||||
// mark data as unchanged since this is a clean plan
|
||||
c->_arrivalChanged = false;
|
||||
c->_departureChanged = false;
|
||||
|
||||
// copy legs
|
||||
c->_waypointsChanged = true;
|
||||
for (int l=0; l < numLegs(); ++l) {
|
||||
|
@ -692,6 +696,10 @@ bool FlightPlan::load(const SGPath& path)
|
|||
setIdent(path.file_base());
|
||||
}
|
||||
|
||||
// mark data as unchanged since this is a clean plan
|
||||
_arrivalChanged = false;
|
||||
_departureChanged = false;
|
||||
|
||||
_waypointsChanged = true;
|
||||
unlockDelegates();
|
||||
|
||||
|
|
|
@ -248,10 +248,12 @@ nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position,
|
|||
nav_list_type stations;
|
||||
|
||||
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
|
||||
int freqKhz = static_cast<int>(freq * 1000 + 0.5);
|
||||
PositionedIDVec ids(cache->findNavaidsByFreq(freqKhz, position, filter));
|
||||
// note this frequency is passed in 'database units', which depend on the
|
||||
// type of navaid being requested
|
||||
int f = static_cast<int>(freq * 100 + 0.5);
|
||||
PositionedIDVec ids(cache->findNavaidsByFreq(f, position, filter));
|
||||
|
||||
BOOST_FOREACH(PositionedID id, ids) {
|
||||
for (PositionedID id : ids) {
|
||||
FGNavRecordRef station = FGPositioned::loadById<FGNavRecord>(id);
|
||||
if (!filter->pass(station)) {
|
||||
continue;
|
||||
|
|
|
@ -1686,6 +1686,50 @@ static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* ar
|
|||
return r;
|
||||
}
|
||||
|
||||
static naRef f_findNDBByFrequency(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
int argOffset = 0;
|
||||
SGGeod pos = globals->get_aircraft_position();
|
||||
argOffset += geodFromArgs(args, 0, argc, pos);
|
||||
|
||||
if (!naIsNum(args[argOffset])) {
|
||||
naRuntimeError(c, "findNDBByFrquency expectes frequency (in kHz) as arg %d", argOffset);
|
||||
}
|
||||
|
||||
double dbFreq = args[argOffset++].num;
|
||||
FGNavList::TypeFilter filter(FGPositioned::NDB);
|
||||
nav_list_type navs = FGNavList::findAllByFreq(dbFreq, pos, &filter);
|
||||
if (navs.empty()) {
|
||||
return naNil();
|
||||
}
|
||||
|
||||
return ghostForNavaid(c, navs.front().ptr());
|
||||
}
|
||||
|
||||
static naRef f_findNDBsByFrequency(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
int argOffset = 0;
|
||||
SGGeod pos = globals->get_aircraft_position();
|
||||
argOffset += geodFromArgs(args, 0, argc, pos);
|
||||
|
||||
if (!naIsNum(args[argOffset])) {
|
||||
naRuntimeError(c, "findNDBsByFrquency expectes frequency (in kHz) as arg %d", argOffset);
|
||||
}
|
||||
|
||||
double dbFreq = args[argOffset++].num;
|
||||
FGNavList::TypeFilter filter(FGPositioned::NDB);
|
||||
nav_list_type navs = FGNavList::findAllByFreq(dbFreq, pos, &filter);
|
||||
if (navs.empty()) {
|
||||
return naNil();
|
||||
}
|
||||
|
||||
naRef r = naNewVector(c);
|
||||
for (nav_rec_ptr a : navs) {
|
||||
naVec_append(r, ghostForNavaid(c, a.ptr()));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
int argOffset = 0;
|
||||
|
@ -1700,10 +1744,13 @@ static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* arg
|
|||
double freqMhz = args[argOffset++].num;
|
||||
if (argOffset < argc) {
|
||||
type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
|
||||
if (type == FGPositioned::NDB) {
|
||||
naRuntimeError(c, "Use findNDBByFrquency to seach NDBs");
|
||||
}
|
||||
}
|
||||
|
||||
FGNavList::TypeFilter filter(type);
|
||||
nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
|
||||
auto navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
|
||||
if (navs.empty()) {
|
||||
return naNil();
|
||||
}
|
||||
|
@ -1725,14 +1772,15 @@ static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* ar
|
|||
double freqMhz = args[argOffset++].num;
|
||||
if (argOffset < argc) {
|
||||
type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
|
||||
if (type == FGPositioned::NDB) {
|
||||
naRuntimeError(c, "Use findNDBsByFrquency to seach NDBs");
|
||||
}
|
||||
}
|
||||
|
||||
naRef r = naNewVector(c);
|
||||
|
||||
FGNavList::TypeFilter filter(type);
|
||||
nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
|
||||
|
||||
BOOST_FOREACH(nav_rec_ptr a, navs) {
|
||||
auto navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
|
||||
for (nav_rec_ptr a : navs) {
|
||||
naVec_append(r, ghostForNavaid(c, a.ptr()));
|
||||
}
|
||||
|
||||
|
@ -2724,8 +2772,10 @@ static struct { const char* name; naCFunction func; } funcs[] = {
|
|||
{ "findAirportsByICAO", f_findAirportsByICAO },
|
||||
{ "navinfo", f_navinfo },
|
||||
{ "findNavaidsWithinRange", f_findNavaidsWithinRange },
|
||||
{ "findNavaidByFrequency", f_findNavaidByFrequency },
|
||||
{ "findNavaidsByFrequency", f_findNavaidsByFrequency },
|
||||
{ "findNDBByFrequencyKHz", f_findNDBByFrequency },
|
||||
{ "findNDBsByFrequencyKHz", f_findNDBsByFrequency },
|
||||
{ "findNavaidByFrequencyMHz", f_findNavaidByFrequency },
|
||||
{ "findNavaidsByFrequencyMHz", f_findNavaidsByFrequency },
|
||||
{ "findNavaidsByID", f_findNavaidsByIdent },
|
||||
{ "findFixesByID", f_findFixesByIdent },
|
||||
{ "flightplan", f_flightplan },
|
||||
|
|
|
@ -32,5 +32,10 @@ if (Qt5Core_FOUND)
|
|||
list(APPEND SOURCES GraphicsWindowQt5.cpp)
|
||||
endif()
|
||||
|
||||
if (YES)
|
||||
list(APPEND HEADERS PUICamera.hxx)
|
||||
list(APPEND SOURCES PUICamera.cxx)
|
||||
endif()
|
||||
|
||||
|
||||
flightgear_component(Viewer "${SOURCES}" "${HEADERS}")
|
||||
|
|
|
@ -28,8 +28,7 @@ namespace flightgear
|
|||
{
|
||||
const int displayStatsKey = 1;
|
||||
const int printStatsKey = 2;
|
||||
|
||||
|
||||
|
||||
// The manipulator is responsible for updating a Viewer's camera. Its
|
||||
// event handling method is also a convenient place to run the FG idle
|
||||
// and draw handlers.
|
||||
|
@ -53,49 +52,6 @@ FGEventHandler::FGEventHandler() :
|
|||
statsHandler->setKeyEventPrintsOutStats(printStatsKey);
|
||||
statsEvent->setEventType(GUIEventAdapter::KEYDOWN);
|
||||
|
||||
// OSG reports NumPad keycodes independent of the NumLock modifier.
|
||||
// Both KP-4 and KP-Left are reported as KEY_KP_Left (0xff96), so we
|
||||
// have to generate the locked keys ourselves.
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = '0';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_End] = '1';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Down] = '2';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = '3';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Left] = '4';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Right] = '6';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = '.';
|
||||
|
||||
// The comment above is incorrect on Mac osgViewer, at least. So we
|
||||
// need to map the 'num-locked' key codes to real values.
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_0] = '0';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_1] = '1';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_2] = '2';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_3] = '3';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_4] = '4';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_5] = '5';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_6] = '6';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_7] = '7';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_8] = '8';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_9] = '9';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Decimal] = '.';
|
||||
|
||||
|
||||
// mapping when NumLock is off
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = PU_KEY_INSERT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_End] = PU_KEY_END;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Down] = PU_KEY_DOWN;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = PU_KEY_PAGE_DOWN;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Left] = PU_KEY_LEFT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Right] = PU_KEY_RIGHT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Home] = PU_KEY_HOME;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Up] = PU_KEY_UP;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = PU_KEY_PAGE_UP;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = 127;
|
||||
|
||||
for (int i = 0; i < 128; i++)
|
||||
release_keys[i] = i;
|
||||
|
||||
|
@ -110,33 +66,6 @@ void FGEventHandler::reset()
|
|||
statsHandler->reset();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Translate OSG modifier mask to FG modifier mask.
|
||||
int osgToFGModifiers(int modifiers)
|
||||
{
|
||||
int result = 0;
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT)
|
||||
result |= KEYMOD_SHIFT;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_CTRL)
|
||||
result |= KEYMOD_CTRL;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_ALT)
|
||||
result |= KEYMOD_ALT;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_META)
|
||||
result |= KEYMOD_META;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_SUPER)
|
||||
result |= KEYMOD_SUPER;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_HYPER)
|
||||
result |= KEYMOD_HYPER;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void FGEventHandler::init(const osgGA::GUIEventAdapter& ea,
|
||||
osgGA::GUIActionAdapter& us)
|
||||
|
@ -312,75 +241,151 @@ bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key,
|
||||
int& modifiers)
|
||||
|
||||
int FGEventHandler::translateKey(const osgGA::GUIEventAdapter& ea)
|
||||
{
|
||||
using namespace osgGA;
|
||||
key = ea.getKey();
|
||||
|
||||
static std::map<int, int> numlockKeyMap;
|
||||
static std::map<int, int> noNumlockKeyMap;
|
||||
|
||||
if (numlockKeyMap.empty()) {
|
||||
// init these first time around
|
||||
|
||||
// OSG reports NumPad keycodes independent of the NumLock modifier.
|
||||
// Both KP-4 and KP-Left are reported as KEY_KP_Left (0xff96), so we
|
||||
// have to generate the locked keys ourselves.
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = '0';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_End] = '1';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Down] = '2';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = '3';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Left] = '4';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Right] = '6';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = '.';
|
||||
|
||||
// The comment above is incorrect on Mac osgViewer, at least. So we
|
||||
// need to map the 'num-locked' key codes to real values.
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_0] = '0';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_1] = '1';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_2] = '2';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_3] = '3';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_4] = '4';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_5] = '5';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_6] = '6';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_7] = '7';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_8] = '8';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_9] = '9';
|
||||
numlockKeyMap[GUIEventAdapter::KEY_KP_Decimal] = '.';
|
||||
|
||||
// mapping when NumLock is off
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = PU_KEY_INSERT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_End] = PU_KEY_END;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Down] = PU_KEY_DOWN;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = PU_KEY_PAGE_DOWN;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Left] = PU_KEY_LEFT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5';
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Right] = PU_KEY_RIGHT;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Home] = PU_KEY_HOME;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Up] = PU_KEY_UP;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = PU_KEY_PAGE_UP;
|
||||
noNumlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = 127;
|
||||
}
|
||||
|
||||
int key = ea.getKey();
|
||||
// XXX Probably other translations are needed too.
|
||||
switch (key) {
|
||||
case GUIEventAdapter::KEY_Escape: key = 0x1b; break;
|
||||
case GUIEventAdapter::KEY_Return: key = '\n'; break;
|
||||
case GUIEventAdapter::KEY_BackSpace: key = '\b'; break;
|
||||
case GUIEventAdapter::KEY_Delete: key = 0x7f; break;
|
||||
case GUIEventAdapter::KEY_Tab: key = '\t'; break;
|
||||
case GUIEventAdapter::KEY_Left: key = PU_KEY_LEFT; break;
|
||||
case GUIEventAdapter::KEY_Up: key = PU_KEY_UP; break;
|
||||
case GUIEventAdapter::KEY_Right: key = PU_KEY_RIGHT; break;
|
||||
case GUIEventAdapter::KEY_Down: key = PU_KEY_DOWN; break;
|
||||
case GUIEventAdapter::KEY_Page_Up: key = PU_KEY_PAGE_UP; break;
|
||||
case GUIEventAdapter::KEY_Page_Down: key = PU_KEY_PAGE_DOWN; break;
|
||||
case GUIEventAdapter::KEY_Home: key = PU_KEY_HOME; break;
|
||||
case GUIEventAdapter::KEY_End: key = PU_KEY_END; break;
|
||||
case GUIEventAdapter::KEY_Insert: key = PU_KEY_INSERT; break;
|
||||
case GUIEventAdapter::KEY_F1: key = PU_KEY_F1; break;
|
||||
case GUIEventAdapter::KEY_F2: key = PU_KEY_F2; break;
|
||||
case GUIEventAdapter::KEY_F3: key = PU_KEY_F3; break;
|
||||
case GUIEventAdapter::KEY_F4: key = PU_KEY_F4; break;
|
||||
case GUIEventAdapter::KEY_F5: key = PU_KEY_F5; break;
|
||||
case GUIEventAdapter::KEY_F6: key = PU_KEY_F6; break;
|
||||
case GUIEventAdapter::KEY_F7: key = PU_KEY_F7; break;
|
||||
case GUIEventAdapter::KEY_F8: key = PU_KEY_F8; break;
|
||||
case GUIEventAdapter::KEY_F9: key = PU_KEY_F9; break;
|
||||
case GUIEventAdapter::KEY_F10: key = PU_KEY_F10; break;
|
||||
case GUIEventAdapter::KEY_F11: key = PU_KEY_F11; break;
|
||||
case GUIEventAdapter::KEY_F12: key = PU_KEY_F12; break;
|
||||
case GUIEventAdapter::KEY_KP_Enter: key = '\r'; break;
|
||||
case GUIEventAdapter::KEY_KP_Add: key = '+'; break;
|
||||
case GUIEventAdapter::KEY_KP_Divide: key = '/'; break;
|
||||
case GUIEventAdapter::KEY_KP_Multiply: key = '*'; break;
|
||||
case GUIEventAdapter::KEY_KP_Subtract: key = '-'; break;
|
||||
case GUIEventAdapter::KEY_Escape: key = 0x1b; break;
|
||||
case GUIEventAdapter::KEY_Return: key = '\n'; break;
|
||||
case GUIEventAdapter::KEY_BackSpace: key = '\b'; break;
|
||||
case GUIEventAdapter::KEY_Delete: key = 0x7f; break;
|
||||
case GUIEventAdapter::KEY_Tab: key = '\t'; break;
|
||||
case GUIEventAdapter::KEY_Left: key = PU_KEY_LEFT; break;
|
||||
case GUIEventAdapter::KEY_Up: key = PU_KEY_UP; break;
|
||||
case GUIEventAdapter::KEY_Right: key = PU_KEY_RIGHT; break;
|
||||
case GUIEventAdapter::KEY_Down: key = PU_KEY_DOWN; break;
|
||||
case GUIEventAdapter::KEY_Page_Up: key = PU_KEY_PAGE_UP; break;
|
||||
case GUIEventAdapter::KEY_Page_Down: key = PU_KEY_PAGE_DOWN; break;
|
||||
case GUIEventAdapter::KEY_Home: key = PU_KEY_HOME; break;
|
||||
case GUIEventAdapter::KEY_End: key = PU_KEY_END; break;
|
||||
case GUIEventAdapter::KEY_Insert: key = PU_KEY_INSERT; break;
|
||||
case GUIEventAdapter::KEY_F1: key = PU_KEY_F1; break;
|
||||
case GUIEventAdapter::KEY_F2: key = PU_KEY_F2; break;
|
||||
case GUIEventAdapter::KEY_F3: key = PU_KEY_F3; break;
|
||||
case GUIEventAdapter::KEY_F4: key = PU_KEY_F4; break;
|
||||
case GUIEventAdapter::KEY_F5: key = PU_KEY_F5; break;
|
||||
case GUIEventAdapter::KEY_F6: key = PU_KEY_F6; break;
|
||||
case GUIEventAdapter::KEY_F7: key = PU_KEY_F7; break;
|
||||
case GUIEventAdapter::KEY_F8: key = PU_KEY_F8; break;
|
||||
case GUIEventAdapter::KEY_F9: key = PU_KEY_F9; break;
|
||||
case GUIEventAdapter::KEY_F10: key = PU_KEY_F10; break;
|
||||
case GUIEventAdapter::KEY_F11: key = PU_KEY_F11; break;
|
||||
case GUIEventAdapter::KEY_F12: key = PU_KEY_F12; break;
|
||||
case GUIEventAdapter::KEY_KP_Enter: key = '\r'; break;
|
||||
case GUIEventAdapter::KEY_KP_Add: key = '+'; break;
|
||||
case GUIEventAdapter::KEY_KP_Divide: key = '/'; break;
|
||||
case GUIEventAdapter::KEY_KP_Multiply: key = '*'; break;
|
||||
case GUIEventAdapter::KEY_KP_Subtract: key = '-'; break;
|
||||
}
|
||||
osgGA::GUIEventAdapter::EventType eventType = ea.getEventType();
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Num Lock is always true on Mac
|
||||
std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
|
||||
auto numPadIter = numlockKeyMap.find(key);
|
||||
if (numPadIter != numlockKeyMap.end()) {
|
||||
key = numPadIter->second;
|
||||
}
|
||||
#else
|
||||
if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK)
|
||||
{
|
||||
if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) {
|
||||
// NumLock on: map to numeric keys
|
||||
std::map<int, int>::iterator numPadIter = numlockKeyMap.find(key);
|
||||
auto numPadIter = numlockKeyMap.find(key);
|
||||
if (numPadIter != numlockKeyMap.end()) {
|
||||
key = numPadIter->second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// NumLock off: map to PU arrow keys
|
||||
std::map<int, int>::iterator numPadIter = noNumlockKeyMap.find(key);
|
||||
auto numPadIter = noNumlockKeyMap.find(key);
|
||||
if (numPadIter != noNumlockKeyMap.end()) {
|
||||
key = numPadIter->second;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return key;
|
||||
}
|
||||
|
||||
modifiers = osgToFGModifiers(ea.getModKeyMask());
|
||||
int FGEventHandler::translateModifiers(const osgGA::GUIEventAdapter& ea)
|
||||
{
|
||||
int result = 0;
|
||||
const auto modifiers = ea.getModKeyMask();
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT)
|
||||
result |= KEYMOD_SHIFT;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_CTRL)
|
||||
result |= KEYMOD_CTRL;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_ALT)
|
||||
result |= KEYMOD_ALT;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_META)
|
||||
result |= KEYMOD_META;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_SUPER)
|
||||
result |= KEYMOD_SUPER;
|
||||
|
||||
if (modifiers & osgGA::GUIEventAdapter::MODKEY_HYPER)
|
||||
result |= KEYMOD_HYPER;
|
||||
return result;
|
||||
}
|
||||
|
||||
void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key,
|
||||
int& modifiers)
|
||||
{
|
||||
key = translateKey(ea);
|
||||
modifiers = translateModifiers(ea);
|
||||
currentModifiers = modifiers;
|
||||
const auto eventType = ea.getEventType();
|
||||
if (eventType == osgGA::GUIEventAdapter::KEYUP)
|
||||
modifiers |= KEYMOD_RELEASED;
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ public:
|
|||
void setResizable(bool _resizable) { resizable = _resizable; }
|
||||
|
||||
void reset();
|
||||
|
||||
static int translateKey(const osgGA::GUIEventAdapter& ea);
|
||||
static int translateModifiers(const osgGA::GUIEventAdapter& ea);
|
||||
protected:
|
||||
osg::ref_ptr<osg::Node> _node;
|
||||
fgIdleHandler idleHandler;
|
||||
|
@ -109,8 +112,7 @@ protected:
|
|||
osg::ref_ptr<osgGA::GUIEventAdapter> statsEvent;
|
||||
int statsType;
|
||||
int currentModifiers;
|
||||
std::map<int, int> numlockKeyMap;
|
||||
std::map<int, int> noNumlockKeyMap;
|
||||
|
||||
void handleKey(const osgGA::GUIEventAdapter& ea, int& key, int& modifiers);
|
||||
bool resizable;
|
||||
bool mouseWarped;
|
||||
|
|
|
@ -645,6 +645,8 @@ bool GraphicsWindowQt5::realizeImplementation()
|
|||
getEventQueue()->syncWindowRectangleWithGraphicsContext();
|
||||
#endif
|
||||
|
||||
getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS);
|
||||
|
||||
_realized = true;
|
||||
return true;
|
||||
}
|
||||
|
|
323
src/Viewer/PUICamera.cxx
Normal file
323
src/Viewer/PUICamera.cxx
Normal file
|
@ -0,0 +1,323 @@
|
|||
// Copyright (C) 2017 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 "PUICamera.hxx"
|
||||
|
||||
#include <osg/StateSet>
|
||||
#include <osg/State>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Version>
|
||||
#include <osg/RenderInfo>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Geode>
|
||||
#include <osg/BlendFunc>
|
||||
|
||||
#include <osg/NodeVisitor>
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
#include <plib/pu.h>
|
||||
|
||||
#include <simgear/scene/util/SGNodeMasks.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/locale.hxx>
|
||||
#include <Viewer/CameraGroup.hxx>
|
||||
#include <Viewer/FGEventHandler.hxx>
|
||||
|
||||
// Old versions of PUI are missing these defines
|
||||
#ifndef PU_SCROLL_UP_BUTTON
|
||||
#define PU_SCROLL_UP_BUTTON 3
|
||||
#endif
|
||||
#ifndef PU_SCROLL_DOWN_BUTTON
|
||||
#define PU_SCROLL_DOWN_BUTTON 4
|
||||
#endif
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
class PUIDrawable : public osg::Drawable
|
||||
{
|
||||
public:
|
||||
PUIDrawable()
|
||||
{
|
||||
setUseDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
}
|
||||
|
||||
void drawImplementation(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
osg::State* state = renderInfo.getState();
|
||||
state->setActiveTextureUnit(0);
|
||||
state->setClientActiveTextureUnit(0);
|
||||
state->disableAllVertexArrays();
|
||||
|
||||
state->applyMode(GL_FOG, false);
|
||||
state->applyMode(GL_DEPTH_TEST, false);
|
||||
state->applyMode(GL_LIGHTING, false);
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glPushClientAttrib(~0u);
|
||||
|
||||
// reset pixel storage stuff for PLIB FNT drawing via glBitmap
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
puDisplay();
|
||||
|
||||
glPopClientAttrib();
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
osg::Object* cloneType() const override { return new PUIDrawable; }
|
||||
osg::Object* clone(const osg::CopyOp&) const override { return new PUIDrawable; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class PUIEventHandler : public osgGA::GUIEventHandler
|
||||
{
|
||||
public:
|
||||
PUIEventHandler(PUICamera* cam) :
|
||||
_puiCamera(cam)
|
||||
{}
|
||||
|
||||
bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *nv) override
|
||||
{
|
||||
if (ea.getHandled()) return false;
|
||||
|
||||
const double ratio = fgGetDouble("/sim/rendering/gui-pixel-ratio", 1.0);
|
||||
// PUI expects increasing downward mouse coords
|
||||
const int fixedY = (ea.getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) ?
|
||||
ea.getWindowHeight() - ea.getY() : ea.getY();
|
||||
const int scaledX = static_cast<int>(ea.getX() / ratio);
|
||||
const int scaledY = static_cast<int>(fixedY / ratio);
|
||||
|
||||
switch(ea.getEventType())
|
||||
{
|
||||
case(osgGA::GUIEventAdapter::MOVE):
|
||||
case(osgGA::GUIEventAdapter::DRAG):
|
||||
{
|
||||
bool handled = puMouse(scaledX, scaledY);
|
||||
return handled;
|
||||
}
|
||||
// you might thnk the code below is a good idea, but apparently it's not
|
||||
// PUI synthesises PU_DRAG internally, it doesn't want to see it delivered
|
||||
// from the windowing system
|
||||
#if 0
|
||||
case(osgGA::GUIEventAdapter::DRAG):
|
||||
{
|
||||
bool handled = puMouse(osgButtonToPUI(ea), PU_DRAG, scaledX, scaledY);
|
||||
return handled;
|
||||
}
|
||||
#endif
|
||||
case(osgGA::GUIEventAdapter::PUSH):
|
||||
case(osgGA::GUIEventAdapter::RELEASE):
|
||||
{
|
||||
bool upOrDown = (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE);
|
||||
bool handled = puMouse(osgButtonToPUI(ea), upOrDown, scaledX, scaledY);
|
||||
return handled;
|
||||
}
|
||||
|
||||
case(osgGA::GUIEventAdapter::KEYDOWN):
|
||||
case(osgGA::GUIEventAdapter::KEYUP):
|
||||
{
|
||||
const bool isKeyRelease = (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP);
|
||||
const int key = flightgear::FGEventHandler::translateKey(ea);
|
||||
bool handled = puKeyboard(key, isKeyRelease);
|
||||
return handled;
|
||||
}
|
||||
|
||||
case osgGA::GUIEventAdapter::SCROLL:
|
||||
{
|
||||
const int button = (ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP) ?
|
||||
PU_SCROLL_UP_BUTTON : PU_SCROLL_DOWN_BUTTON;
|
||||
bool handled = puMouse(button, PU_DOWN, scaledX, scaledY);
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
||||
case (osgGA::GUIEventAdapter::RESIZE):
|
||||
_puiCamera->resizeUi(ea.getWindowWidth(), ea.getWindowHeight());
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
int osgButtonToPUI(const osgGA::GUIEventAdapter &ea) const
|
||||
{
|
||||
switch (ea.getButton()) {
|
||||
case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON:
|
||||
return 0;
|
||||
case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON:
|
||||
return 1;
|
||||
case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON:
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PUICamera* _puiCamera;
|
||||
};
|
||||
|
||||
// The pu getWindow callback is supposed to return a window ID that
|
||||
// would allow drawing a GUI on different windows. All that stuff is
|
||||
// broken in multi-threaded OSG, and we only have one GUI "window"
|
||||
// anyway, so just return a constant.
|
||||
int PUICamera::puGetWindow()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PUICamera::puGetWindowSize(int* width, int* height)
|
||||
{
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
osg::Camera* camera = getGUICamera(CameraGroup::getDefault());
|
||||
if (!camera)
|
||||
return;
|
||||
|
||||
osg::Viewport* vport = camera->getViewport();
|
||||
const double ratio = fgGetDouble("/sim/rendering/gui-pixel-ratio", 1.0);
|
||||
*width = static_cast<int>(vport->width() / ratio);
|
||||
*height = static_cast<int>(vport->height() / ratio);
|
||||
}
|
||||
|
||||
void PUICamera::initPUI()
|
||||
{
|
||||
puSetWindowFuncs(PUICamera::puGetWindow, nullptr,
|
||||
PUICamera::puGetWindowSize, nullptr);
|
||||
puRealInit();
|
||||
}
|
||||
|
||||
PUICamera::PUICamera() :
|
||||
osg::Camera()
|
||||
{
|
||||
}
|
||||
|
||||
void PUICamera::init(osg::Group* parent)
|
||||
{
|
||||
setName("PUI FBO camera");
|
||||
|
||||
_fboTexture = new osg::Texture2D;
|
||||
_fboTexture->setInternalFormat(GL_RGBA);
|
||||
_fboTexture->setResizeNonPowerOfTwoHint(false);
|
||||
_fboTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
_fboTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
|
||||
// setup the camera as render to texture
|
||||
setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
setViewMatrix(osg::Matrix::identity());
|
||||
setClearMask( GL_COLOR_BUFFER_BIT );
|
||||
setClearColor( osg::Vec4( 0.0, 0.0, 0.0, 0.0 ) );
|
||||
setAllowEventFocus(false);
|
||||
setCullingActive(false);
|
||||
setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
|
||||
setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
attach(osg::Camera::COLOR_BUFFER, _fboTexture);
|
||||
|
||||
// set the camera's node mask, ensure the pick bit is clear
|
||||
setNodeMask(SG_NODEMASK_GUI_BIT);
|
||||
|
||||
// geode+drawable to call puDisplay, as a child of this FBO-camera
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
geode->setName("PUIDrawableGeode");
|
||||
geode->addDrawable(new PUIDrawable);
|
||||
addChild(geode);
|
||||
|
||||
// geometry (full-screen quad) to draw the output
|
||||
_fullScreenQuad = new osg::Geometry;
|
||||
_fullScreenQuad = osg::createTexturedQuadGeometry(osg::Vec3(0.0, 0.0, 0.0),
|
||||
osg::Vec3(200.0, 0.0, 0.0),
|
||||
osg::Vec3(0.0, 200.0, 0.0));
|
||||
_fullScreenQuad->setSupportsDisplayList(false);
|
||||
_fullScreenQuad->setNodeMask(SG_NODEMASK_GUI_BIT);
|
||||
_fullScreenQuad->setName("PUI fullscreen quad");
|
||||
|
||||
// state used for drawing the quad (not for rendering PUI, that's done in the
|
||||
// drawbale above)
|
||||
osg::StateSet* stateSet = _fullScreenQuad->getOrCreateStateSet();
|
||||
stateSet->setRenderBinDetails(1001, "RenderBin");
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
|
||||
stateSet->setTextureAttribute(0, _fboTexture);
|
||||
stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
||||
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
|
||||
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
|
||||
// geode to display the FSquad in the parent scene (which is GUI camera)
|
||||
osg::Geode* fsQuadGeode = new osg::Geode;
|
||||
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);
|
||||
|
||||
osg::Camera* camera = getGUICamera(CameraGroup::getDefault());
|
||||
if (camera) {
|
||||
osg::Viewport* vport = camera->getViewport();
|
||||
resizeUi(vport->width(), vport->height());
|
||||
}
|
||||
}
|
||||
|
||||
// remove once we require OSG 3.4
|
||||
void PUICamera::manuallyResizeFBO(int width, int height)
|
||||
{
|
||||
_fboTexture->setTextureSize(width, height);
|
||||
_fboTexture->dirtyTextureObject();
|
||||
}
|
||||
|
||||
void PUICamera::resizeUi(int width, int height)
|
||||
{
|
||||
double ratio = fgGetDouble("/sim/rendering/gui-pixel-ratio", 1.0);
|
||||
const int scaledWidth = static_cast<int>(width / ratio);
|
||||
const int scaledHeight = static_cast<int>(height / ratio);
|
||||
|
||||
osg::Camera::resize(scaledWidth, scaledHeight);
|
||||
|
||||
setViewport(0, 0, scaledWidth, scaledHeight);
|
||||
#if OSG_VERSION_LESS_THAN(3,4,0)
|
||||
manuallyResizeFBO(scaledWidth, scaledHeight);
|
||||
#else
|
||||
resizeAttachments(scaledWidth, scaledHeight);
|
||||
#endif
|
||||
|
||||
// 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->dirty();
|
||||
}
|
||||
|
57
src/Viewer/PUICamera.hxx
Normal file
57
src/Viewer/PUICamera.hxx
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright (C) 2017 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.
|
||||
|
||||
#ifndef PUICAMERA_HXX
|
||||
#define PUICAMERA_HXX
|
||||
|
||||
#include <osg/Camera>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Texture2D;
|
||||
class Geometry;
|
||||
}
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
class PUICamera : public osg::Camera
|
||||
{
|
||||
public:
|
||||
static void initPUI();
|
||||
|
||||
PUICamera();
|
||||
|
||||
osg::Object* cloneType() const override { return new PUICamera; }
|
||||
osg::Object* clone(const osg::CopyOp&) const override { return new PUICamera; }
|
||||
|
||||
// osg::Camera already defines a resize() so use this name
|
||||
void resizeUi(int width, int height);
|
||||
void init(osg::Group *parent);
|
||||
private:
|
||||
|
||||
void manuallyResizeFBO(int width, int height);
|
||||
|
||||
osg::Texture2D* _fboTexture = nullptr;
|
||||
osg::Geometry* _fullScreenQuad = nullptr;
|
||||
|
||||
static void puGetWindowSize(int *width, int *height);
|
||||
static int puGetWindow();
|
||||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // PUICAMERA_HXX
|
|
@ -17,8 +17,6 @@
|
|||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <plib/pu.h>
|
||||
|
||||
#include<algorithm>
|
||||
#include <functional>
|
||||
|
||||
|
@ -43,7 +41,7 @@ void GraphicsContextOperation::operator()(GraphicsContext* gc)
|
|||
}
|
||||
|
||||
WindowSystemAdapter::WindowSystemAdapter() :
|
||||
_nextWindowID(0), _isPuInitialized(false)
|
||||
_nextWindowID(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -57,33 +55,6 @@ WindowSystemAdapter::registerWindow(GraphicsContext* gc,
|
|||
return window;
|
||||
}
|
||||
|
||||
// The pu getWindow callback is supposed to return a window ID that
|
||||
// would allow drawing a GUI on different windows. All that stuff is
|
||||
// broken in multi-threaded OSG, and we only have one GUI "window"
|
||||
// anyway, so just return a constant.
|
||||
int WindowSystemAdapter::puGetWindow()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WindowSystemAdapter::puGetWindowSize(int* width, int* height)
|
||||
{
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
Camera* camera = getGUICamera(CameraGroup::getDefault());
|
||||
if (!camera)
|
||||
return;
|
||||
Viewport* vport = camera->getViewport();
|
||||
*width = (int)vport->width();
|
||||
*height = (int)vport->height();
|
||||
}
|
||||
|
||||
void WindowSystemAdapter::puInitialize()
|
||||
{
|
||||
puSetWindowFuncs(puGetWindow, 0, puGetWindowSize, 0);
|
||||
puRealInit();
|
||||
}
|
||||
|
||||
GraphicsWindow* WindowSystemAdapter::findWindow(const string& name)
|
||||
{
|
||||
for (WindowVector::iterator iter = windows.begin(), e = windows.end();
|
||||
|
|
|
@ -110,10 +110,7 @@ public:
|
|||
*/
|
||||
GraphicsWindow* registerWindow(osg::GraphicsContext* gc,
|
||||
const std::string& windowName);
|
||||
/** Initialize the plib pui interface library. This might happen
|
||||
*in another thread and may be deferred.
|
||||
*/
|
||||
virtual void puInitialize();
|
||||
|
||||
/** Find a window by name.
|
||||
* @param name the window name
|
||||
* @return the window or 0
|
||||
|
@ -129,12 +126,7 @@ public:
|
|||
static void setWSA(WindowSystemAdapter* wsa) { _wsa = wsa; }
|
||||
protected:
|
||||
int _nextWindowID;
|
||||
osg::ref_ptr<GraphicsContextOperation> _puInitOp;
|
||||
bool _isPuInitialized;
|
||||
static osg::ref_ptr<WindowSystemAdapter> _wsa;
|
||||
// Default callbacks for plib
|
||||
static int puGetWindow();
|
||||
static void puGetWindowSize(int* width, int* height);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
// 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>
|
||||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
# include <windows.h>
|
||||
|
@ -112,7 +110,14 @@
|
|||
#include "CameraGroup.hxx"
|
||||
#include "FGEventHandler.hxx"
|
||||
|
||||
#include <plib/pu.h>
|
||||
#if defined(HAVE_QT)
|
||||
#include <GUI/QQuickDrawable.hxx>
|
||||
#include <Viewer/GraphicsWindowQt5.hxx>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PUI)
|
||||
#include <Viewer/PUICamera.hxx>
|
||||
#endif
|
||||
|
||||
using namespace osg;
|
||||
using namespace simgear;
|
||||
|
@ -144,52 +149,6 @@ private:
|
|||
};
|
||||
|
||||
|
||||
class SGPuDrawable : public osg::Drawable {
|
||||
public:
|
||||
SGPuDrawable()
|
||||
{
|
||||
// Dynamic stuff, do not store geometry
|
||||
setUseDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
|
||||
osg::StateSet* stateSet = getOrCreateStateSet();
|
||||
stateSet->setRenderBinDetails(1001, "RenderBin");
|
||||
// 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);
|
||||
}
|
||||
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
|
||||
{ drawImplementation(*renderInfo.getState()); }
|
||||
void drawImplementation(osg::State& state) const
|
||||
{
|
||||
state.setActiveTextureUnit(0);
|
||||
state.setClientActiveTextureUnit(0);
|
||||
|
||||
state.disableAllVertexArrays();
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glPushClientAttrib(~0u);
|
||||
|
||||
puDisplay();
|
||||
|
||||
glPopClientAttrib();
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
virtual osg::Object* cloneType() const { return new SGPuDrawable; }
|
||||
virtual osg::Object* clone(const osg::CopyOp&) const { return new SGPuDrawable; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class SGHUDDrawable : public osg::Drawable {
|
||||
public:
|
||||
SGHUDDrawable()
|
||||
|
@ -445,6 +404,7 @@ FGRenderer::preinit( void )
|
|||
_viewerSceneRoot->setName("viewerSceneRoot");
|
||||
viewer->setSceneData(_viewerSceneRoot);
|
||||
|
||||
_quickDrawable = nullptr;
|
||||
_splash = new SplashScreen;
|
||||
if (_classicalRenderer) {
|
||||
_viewerSceneRoot->addChild(_splash);
|
||||
|
@ -1540,13 +1500,29 @@ FGRenderer::setupView( void )
|
|||
// plug in the GUI
|
||||
osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
|
||||
if (guiCamera) {
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
geode->addDrawable(new SGHUDDrawable);
|
||||
geode->addDrawable(new SGPuDrawable);
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
geode->addDrawable(new SGHUDDrawable);
|
||||
|
||||
// Draw first (eg. before Canvas GUI)
|
||||
guiCamera->insertChild(0, geode);
|
||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||
#if defined(HAVE_PUI)
|
||||
_puiCamera = new flightgear::PUICamera;
|
||||
_puiCamera->init(guiCamera);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
|
||||
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext());
|
||||
|
||||
if (graphicsWindowQt && !rootQMLPath.empty()) {
|
||||
_quickDrawable = new QQuickDrawable;
|
||||
_quickDrawable->setup(graphicsWindowQt);
|
||||
|
||||
_quickDrawable->setSource(QUrl::fromLocalFile(QString::fromStdString(rootQMLPath)));
|
||||
geode->addDrawable(_quickDrawable);
|
||||
}
|
||||
#endif
|
||||
// Draw first (eg. before Canvas GUI)
|
||||
guiCamera->insertChild(0, geode);
|
||||
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
|
||||
}
|
||||
|
||||
osg::Switch* sw = new osg::Switch;
|
||||
|
@ -1729,8 +1705,13 @@ FGRenderer::resize( int width, int height )
|
|||
_ysize->setIntValue(height);
|
||||
}
|
||||
|
||||
// update splash node if present ?
|
||||
// update splash node if present
|
||||
_splash->resize(width, height);
|
||||
#if defined(HAVE_QT)
|
||||
if (_quickDrawable) {
|
||||
_quickDrawable->resize(width, height);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef osgUtil::LineSegmentIntersector::Intersection Intersection;
|
||||
|
|
|
@ -39,11 +39,13 @@ namespace flightgear
|
|||
class FGEventHandler;
|
||||
struct CameraInfo;
|
||||
class CameraGroup;
|
||||
class PUICamera;
|
||||
}
|
||||
|
||||
class SGSky;
|
||||
class SGUpdateVisitor;
|
||||
class SplashScreen;
|
||||
class QQuickDrawable;
|
||||
|
||||
typedef std::vector<SGSceneryPick> PickList;
|
||||
|
||||
|
@ -191,6 +193,8 @@ protected:
|
|||
void setupRoot();
|
||||
|
||||
SplashScreen* _splash;
|
||||
QQuickDrawable* _quickDrawable;
|
||||
flightgear::PUICamera* _puiCamera = nullptr;
|
||||
};
|
||||
|
||||
bool fgDumpSceneGraphToFile(const char* filename);
|
||||
|
|
|
@ -6,11 +6,11 @@ include(GNUInstallDirs)
|
|||
|
||||
project(FGQCanvas)
|
||||
|
||||
find_package(Qt5 5.4 COMPONENTS Widgets WebSockets)
|
||||
find_package(Qt5 5.4 COMPONENTS Widgets WebSockets Gui Quick QuickWidgets)
|
||||
|
||||
if (NOT Qt5WebSockets_FOUND)
|
||||
message(WARNING "FGQCanvas utility requested, but QtWebSockets not found")
|
||||
message(STATUS "Check you have the development package for Qt5 WebSockets installed")
|
||||
if (NOT Qt5WebSockets_FOUND OR NOT Qt5QuickWidgets_FOUND)
|
||||
message(WARNING "FGQCanvas utility requested, but QtWebSockets/QtQuickWidgets not found")
|
||||
message(STATUS "Check you have the development package for Qt5 WebSockets/QuickWidgets installed")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
@ -44,6 +44,8 @@ set(SOURCES
|
|||
fgqcanvasimageloader.h
|
||||
elementdatamodel.cpp
|
||||
elementdatamodel.h
|
||||
canvasitem.cpp
|
||||
canvasitem.h
|
||||
)
|
||||
|
||||
qt5_wrap_ui(uic_sources temporarywidget.ui)
|
||||
|
@ -51,11 +53,13 @@ qt5_wrap_ui(uic_sources temporarywidget.ui)
|
|||
add_executable(fgqcanvas ${SOURCES} ${uic_sources})
|
||||
|
||||
set_property(TARGET fgqcanvas PROPERTY AUTOMOC ON)
|
||||
target_link_libraries(fgqcanvas Qt5::Core Qt5::Widgets Qt5::WebSockets)
|
||||
target_link_libraries(fgqcanvas Qt5::Core Qt5::Widgets Qt5::WebSockets Qt5::Quick Qt5::QuickWidgets)
|
||||
|
||||
target_include_directories(fgqcanvas PRIVATE ${PROJECT_SOURCE_DIR})
|
||||
|
||||
# so ui_foo.h files are found
|
||||
target_include_directories(fgqcanvas PRIVATE ${PROJECT_BINARY_DIR})
|
||||
|
||||
target_include_directories(fgqcanvas PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
install(TARGETS fgqcanvas RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
|
51
utils/fgqcanvas/canvasitem.cpp
Normal file
51
utils/fgqcanvas/canvasitem.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "canvasitem.h"
|
||||
|
||||
#include <QMatrix4x4>
|
||||
|
||||
class LocalTransform : public QQuickTransform
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LocalTransform(QObject *parent) : QQuickTransform(parent) {}
|
||||
|
||||
void setTransform(const QMatrix4x4 &t) {
|
||||
transform = t;
|
||||
update();
|
||||
}
|
||||
void applyTo(QMatrix4x4 *matrix) const Q_DECL_OVERRIDE {
|
||||
*matrix *= transform;
|
||||
}
|
||||
private:
|
||||
QMatrix4x4 transform;
|
||||
};
|
||||
|
||||
CanvasItem::CanvasItem(QQuickItem* pr)
|
||||
: QQuickItem(pr)
|
||||
, m_localTransform(new LocalTransform(this))
|
||||
{
|
||||
m_localTransform->prependToItem(this);
|
||||
}
|
||||
|
||||
void CanvasItem::setTransform(const QMatrix4x4 &mat)
|
||||
{
|
||||
m_localTransform->setTransform(mat);
|
||||
}
|
||||
|
||||
#include "canvasitem.moc"
|
40
utils/fgqcanvas/canvasitem.h
Normal file
40
utils/fgqcanvas/canvasitem.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef CANVASITEM_H
|
||||
#define CANVASITEM_H
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
class LocalTransform;
|
||||
|
||||
class CanvasItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CanvasItem(QQuickItem* pr = nullptr);
|
||||
|
||||
void setTransform(const QMatrix4x4& mat);
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
LocalTransform* m_localTransform;
|
||||
};
|
||||
|
||||
#endif // CANVASITEM_H
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "canvastreemodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef CANVASTREEMODEL_H
|
||||
#define CANVASTREEMODEL_H
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "elementdatamodel.h"
|
||||
|
||||
#include "fgcanvaselement.h"
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef ELEMENTDATAMODEL_H
|
||||
#define ELEMENTDATAMODEL_H
|
||||
|
||||
|
|
|
@ -1,13 +1,32 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgcanvaselement.h"
|
||||
|
||||
#include "localprop.h"
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
#include "fgcanvasgroup.h"
|
||||
#include "canvasitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
QTransform qTransformFromCanvas(LocalProp* prop)
|
||||
{
|
||||
|
@ -50,6 +69,16 @@ bool FGCanvasElement::isHighlighted() const
|
|||
return _highlighted;
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasElement::createQuickItem(QQuickItem *parent)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasElement::quickItem() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FGCanvasElement::FGCanvasElement(FGCanvasGroup* pr, LocalProp* prop) :
|
||||
QObject(pr),
|
||||
_propertyRoot(prop),
|
||||
|
@ -80,19 +109,31 @@ void FGCanvasElement::paint(FGCanvasPaintContext *context) const
|
|||
|
||||
p->save();
|
||||
|
||||
if (_hasClip) {
|
||||
#if 0
|
||||
|
||||
if (_hasClip)
|
||||
{
|
||||
// clip is define in parent's coordinate system
|
||||
#if 1
|
||||
p->save();
|
||||
p->setTransform(context->globalCoordinateTransform());
|
||||
p->setPen(Qt::yellow);
|
||||
p->setBrush(QBrush(Qt::yellow, Qt::DiagCrossPattern));
|
||||
p->drawRect(_clipRect);
|
||||
p->restore();
|
||||
#endif
|
||||
context->painter()->setClipRect(_clipRect);
|
||||
context->painter()->setClipping(true);
|
||||
|
||||
QTransform t = p->transform();
|
||||
p->setTransform(context->globalCoordinateTransform());
|
||||
p->setClipRect(_clipRect);
|
||||
p->setClipping(true);
|
||||
p->setTransform(t);
|
||||
}
|
||||
|
||||
p->setTransform(combinedTransform(), true /* combine */);
|
||||
QTransform combined = combinedTransform();
|
||||
p->setTransform(combined, true /* combine */);
|
||||
if (quickItem()) {
|
||||
quickItem()->setTransform(combined);
|
||||
}
|
||||
|
||||
if (_styleDirty) {
|
||||
_fillColor = parseColorValue(getCascadedStyle("fill"));
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGCANVASELEMENT_H
|
||||
#define FGCANVASELEMENT_H
|
||||
|
||||
|
@ -11,6 +28,8 @@
|
|||
class LocalProp;
|
||||
class FGCanvasPaintContext;
|
||||
class FGCanvasGroup;
|
||||
class CanvasItem;
|
||||
class QQuickItem;
|
||||
|
||||
class FGCanvasElement : public QObject
|
||||
{
|
||||
|
@ -34,6 +53,10 @@ public:
|
|||
|
||||
void setHighlighted(bool hilighted);
|
||||
bool isHighlighted() const;
|
||||
|
||||
virtual CanvasItem* createQuickItem(QQuickItem* parent);
|
||||
virtual CanvasItem* quickItem() const;
|
||||
|
||||
protected:
|
||||
virtual void doPaint(FGCanvasPaintContext* context) const;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include "canvasitem.h"
|
||||
#include "localprop.h"
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
#include "fgcanvaspath.h"
|
||||
|
@ -26,7 +27,6 @@ public:
|
|||
FGCanvasGroup::FGCanvasGroup(FGCanvasGroup* pr, LocalProp* prop) :
|
||||
FGCanvasElement(pr, prop)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const FGCanvasElementVec &FGCanvasGroup::children() const
|
||||
|
@ -65,6 +65,18 @@ unsigned int FGCanvasGroup::indexOfChild(const FGCanvasElement *e) const
|
|||
return std::distance(_children.begin(), it);
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasGroup::createQuickItem(QQuickItem *parent)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
_quick = new CanvasItem(parent);
|
||||
|
||||
for (auto e : _children) {
|
||||
e->createQuickItem(_quick);
|
||||
}
|
||||
|
||||
return _quick;
|
||||
}
|
||||
|
||||
void FGCanvasGroup::doPaint(FGCanvasPaintContext *context) const
|
||||
{
|
||||
if (_zIndicesDirty) {
|
||||
|
@ -121,6 +133,12 @@ bool FGCanvasGroup::onChildAdded(LocalProp *prop)
|
|||
|
||||
if (newChildCount > 0) {
|
||||
markChildZIndicesDirty();
|
||||
|
||||
if (_quick) {
|
||||
qDebug() << "creating quick item for child";
|
||||
_children.back()->createQuickItem(_quick);
|
||||
}
|
||||
|
||||
emit childAdded();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ public:
|
|||
|
||||
unsigned int indexOfChild(const FGCanvasElement* e) const;
|
||||
|
||||
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
||||
CanvasItem* quickItem() const override
|
||||
{ return _quick; }
|
||||
|
||||
signals:
|
||||
void childAdded();
|
||||
void childRemoved(int index);
|
||||
|
@ -40,6 +44,8 @@ private:
|
|||
mutable FGCanvasElementVec _children;
|
||||
mutable bool _zIndicesDirty = false;
|
||||
mutable bool _cachedSymbolDirty = false;
|
||||
|
||||
CanvasItem* _quick = nullptr;
|
||||
};
|
||||
|
||||
#endif // FGCANVASGROUP_H
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
|
||||
FGCanvasPaintContext::FGCanvasPaintContext(QPainter* painter) :
|
||||
_painter(painter)
|
||||
{
|
||||
|
||||
_globalCoordsTransform = painter->transform();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGCANVASPAINTCONTEXT_H
|
||||
#define FGCANVASPAINTCONTEXT_H
|
||||
|
||||
|
@ -11,8 +28,14 @@ public:
|
|||
QPainter* painter() const
|
||||
{ return _painter; }
|
||||
|
||||
QTransform globalCoordinateTransform() const
|
||||
{
|
||||
return _globalCoordsTransform;
|
||||
}
|
||||
|
||||
private:
|
||||
QPainter* _painter;
|
||||
QTransform _globalCoordsTransform;
|
||||
};
|
||||
|
||||
#endif // FGCANVASPAINTCONTEXT_H
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgcanvaspath.h"
|
||||
|
||||
#include <cctype>
|
||||
|
@ -6,9 +23,207 @@
|
|||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
#include <QPen>
|
||||
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
#include "localprop.h"
|
||||
#include "canvasitem.h"
|
||||
|
||||
#include "private/qtriangulator_p.h" // private QtGui header
|
||||
#include "private/qtriangulatingstroker_p.h" // private QtGui header
|
||||
#include "private/qvectorpath_p.h" // private QtGui header
|
||||
|
||||
#include <QSGGeometry>
|
||||
#include <QSGGeometryNode>
|
||||
#include <QSGFlatColorMaterial>
|
||||
|
||||
class PathQuickItem : public CanvasItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged)
|
||||
Q_PROPERTY(QPen stroke READ stroke WRITE setStroke NOTIFY strokeChanged)
|
||||
|
||||
public:
|
||||
PathQuickItem(QQuickItem* parent)
|
||||
: CanvasItem(parent)
|
||||
{
|
||||
setFlag(ItemHasContents);
|
||||
}
|
||||
|
||||
void setPath(QPainterPath pp)
|
||||
{
|
||||
m_path = pp;
|
||||
|
||||
QRectF pathBounds = pp.boundingRect();
|
||||
setImplicitSize(pathBounds.width(), pathBounds.height());
|
||||
|
||||
update(); // request a paint node update
|
||||
}
|
||||
|
||||
virtual QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *data)
|
||||
{
|
||||
if (m_path.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSGGeometryNode* fillGeom = nullptr;
|
||||
QSGGeometryNode* strokeGeom = nullptr;
|
||||
|
||||
if (m_fillColor.isValid()) {
|
||||
// TODO: compute LOD for qTriangulate based on world transform
|
||||
QTransform transform;
|
||||
QTriangleSet triangles = qTriangulate(m_path, transform);
|
||||
|
||||
// TODO, handle > 2^16 indices
|
||||
Q_ASSERT(triangles.indices.type() == QVertexIndexVector::UnsignedShort);
|
||||
|
||||
QSGGeometry* sgGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
|
||||
triangles.vertices.size() >> 1,
|
||||
triangles.indices.size());
|
||||
sgGeom->setIndexDataPattern(QSGGeometry::StaticPattern);
|
||||
sgGeom->setDrawingMode(GL_TRIANGLES);
|
||||
|
||||
//
|
||||
QSGGeometry::Point2D *points = sgGeom->vertexDataAsPoint2D();
|
||||
for (int v=0; v < triangles.vertices.size(); ) {
|
||||
const float vx = triangles.vertices.at(v++);
|
||||
const float vy = triangles.vertices.at(v++);
|
||||
(points++)->set(vx, vy);
|
||||
}
|
||||
|
||||
quint16* indices = sgGeom->indexDataAsUShort();
|
||||
::memcpy(indices, triangles.indices.data(), sizeof(unsigned short) * triangles.indices.size());
|
||||
|
||||
// create the node now, pretty trivial
|
||||
fillGeom = new QSGGeometryNode;
|
||||
fillGeom->setGeometry(sgGeom);
|
||||
fillGeom->setFlag(QSGNode::OwnsGeometry);
|
||||
|
||||
QSGFlatColorMaterial* mat = new QSGFlatColorMaterial();
|
||||
mat->setColor(m_fillColor);
|
||||
fillGeom->setMaterial(mat);
|
||||
fillGeom->setFlag(QSGNode::OwnsMaterial);
|
||||
}
|
||||
|
||||
if (m_stroke.style() != Qt::NoPen) {
|
||||
const QVectorPath& vp = qtVectorPathForPath(m_path);
|
||||
QRectF clipBounds;
|
||||
QTriangulatingStroker ts;
|
||||
QPainter::RenderHints renderHints;
|
||||
|
||||
if (m_stroke.style() == Qt::SolidLine) {
|
||||
ts.process(vp, m_stroke, clipBounds, renderHints);
|
||||
#if 0
|
||||
inline int vertexCount() const { return m_vertices.size(); }
|
||||
inline const float *vertices() const { return m_vertices.data(); }
|
||||
|
||||
#endif
|
||||
} else {
|
||||
QDashedStrokeProcessor dasher;
|
||||
dasher.process(vp, m_stroke, clipBounds, renderHints);
|
||||
|
||||
QVectorPath dashStroke(dasher.points(),
|
||||
dasher.elementCount(),
|
||||
dasher.elementTypes(),
|
||||
renderHints);
|
||||
|
||||
ts.process(dashStroke, m_stroke, clipBounds, renderHints);
|
||||
}
|
||||
|
||||
QSGGeometry* sgGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),
|
||||
ts.vertexCount() >> 1);
|
||||
sgGeom->setVertexDataPattern(QSGGeometry::StaticPattern);
|
||||
sgGeom->setDrawingMode(GL_TRIANGLE_STRIP);
|
||||
|
||||
QSGGeometry::Point2D *points = sgGeom->vertexDataAsPoint2D();
|
||||
const float* vPtr = ts.vertices();
|
||||
for (int v=0; v < ts.vertexCount(); v += 2) {
|
||||
const float vx = *vPtr++;
|
||||
const float vy = *vPtr++;
|
||||
(points++)->set(vx, vy);
|
||||
}
|
||||
|
||||
// create the node now, pretty trivial
|
||||
strokeGeom = new QSGGeometryNode;
|
||||
strokeGeom->setGeometry(sgGeom);
|
||||
strokeGeom->setFlag(QSGNode::OwnsGeometry);
|
||||
|
||||
QSGFlatColorMaterial* mat = new QSGFlatColorMaterial();
|
||||
mat->setColor(m_stroke.color());
|
||||
strokeGeom->setMaterial(mat);
|
||||
strokeGeom->setFlag(QSGNode::OwnsMaterial);
|
||||
}
|
||||
|
||||
if (fillGeom && strokeGeom) {
|
||||
QSGNode* groupNode = new QSGNode;
|
||||
groupNode->appendChildNode(fillGeom);
|
||||
groupNode->appendChildNode(strokeGeom);
|
||||
return groupNode;
|
||||
} else if (fillGeom) {
|
||||
return fillGeom;
|
||||
}
|
||||
|
||||
return strokeGeom;
|
||||
}
|
||||
|
||||
QColor fillColor() const
|
||||
{
|
||||
return m_fillColor;
|
||||
}
|
||||
|
||||
QPen stroke() const
|
||||
{
|
||||
return m_stroke;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setFillColor(QColor fillColor)
|
||||
{
|
||||
if (m_fillColor == fillColor)
|
||||
return;
|
||||
|
||||
m_fillColor = fillColor;
|
||||
emit fillColorChanged(fillColor);
|
||||
update();
|
||||
}
|
||||
|
||||
void setStroke(QPen stroke)
|
||||
{
|
||||
if (m_stroke == stroke)
|
||||
return;
|
||||
|
||||
m_stroke = stroke;
|
||||
emit strokeChanged(stroke);
|
||||
update();
|
||||
}
|
||||
|
||||
signals:
|
||||
void fillColorChanged(QColor fillColor);
|
||||
|
||||
void strokeChanged(QPen stroke);
|
||||
|
||||
protected:
|
||||
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||
{
|
||||
QQuickItem::geometryChanged(newGeometry, oldGeometry);
|
||||
update();
|
||||
}
|
||||
|
||||
QRectF boundingRect() const
|
||||
{
|
||||
if ((width() == 0.0) || (height() == 0.0)) {
|
||||
return QRectF(0.0, 0.0, implicitWidth(), implicitHeight());
|
||||
}
|
||||
|
||||
return QQuickItem::boundingRect();
|
||||
}
|
||||
|
||||
private:
|
||||
QPainterPath m_path;
|
||||
QColor m_fillColor;
|
||||
QPen m_stroke;
|
||||
};
|
||||
|
||||
static void pathArcSegment(QPainterPath &path,
|
||||
qreal xc, qreal yc,
|
||||
|
@ -155,18 +370,23 @@ static void pathArc(QPainterPath &path,
|
|||
FGCanvasPath::FGCanvasPath(FGCanvasGroup* pr, LocalProp* prop) :
|
||||
FGCanvasElement(pr, prop)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FGCanvasPath::doPaint(FGCanvasPaintContext *context) const
|
||||
{
|
||||
if (_pathDirty) {
|
||||
rebuildPath();
|
||||
if (_quickPath) {
|
||||
_quickPath->setPath(_painterPath);
|
||||
}
|
||||
_pathDirty = false;
|
||||
}
|
||||
|
||||
if (_penDirty) {
|
||||
rebuildPen();
|
||||
if (_quickPath) {
|
||||
_quickPath->setStroke(_stroke);
|
||||
}
|
||||
_penDirty = false;
|
||||
}
|
||||
|
||||
|
@ -207,6 +427,20 @@ void FGCanvasPath::markStyleDirty()
|
|||
_penDirty = true;
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasPath::createQuickItem(QQuickItem *parent)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
_quickPath = new PathQuickItem(parent);
|
||||
_quickPath->setPath(_painterPath);
|
||||
_quickPath->setStroke(_stroke);
|
||||
return _quickPath;
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasPath::quickItem() const
|
||||
{
|
||||
return _quickPath;
|
||||
}
|
||||
|
||||
void FGCanvasPath::markPathDirty()
|
||||
{
|
||||
_pathDirty = true;
|
||||
|
@ -693,3 +927,5 @@ void FGCanvasPath::rebuildPen() const
|
|||
|
||||
_stroke = p;
|
||||
}
|
||||
|
||||
#include "fgcanvaspath.moc"
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGCANVASPATH_H
|
||||
#define FGCANVASPATH_H
|
||||
|
||||
|
@ -6,6 +23,8 @@
|
|||
#include <QPainterPath>
|
||||
#include <QPen>
|
||||
|
||||
class PathQuickItem;
|
||||
|
||||
class FGCanvasPath : public FGCanvasElement
|
||||
{
|
||||
public:
|
||||
|
@ -15,6 +34,10 @@ protected:
|
|||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||
|
||||
virtual void markStyleDirty() override;
|
||||
|
||||
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
||||
CanvasItem* quickItem() const override;
|
||||
|
||||
private:
|
||||
void markPathDirty();
|
||||
void markStrokeDirty();
|
||||
|
@ -44,6 +67,8 @@ private:
|
|||
mutable PaintType _paintType = Path;
|
||||
mutable QRectF _rect;
|
||||
mutable QSizeF _roundRectRadius;
|
||||
|
||||
PathQuickItem* _quickPath = nullptr;
|
||||
};
|
||||
|
||||
#endif // FGCANVASPATH_H
|
||||
|
|
|
@ -1,11 +1,33 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgcanvastext.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
#include "localprop.h"
|
||||
#include "fgqcanvasfontcache.h"
|
||||
#include "canvasitem.h"
|
||||
|
||||
static QQmlComponent* static_textComponent = nullptr;
|
||||
|
||||
FGCanvasText::FGCanvasText(FGCanvasGroup* pr, LocalProp* prop) :
|
||||
FGCanvasElement(pr, prop),
|
||||
|
@ -17,6 +39,26 @@ FGCanvasText::FGCanvasText(FGCanvasGroup* pr, LocalProp* prop) :
|
|||
this, &FGCanvasText::onFontLoaded);
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasText::createQuickItem(QQuickItem *parent)
|
||||
{
|
||||
Q_ASSERT(static_textComponent);
|
||||
_quickItem = qobject_cast<CanvasItem*>(static_textComponent->create());
|
||||
_quickItem->setParentItem(parent);
|
||||
markFontDirty(); // so it gets set on the new item
|
||||
return _quickItem;
|
||||
}
|
||||
|
||||
CanvasItem *FGCanvasText::quickItem() const
|
||||
{
|
||||
return _quickItem;
|
||||
}
|
||||
|
||||
void FGCanvasText::setEngine(QQmlEngine *engine)
|
||||
{
|
||||
static_textComponent = new QQmlComponent(engine, QUrl("text.qml"));
|
||||
qDebug() << static_textComponent->errorString();
|
||||
}
|
||||
|
||||
void FGCanvasText::doPaint(FGCanvasPaintContext *context) const
|
||||
{
|
||||
if (_fontDirty) {
|
||||
|
@ -82,6 +124,9 @@ bool FGCanvasText::onChildAdded(LocalProp *prop)
|
|||
void FGCanvasText::onTextChanged(QVariant var)
|
||||
{
|
||||
_text = var.toString();
|
||||
if (_quickItem) {
|
||||
_quickItem->setProperty("text", var);
|
||||
}
|
||||
}
|
||||
|
||||
void FGCanvasText::setDrawMode(QVariant var)
|
||||
|
@ -155,9 +200,18 @@ void FGCanvasText::rebuildFont() const
|
|||
// wait for the correct font
|
||||
}
|
||||
|
||||
f.setPixelSize(getCascadedStyle("character-size", 16).toInt());
|
||||
const int pixelSize = getCascadedStyle("character-size", 16).toInt();
|
||||
f.setPixelSize(pixelSize);
|
||||
_font = f;
|
||||
_metrics = QFontMetricsF(_font);
|
||||
rebuildAlignment(getCascadedStyle("alignment"));
|
||||
|
||||
if (_quickItem) {
|
||||
_quickItem->setProperty("fontFamily", f.family());
|
||||
_quickItem->setProperty("fontPixelSize", pixelSize);
|
||||
|
||||
QColor fill = fillColor();
|
||||
_quickItem->setProperty("color", fill.name());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,26 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGCANVASTEXT_H
|
||||
#define FGCANVASTEXT_H
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontMetricsF>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "fgcanvaselement.h"
|
||||
|
||||
|
@ -11,7 +29,10 @@ class FGCanvasText : public FGCanvasElement
|
|||
public:
|
||||
FGCanvasText(FGCanvasGroup* pr, LocalProp* prop);
|
||||
|
||||
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
||||
CanvasItem* quickItem() const override;
|
||||
|
||||
static void setEngine(QQmlEngine* engine);
|
||||
protected:
|
||||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||
|
||||
|
@ -36,6 +57,8 @@ private:
|
|||
mutable bool _fontDirty = true;
|
||||
mutable QFont _font;
|
||||
mutable QFontMetricsF _metrics;
|
||||
|
||||
CanvasItem* _quickItem = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgcanvaswidget.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
@ -37,7 +54,7 @@ void FGCanvasWidget::paintEvent(QPaintEvent *pe)
|
|||
|
||||
QSizeF sz(_canvasRoot->value("size[0]", 512).toInt(),
|
||||
_canvasRoot->value("size[1]", 512).toInt());
|
||||
FGCanvasPaintContext context(&painter);
|
||||
|
||||
|
||||
// set scaling by canvas size
|
||||
|
||||
|
@ -46,6 +63,9 @@ void FGCanvasWidget::paintEvent(QPaintEvent *pe)
|
|||
const double usedScale = std::min(verticalScaling, horizontalScaling);
|
||||
|
||||
painter.scale(usedScale, usedScale);
|
||||
|
||||
// captures the view scaling
|
||||
FGCanvasPaintContext context(&painter);
|
||||
_rootElement->paint(&context);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGCANVASWIDGET_H
|
||||
#define FGCANVASWIDGET_H
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgqcanvasfontcache.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGQCANVASFONTCACHE_H
|
||||
#define FGQCANVASFONTCACHE_H
|
||||
|
||||
|
|
|
@ -1,11 +1,32 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgqcanvasimage.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QQmlComponent>
|
||||
|
||||
#include "fgcanvaspaintcontext.h"
|
||||
#include "localprop.h"
|
||||
#include "fgqcanvasimageloader.h"
|
||||
#include "canvasitem.h"
|
||||
|
||||
static QQmlComponent* static_imageComponent = nullptr;
|
||||
|
||||
FGQCanvasImage::FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop) :
|
||||
FGCanvasElement(pr, prop)
|
||||
|
@ -13,6 +34,12 @@ FGQCanvasImage::FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop) :
|
|||
|
||||
}
|
||||
|
||||
void FGQCanvasImage::setEngine(QQmlEngine *engine)
|
||||
{
|
||||
static_imageComponent = new QQmlComponent(engine, QUrl("image.qml"));
|
||||
qDebug() << static_imageComponent->errorString();
|
||||
}
|
||||
|
||||
void FGQCanvasImage::doPaint(FGCanvasPaintContext *context) const
|
||||
{
|
||||
if (_imageDirty) {
|
||||
|
@ -117,3 +144,17 @@ void FGQCanvasImage::rebuildImage() const
|
|||
void FGQCanvasImage::markStyleDirty()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CanvasItem *FGQCanvasImage::createQuickItem(QQuickItem *parent)
|
||||
{
|
||||
Q_ASSERT(static_imageComponent);
|
||||
_quickItem = qobject_cast<CanvasItem*>(static_imageComponent->create());
|
||||
_quickItem->setParentItem(parent);
|
||||
return _quickItem;
|
||||
}
|
||||
|
||||
CanvasItem *FGQCanvasImage::quickItem() const
|
||||
{
|
||||
return _quickItem;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGQCANVASIMAGE_H
|
||||
#define FGQCANVASIMAGE_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "fgcanvaselement.h"
|
||||
|
||||
|
@ -11,11 +29,16 @@ class FGQCanvasImage : public FGCanvasElement
|
|||
public:
|
||||
FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop);
|
||||
|
||||
static void setEngine(QQmlEngine* engine);
|
||||
|
||||
CanvasItem* createQuickItem(QQuickItem *parent) override;
|
||||
CanvasItem* quickItem() const override;
|
||||
protected:
|
||||
virtual void doPaint(FGCanvasPaintContext* context) const override;
|
||||
|
||||
virtual void markStyleDirty() override;
|
||||
|
||||
|
||||
private slots:
|
||||
void markImageDirty();
|
||||
void markSourceDirty();
|
||||
|
@ -33,6 +56,8 @@ private:
|
|||
QString _source;
|
||||
mutable QSizeF _destSize;
|
||||
mutable QRectF _sourceRect;
|
||||
|
||||
CanvasItem* _quickItem = nullptr;
|
||||
};
|
||||
|
||||
#endif // FGQCANVASIMAGE_H
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgqcanvasimageloader.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGQCANVASIMAGELOADER_H
|
||||
#define FGQCANVASIMAGELOADER_H
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "fgqcanvasmap.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FGQCANVASMAP_H
|
||||
#define FGQCANVASMAP_H
|
||||
|
||||
|
|
14
utils/fgqcanvas/image.qml
Normal file
14
utils/fgqcanvas/image.qml
Normal file
|
@ -0,0 +1,14 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear 1.0
|
||||
|
||||
CanvasItem {
|
||||
implicitHeight: img.implicitHeight
|
||||
implicitWidth: img.implicitWidth
|
||||
|
||||
property alias source: img.source
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
}
|
||||
}
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "localprop.h"
|
||||
|
||||
#include <QJsonValue>
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef LOCALPROP_H
|
||||
#define LOCALPROP_H
|
||||
|
||||
|
|
|
@ -1,15 +1,35 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "temporarywidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QStandardPaths>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "fgqcanvasfontcache.h"
|
||||
#include "fgqcanvasimageloader.h"
|
||||
#include "canvasitem.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
a.setApplicationName("FGCanvas");
|
||||
|
@ -25,7 +45,11 @@ int main(int argc, char *argv[])
|
|||
FGQCanvasFontCache::initialise(downloader);
|
||||
FGQCanvasImageLoader::initialise(downloader);
|
||||
|
||||
qmlRegisterType<CanvasItem>("FlightGear", 1, 0, "CanvasItem");
|
||||
|
||||
|
||||
TemporaryWidget w;
|
||||
w.setNetworkAccess(downloader);
|
||||
w.show();
|
||||
|
||||
int result = a.exec();
|
||||
|
|
5
utils/fgqcanvas/root.qml
Normal file
5
utils/fgqcanvas/root.qml
Normal file
|
@ -0,0 +1,5 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Rectangle {
|
||||
color: "black"
|
||||
}
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "temporarywidget.h"
|
||||
#include "ui_temporarywidget.h"
|
||||
|
||||
|
@ -7,20 +24,35 @@
|
|||
#include <QJsonValue>
|
||||
#include <QSettings>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "fgqcanvasfontcache.h"
|
||||
#include "fgqcanvasimageloader.h"
|
||||
#include "canvastreemodel.h"
|
||||
#include "localprop.h"
|
||||
#include "elementdatamodel.h"
|
||||
#include "fgcanvastext.h"
|
||||
#include "fgqcanvasimage.h"
|
||||
|
||||
TemporaryWidget::TemporaryWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::TemporaryWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->connectButton, &QPushButton::clicked, this, &TemporaryWidget::onStartConnect);
|
||||
connect(ui->connectButton, &QPushButton::clicked, this, &TemporaryWidget::onConnect);
|
||||
restoreSettings();
|
||||
|
||||
ui->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
ui->quickWidget->setSource(QUrl("root.qml"));
|
||||
|
||||
FGCanvasText::setEngine(ui->quickWidget->engine());
|
||||
FGQCanvasImage::setEngine(ui->quickWidget->engine());
|
||||
|
||||
connect(ui->canvasSelectCombo, SIGNAL(activated(int)),
|
||||
this, SLOT(onCanvasSelected(int)));
|
||||
ui->canvasSelectCombo->hide();
|
||||
}
|
||||
|
||||
TemporaryWidget::~TemporaryWidget()
|
||||
|
@ -30,49 +62,77 @@ TemporaryWidget::~TemporaryWidget()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void TemporaryWidget::onStartConnect()
|
||||
void TemporaryWidget::setNetworkAccess(QNetworkAccessManager *dl)
|
||||
{
|
||||
QUrl wsUrl;
|
||||
wsUrl.setScheme("ws");
|
||||
wsUrl.setHost(ui->hostName->text());
|
||||
wsUrl.setPort(ui->portEdit->text().toInt());
|
||||
|
||||
// string clean up, to ensure our root path has a leading slash but
|
||||
// no trailing slash
|
||||
QString propPath = ui->propertyPath->text();
|
||||
QString rootPath = propPath;
|
||||
if (!propPath.startsWith('/')) {
|
||||
rootPath = '/' + propPath;
|
||||
}
|
||||
|
||||
if (propPath.endsWith('/')) {
|
||||
rootPath.chop(1);
|
||||
}
|
||||
|
||||
wsUrl.setPath("/PropertyTreeMirror" + rootPath);
|
||||
rootPropertyPath = rootPath.toUtf8();
|
||||
|
||||
connect(&m_webSocket, &QWebSocket::connected, this, &TemporaryWidget::onConnected);
|
||||
connect(&m_webSocket, &QWebSocket::disconnected, this, &TemporaryWidget::onSocketClosed);
|
||||
|
||||
saveSettings();
|
||||
|
||||
qDebug() << "starting connection to:" << wsUrl;
|
||||
m_webSocket.open(wsUrl);
|
||||
m_netAccess = dl;
|
||||
}
|
||||
|
||||
void TemporaryWidget::onConnected()
|
||||
void TemporaryWidget::onConnect()
|
||||
{
|
||||
QUrl queryUrl;
|
||||
queryUrl.setScheme("http");
|
||||
queryUrl.setHost(ui->hostName->text());
|
||||
queryUrl.setPort(ui->portEdit->text().toInt());
|
||||
queryUrl.setPath("/json/canvas/by-index");
|
||||
queryUrl.setQuery("d=2");
|
||||
|
||||
QNetworkReply* reply = m_netAccess->get(QNetworkRequest(queryUrl));
|
||||
connect(reply, &QNetworkReply::finished, this, &TemporaryWidget::onFinishedGetCanvasList);
|
||||
|
||||
ui->connectButton->setEnabled(false);
|
||||
}
|
||||
|
||||
QJsonObject jsonPropNodeFindChild(QJsonObject obj, QByteArray name)
|
||||
{
|
||||
Q_FOREACH (QJsonValue v, obj.value("children").toArray()) {
|
||||
QJsonObject vo = v.toObject();
|
||||
if (vo.value("name").toString() == name) {
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
void TemporaryWidget::onFinishedGetCanvasList()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
reply->deleteLater();
|
||||
ui->connectButton->setEnabled(true);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "failed to get canvases";
|
||||
// reset some state
|
||||
return;
|
||||
}
|
||||
|
||||
ui->connectButton->hide();
|
||||
ui->canvasSelectCombo->show();
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
|
||||
|
||||
ui->canvasSelectCombo->clear();
|
||||
QJsonArray canvasArray = json.object().value("children").toArray();
|
||||
Q_FOREACH (QJsonValue canvasValue, canvasArray) {
|
||||
QJsonObject canvas = canvasValue.toObject();
|
||||
QString canvasName = jsonPropNodeFindChild(canvas, "name").value("value").toString();
|
||||
QString propPath = canvas.value("path").toString();
|
||||
ui->canvasSelectCombo->addItem(canvasName, propPath);
|
||||
}
|
||||
}
|
||||
|
||||
void TemporaryWidget::onWebSocketConnected()
|
||||
{
|
||||
qDebug() << "connected";
|
||||
connect(&m_webSocket, &QWebSocket::textMessageReceived,
|
||||
this, &TemporaryWidget::onTextMessageReceived);
|
||||
m_webSocket.sendTextMessage(QStringLiteral("Hello, world!"));
|
||||
|
||||
m_localPropertyRoot = new LocalProp(nullptr, NameIndexTuple(""));
|
||||
|
||||
ui->canvas->setRootProperty(m_localPropertyRoot);
|
||||
ui->stack->setCurrentIndex(1);
|
||||
|
||||
ui->canvas->rootElement()->createQuickItem(ui->quickWidget->rootObject());
|
||||
|
||||
m_canvasModel = new CanvasTreeModel(ui->canvas->rootElement());
|
||||
ui->treeView->setModel(m_canvasModel);
|
||||
|
||||
|
@ -85,7 +145,26 @@ void TemporaryWidget::onConnected()
|
|||
ui->portEdit->text().toInt());
|
||||
FGQCanvasImageLoader::instance()->setHost(ui->hostName->text(),
|
||||
ui->portEdit->text().toInt());
|
||||
m_webSocket.sendTextMessage("nonsense");
|
||||
}
|
||||
|
||||
void TemporaryWidget::onCanvasSelected(int index)
|
||||
{
|
||||
QString path = ui->canvasSelectCombo->itemData(index).toString();
|
||||
|
||||
QUrl wsUrl;
|
||||
wsUrl.setScheme("ws");
|
||||
wsUrl.setHost(ui->hostName->text());
|
||||
wsUrl.setPort(ui->portEdit->text().toInt());
|
||||
wsUrl.setPath("/PropertyTreeMirror" + path);
|
||||
m_rootPropertyPath = path.toUtf8();
|
||||
|
||||
connect(&m_webSocket, &QWebSocket::connected, this, &TemporaryWidget::onWebSocketConnected);
|
||||
connect(&m_webSocket, &QWebSocket::disconnected, this, &TemporaryWidget::onSocketClosed);
|
||||
|
||||
saveSettings();
|
||||
|
||||
qDebug() << "starting connection to:" << wsUrl;
|
||||
m_webSocket.open(wsUrl);
|
||||
}
|
||||
|
||||
void TemporaryWidget::onTextMessageReceived(QString message)
|
||||
|
@ -98,12 +177,12 @@ void TemporaryWidget::onTextMessageReceived(QString message)
|
|||
QJsonObject newProp = v.toObject();
|
||||
|
||||
QByteArray nodePath = newProp.value("path").toString().toUtf8();
|
||||
if (nodePath.indexOf(rootPropertyPath) != 0) {
|
||||
if (nodePath.indexOf(m_rootPropertyPath) != 0) {
|
||||
qWarning() << "not a property path we are mirroring:" << nodePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray localPath = nodePath.mid(rootPropertyPath.size() + 1);
|
||||
QByteArray localPath = nodePath.mid(m_rootPropertyPath.size() + 1);
|
||||
LocalProp* newNode = propertyFromPath(localPath);
|
||||
newNode->setPosition(newProp.value("position").toInt());
|
||||
// store in the global dict
|
||||
|
@ -163,7 +242,7 @@ void TemporaryWidget::onSocketClosed()
|
|||
ui->stack->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void TemporaryWidget::onTreeCurrentChanged(const QModelIndex &previous, const QModelIndex ¤t)
|
||||
void TemporaryWidget::onTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
FGCanvasElement* prev = m_canvasModel->elementFromIndex(previous);
|
||||
if (prev) {
|
||||
|
@ -182,7 +261,7 @@ void TemporaryWidget::saveSettings()
|
|||
QSettings settings;
|
||||
settings.setValue("ws-host", ui->hostName->text());
|
||||
settings.setValue("ws-port", ui->portEdit->text());
|
||||
settings.setValue("prop-path", ui->propertyPath->text());
|
||||
//settings.setValue("prop-path", ui->propertyPath->text());
|
||||
}
|
||||
|
||||
void TemporaryWidget::restoreSettings()
|
||||
|
@ -190,7 +269,7 @@ void TemporaryWidget::restoreSettings()
|
|||
QSettings settings;
|
||||
ui->hostName->setText(settings.value("ws-host").toString());
|
||||
ui->portEdit->setText(settings.value("ws-port").toString());
|
||||
ui->propertyPath->setText(settings.value("prop-path").toString());
|
||||
// ui->propertyPath->setText(settings.value("prop-path").toString());
|
||||
}
|
||||
|
||||
LocalProp *TemporaryWidget::propertyFromPath(QByteArray path) const
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//
|
||||
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef TEMPORARYWIDGET_H
|
||||
#define TEMPORARYWIDGET_H
|
||||
|
||||
|
@ -12,6 +29,7 @@ class TemporaryWidget;
|
|||
class LocalProp;
|
||||
class CanvasTreeModel;
|
||||
class ElementDataModel;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class TemporaryWidget : public QWidget
|
||||
{
|
||||
|
@ -21,24 +39,29 @@ public:
|
|||
explicit TemporaryWidget(QWidget *parent = 0);
|
||||
~TemporaryWidget();
|
||||
|
||||
void setNetworkAccess(QNetworkAccessManager* dl);
|
||||
private Q_SLOTS:
|
||||
void onStartConnect();
|
||||
void onConnect();
|
||||
|
||||
void onConnected();
|
||||
void onWebSocketConnected();
|
||||
void onTextMessageReceived(QString message);
|
||||
|
||||
void onSocketClosed();
|
||||
|
||||
void onTreeCurrentChanged(const QModelIndex &previous, const QModelIndex ¤t);
|
||||
void onCanvasSelected(int index);
|
||||
|
||||
void onFinishedGetCanvasList();
|
||||
private:
|
||||
void saveSettings();
|
||||
void restoreSettings();
|
||||
|
||||
LocalProp* propertyFromPath(QByteArray path) const;
|
||||
|
||||
QNetworkAccessManager* m_netAccess;
|
||||
QWebSocket m_webSocket;
|
||||
Ui::TemporaryWidget *ui;
|
||||
QByteArray rootPropertyPath;
|
||||
QByteArray m_rootPropertyPath;
|
||||
|
||||
LocalProp* m_localPropertyRoot = nullptr;
|
||||
QHash<int, LocalProp*> idPropertyDict;
|
||||
|
|
|
@ -23,37 +23,7 @@
|
|||
<attribute name="title">
|
||||
<string>Connect</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,1,0" columnstretch="1,0">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="portEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Enter root property path: (eg '/canvas/by-index/texture[2]/')</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Enter FlightGear host-name and port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="hostName"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLineEdit" name="propertyPath"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,1" columnstretch="1,0">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="minimumSize">
|
||||
|
@ -87,6 +57,39 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Enter FlightGear host-name and port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="portEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="hostName"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="selectCanvasLabel">
|
||||
<property name="text">
|
||||
<string>Select canvas:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="canvasSelectCombo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="stackPage2">
|
||||
|
@ -99,12 +102,31 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Quick</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QQuickWidget" name="quickWidget">
|
||||
<property name="resizeMode">
|
||||
<enum>QQuickWidget::SizeRootObjectToView</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QQuickWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>QtQuickWidgets/QQuickWidget</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>FGCanvasWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
|
|
18
utils/fgqcanvas/text.qml
Normal file
18
utils/fgqcanvas/text.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear 1.0
|
||||
|
||||
CanvasItem {
|
||||
implicitHeight: text.implicitHeight
|
||||
implicitWidth: text.implicitWidth
|
||||
|
||||
property alias text: text.text
|
||||
property alias fontPixelSize: text.font.pixelSize
|
||||
property alias fontFamily: text.font.family
|
||||
property alias color: text.color
|
||||
|
||||
Text {
|
||||
id: text
|
||||
font.pixelSize: 20
|
||||
color: "yellow"
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue