Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include "abstractdeclarative_p.h"
++ #include "declarativetheme_p.h"
++ #include "declarativerendernode_p.h"
++ #include <QtGui/QGuiApplication>
++ #if defined(Q_OS_IOS)
++ #include <QtCore/QTimer>
++ #endif
++ #if defined(Q_OS_MACOS)
++ #include <qpa/qplatformwindow_p.h>
++ #endif
++
++ #if !defined(Q_OS_APPLE) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT)
++ #define USE_SHARED_CONTEXT
++ #else
++ #include "glstatestore_p.h"
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ static QHash<AbstractDeclarative *, QQuickWindow *> graphWindowList;
++
++ AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) :
++ QQuickItem(parent),
++ AbstractDeclarativeInterface(),
++ m_controller(0),
++ m_contextWindow(0),
++ m_renderMode(RenderIndirect),
++ m_samples(0),
++ m_windowSamples(0),
++ m_initialisedSize(0, 0),
++ m_contextOrStateStore(0),
++ m_qtContext(0),
++ m_mainThread(QThread::currentThread()),
++ m_contextThread(0)
++ {
++ m_nodeMutex = QSharedPointer<QMutex>::create();
++
++ connect(this, &QQuickItem::windowChanged, this, &AbstractDeclarative::handleWindowChanged);
++
++ // Set contents to false in case we are in qml designer to make component look nice
++ m_runningInDesigner = QGuiApplication::applicationDisplayName() == QLatin1String("Qml2Puppet");
++ setFlag(ItemHasContents, !m_runningInDesigner);
++
++ // Accept touchevents
++ setAcceptTouchEvents(true);
++ }
++
++ AbstractDeclarative::~AbstractDeclarative()
++ {
++ destroyContext();
++
++ disconnect(this, 0, this, 0);
++ checkWindowList(0);
++
++ // Make sure not deleting locked mutex
++ QMutexLocker locker(&m_mutex);
++ locker.unlock();
++
++ m_nodeMutex.clear();
++ }
++
++ void AbstractDeclarative::setRenderingMode(AbstractDeclarative::RenderingMode mode)
++ {
++ if (mode == m_renderMode
++ || mode <= AbstractDeclarative::RenderingMode::RenderDirectToBackground
++ || mode >= AbstractDeclarative::RenderingMode::RenderIndirect) {
++ return;
++ }
++
++ RenderingMode previousMode = m_renderMode;
++
++ m_renderMode = mode;
++
++ QQuickWindow *win = window();
++
++ switch (mode) {
++ case RenderDirectToBackground:
++ // Intentional flowthrough
++ case RenderDirectToBackground_NoClear:
++ m_initialisedSize = QSize(0, 0);
++ if (previousMode == RenderIndirect) {
++ update();
++ setFlag(ItemHasContents, false);
++ if (win) {
++ QObject::connect(win, &QQuickWindow::beforeRenderPassRecording, this,
++ &AbstractDeclarative::render, Qt::DirectConnection);
++ checkWindowList(win);
++ setAntialiasing(m_windowSamples > 0);
++ if (m_windowSamples != m_samples)
++ emit msaaSamplesChanged(m_windowSamples);
++ }
++ }
++ break;
++ case RenderIndirect:
++ m_initialisedSize = QSize(0, 0);
++ setFlag(ItemHasContents, !m_runningInDesigner);
++ update();
++ if (win) {
++ QObject::disconnect(win, &QQuickWindow::beforeRenderPassRecording, this,
++ &AbstractDeclarative::render);
++ checkWindowList(win);
++ }
++ setAntialiasing(m_samples > 0);
++ if (m_windowSamples != m_samples)
++ emit msaaSamplesChanged(m_samples);
++ break;
++ }
++
++ updateWindowParameters();
++
++ emit renderingModeChanged(mode);
++ }
++
++ AbstractDeclarative::RenderingMode AbstractDeclarative::renderingMode() const
++ {
++ return m_renderMode;
++ }
++
++ QSGNode *AbstractDeclarative::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
++ {
++ QSize boundingSize = boundingRect().size().toSize() * m_controller->scene()->devicePixelRatio();
++ if (m_runningInDesigner || boundingSize.width() <= 0 || boundingSize.height() <= 0
++ || m_controller.isNull() || !window()) {
++ delete oldNode;
++ return 0;
++ }
++ DeclarativeRenderNode *node = static_cast<DeclarativeRenderNode *>(oldNode);
++
++ if (!node) {
++ node = new DeclarativeRenderNode(this, m_nodeMutex);
++ node->setController(m_controller.data());
++ node->setQuickWindow(window());
++ }
++
++ node->setSize(boundingSize);
++ node->setSamples(m_samples);
++ node->update();
++ node->markDirty(QSGNode::DirtyMaterial);
++
++ return node;
++ }
++
++ Declarative3DScene* AbstractDeclarative::scene() const
++ {
++ return static_cast<Declarative3DScene *>(m_controller->scene());
++ }
++
++ void AbstractDeclarative::setTheme(Q3DTheme *theme)
++ {
++ m_controller->setActiveTheme(theme, isComponentComplete());
++ }
++
++ Q3DTheme *AbstractDeclarative::theme() const
++ {
++ return m_controller->activeTheme();
++ }
++
++ void AbstractDeclarative::clearSelection()
++ {
++ m_controller->clearSelection();
++ }
++
++ bool AbstractDeclarative::hasSeries(QAbstract3DSeries *series)
++ {
++ return m_controller->hasSeries(series);
++ }
++
++ void AbstractDeclarative::setSelectionMode(SelectionFlags mode)
++ {
++ int intmode = int(mode);
++ m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode));
++ }
++
++ AbstractDeclarative::SelectionFlags AbstractDeclarative::selectionMode() const
++ {
++ int intmode = int(m_controller->selectionMode());
++ return SelectionFlags(intmode);
++ }
++
++ void AbstractDeclarative::setShadowQuality(ShadowQuality quality)
++ {
++ m_controller->setShadowQuality(QAbstract3DGraph::ShadowQuality(quality));
++ }
++
++ AbstractDeclarative::ShadowQuality AbstractDeclarative::shadowQuality() const
++ {
++ return ShadowQuality(m_controller->shadowQuality());
++ }
++
++ bool AbstractDeclarative::shadowsSupported() const
++ {
++ return m_controller->shadowsSupported();
++ }
++
++ int AbstractDeclarative::addCustomItem(QCustom3DItem *item)
++ {
++ return m_controller->addCustomItem(item);
++ }
++
++ void AbstractDeclarative::removeCustomItems()
++ {
++ m_controller->deleteCustomItems();
++ }
++
++ void AbstractDeclarative::removeCustomItem(QCustom3DItem *item)
++ {
++ m_controller->deleteCustomItem(item);
++ }
++
++ void AbstractDeclarative::removeCustomItemAt(const QVector3D &position)
++ {
++ m_controller->deleteCustomItem(position);
++ }
++
++ void AbstractDeclarative::releaseCustomItem(QCustom3DItem *item)
++ {
++ m_controller->releaseCustomItem(item);
++ }
++
++ int AbstractDeclarative::selectedLabelIndex() const
++ {
++ return m_controller->selectedLabelIndex();
++ }
++
++ QAbstract3DAxis *AbstractDeclarative::selectedAxis() const
++ {
++ return m_controller->selectedAxis();
++ }
++
++ int AbstractDeclarative::selectedCustomItemIndex() const
++ {
++ return m_controller->selectedCustomItemIndex();
++ }
++
++ QCustom3DItem *AbstractDeclarative::selectedCustomItem() const
++ {
++ return m_controller->selectedCustomItem();
++ }
++
++ QQmlListProperty<QCustom3DItem> AbstractDeclarative::customItemList()
++ {
++ return QQmlListProperty<QCustom3DItem>(this, this,
++ &AbstractDeclarative::appendCustomItemFunc,
++ &AbstractDeclarative::countCustomItemFunc,
++ &AbstractDeclarative::atCustomItemFunc,
++ &AbstractDeclarative::clearCustomItemFunc);
++ }
++
++ void AbstractDeclarative::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
++ QCustom3DItem *item)
++ {
++ AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data);
++ decl->addCustomItem(item);
++ }
++
++ qsizetype AbstractDeclarative::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
++ {
++ return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.size();
++ }
++
++ QCustom3DItem *AbstractDeclarative::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list,
++ qsizetype index)
++ {
++ return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.at(index);
++ }
++
++ void AbstractDeclarative::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list)
++ {
++ AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data);
++ decl->removeCustomItems();
++ }
++
++ void AbstractDeclarative::setSharedController(Abstract3DController *controller)
++ {
++ Q_ASSERT(controller);
++ m_controller = controller;
++ m_controller->m_qml = this;
++
++ if (!m_controller->isOpenGLES())
++ m_samples = 4;
++ setAntialiasing(m_samples > 0);
++
++ // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D.
++ DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D;
++ defaultTheme->d_ptr->setDefaultTheme(true);
++ defaultTheme->setType(Q3DTheme::ThemeQt);
++ m_controller->setActiveTheme(defaultTheme);
++
++ QObject::connect(m_controller.data(), &Abstract3DController::shadowQualityChanged, this,
++ &AbstractDeclarative::handleShadowQualityChange);
++ QObject::connect(m_controller.data(), &Abstract3DController::activeInputHandlerChanged, this,
++ &AbstractDeclarative::inputHandlerChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::activeThemeChanged, this,
++ &AbstractDeclarative::themeChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::selectionModeChanged, this,
++ &AbstractDeclarative::handleSelectionModeChange);
++ QObject::connect(m_controller.data(), &Abstract3DController::elementSelected, this,
++ &AbstractDeclarative::handleSelectedElementChange);
++
++ QObject::connect(m_controller.data(), &Abstract3DController::axisXChanged, this,
++ &AbstractDeclarative::handleAxisXChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::axisYChanged, this,
++ &AbstractDeclarative::handleAxisYChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::axisZChanged, this,
++ &AbstractDeclarative::handleAxisZChanged);
++
++ QObject::connect(m_controller.data(), &Abstract3DController::measureFpsChanged, this,
++ &AbstractDeclarative::measureFpsChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::currentFpsChanged, this,
++ &AbstractDeclarative::currentFpsChanged);
++
++ QObject::connect(m_controller.data(), &Abstract3DController::orthoProjectionChanged, this,
++ &AbstractDeclarative::orthoProjectionChanged);
++
++ QObject::connect(m_controller.data(), &Abstract3DController::aspectRatioChanged, this,
++ &AbstractDeclarative::aspectRatioChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::optimizationHintsChanged, this,
++ &AbstractDeclarative::handleOptimizationHintChange);
++ QObject::connect(m_controller.data(), &Abstract3DController::polarChanged, this,
++ &AbstractDeclarative::polarChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::radialLabelOffsetChanged, this,
++ &AbstractDeclarative::radialLabelOffsetChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::horizontalAspectRatioChanged, this,
++ &AbstractDeclarative::horizontalAspectRatioChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::reflectionChanged, this,
++ &AbstractDeclarative::reflectionChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::reflectivityChanged, this,
++ &AbstractDeclarative::reflectivityChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::localeChanged, this,
++ &AbstractDeclarative::localeChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::queriedGraphPositionChanged, this,
++ &AbstractDeclarative::queriedGraphPositionChanged);
++ QObject::connect(m_controller.data(), &Abstract3DController::marginChanged, this,
++ &AbstractDeclarative::marginChanged);
++ }
++
++ void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window)
++ {
++ // We can assume we are not in middle of AbstractDeclarative destructor when we are here,
++ // since m_context creation is always done when this function is called from
++ // synchDataToRenderer(), which blocks main thread -> no need to mutex.
++ if (!m_contextOrStateStore || !m_qtContext || m_contextWindow != window) {
++ QOpenGLContext *currentContext = QOpenGLContext::currentContext();
++
++ // Note: Changing graph to different window when using multithreaded renderer will break!
++
++ delete m_contextOrStateStore;
++
++ m_contextThread = QThread::currentThread();
++ m_contextWindow = window;
++ m_qtContext = currentContext;
++
++ #ifdef USE_SHARED_CONTEXT
++ m_context = new QOpenGLContext();
++ m_context->setFormat(m_qtContext->format());
++ m_context->setShareContext(m_qtContext);
++ m_context->create();
++ m_context->makeCurrent(window);
++ #else
++ // Shared contexts don't work properly in some platforms, so just store the
++ // context state on those
++ m_stateStore = new GLStateStore(QOpenGLContext::currentContext());
++ m_stateStore->storeGLState();
++ #endif
++ m_controller->initializeOpenGL();
++
++ // Make sure context / state store gets deleted.
++ QObject::connect(m_contextThread, &QThread::finished, this,
++ &AbstractDeclarative::destroyContext, Qt::DirectConnection);
++ } else {
++ #ifdef USE_SHARED_CONTEXT
++ m_context->makeCurrent(window);
++ #else
++ m_stateStore->storeGLState();
++ #endif
++ }
++ }
++
++ void AbstractDeclarative::doneOpenGLContext(QQuickWindow *window)
++ {
++ #ifdef USE_SHARED_CONTEXT
++ m_qtContext->makeCurrent(window);
++ #else
++ Q_UNUSED(window);
++ m_stateStore->restoreGLState();
++ #endif
++ }
++
++ void AbstractDeclarative::synchDataToRenderer()
++ {
++ QQuickWindow *win = window();
++ activateOpenGLContext(win);
++ m_controller->synchDataToRenderer();
++ doneOpenGLContext(win);
++ }
++
++ int AbstractDeclarative::msaaSamples() const
++ {
++ if (m_renderMode == RenderIndirect)
++ return m_samples;
++ else
++ return m_windowSamples;
++ }
++
++ void AbstractDeclarative::setMsaaSamples(int samples)
++ {
++ if (m_renderMode != RenderIndirect) {
++ qWarning("Multisampling cannot be adjusted in this render mode");
++ } else {
++ if (m_controller->isOpenGLES()) {
++ if (samples > 0)
++ qWarning("Multisampling is not supported in OpenGL ES2");
++ } else if (m_samples != samples) {
++ m_samples = samples;
++ setAntialiasing(m_samples > 0);
++ emit msaaSamplesChanged(samples);
++ update();
++ }
++ }
++ }
++
++ void AbstractDeclarative::handleWindowChanged(QQuickWindow *window)
++ {
++ checkWindowList(window);
++ if (!window)
++ return;
++
++ #if defined(Q_OS_MACOS)
++ bool previousVisibility = window->isVisible();
++ // Enable touch events for Mac touchpads
++ window->setVisible(true);
++ if (auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
++ cocoaWindow->enableTrackpadTouchDelivery(true);
++ window->setVisible(previousVisibility);
++ #endif
++
++ connect(window, &QObject::destroyed, this, &AbstractDeclarative::windowDestroyed);
++
++ int oldWindowSamples = m_windowSamples;
++ m_windowSamples = window->format().samples();
++ if (m_windowSamples < 0)
++ m_windowSamples = 0;
++
++ connect(window, &QQuickWindow::beforeSynchronizing,
++ this, &AbstractDeclarative::synchDataToRenderer,
++ Qt::DirectConnection);
++
++ if (m_renderMode == RenderDirectToBackground_NoClear
++ || m_renderMode == RenderDirectToBackground) {
++ connect(window, &QQuickWindow::beforeRenderPassRecording, this, &AbstractDeclarative::render,
++ Qt::DirectConnection);
++ setAntialiasing(m_windowSamples > 0);
++ if (m_windowSamples != oldWindowSamples)
++ emit msaaSamplesChanged(m_windowSamples);
++ }
++
++ connect(m_controller.data(), &Abstract3DController::needRender, window, &QQuickWindow::update);
++
++ updateWindowParameters();
++
++ #if defined(Q_OS_IOS)
++ // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal.
++ // This ensures we don't end up displaying the graph without any data, in case update is
++ // skipped after synchDataToRenderer.
++ QTimer::singleShot(0, window, SLOT(update()));
++ #endif
++ }
++
++ void AbstractDeclarative::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
++ {
++ QQuickItem::geometryChange(newGeometry, oldGeometry);
++
++ m_cachedGeometry = newGeometry;
++
++ updateWindowParameters();
++ }
++
++ void AbstractDeclarative::itemChange(ItemChange change, const ItemChangeData & value)
++ {
++ QQuickItem::itemChange(change, value);
++ updateWindowParameters();
++ }
++
++ void AbstractDeclarative::updateWindowParameters()
++ {
++ const QMutexLocker locker(&m_mutex);
++
++ // Update the device pixel ratio, window size and bounding box
++ QQuickWindow *win = window();
++ if (win && !m_controller.isNull()) {
++ Q3DScene *scene = m_controller->scene();
++ if (win->devicePixelRatio() != scene->devicePixelRatio()) {
++ scene->setDevicePixelRatio(win->devicePixelRatio());
++ win->update();
++ }
++
++ bool directRender = m_renderMode == RenderDirectToBackground
++ || m_renderMode == RenderDirectToBackground_NoClear;
++ QSize windowSize;
++
++ if (directRender)
++ windowSize = win->size();
++ else
++ windowSize = m_cachedGeometry.size().toSize();
++
++ if (windowSize != scene->d_ptr->windowSize()) {
++ scene->d_ptr->setWindowSize(windowSize);
++ win->update();
++ }
++
++ if (directRender) {
++ // Origin mapping is needed when rendering directly to background
++ QPointF point = QQuickItem::mapToScene(QPointF(0.0, 0.0));
++ scene->d_ptr->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f,
++ m_cachedGeometry.width() + 0.5f,
++ m_cachedGeometry.height() + 0.5f));
++ } else {
++ // No translation needed when rendering to FBO
++ scene->d_ptr->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f,
++ m_cachedGeometry.height() + 0.5f));
++ }
++ }
++ }
++
++ void AbstractDeclarative::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode)
++ {
++ int intmode = int(mode);
++ emit selectionModeChanged(SelectionFlags(intmode));
++ }
++
++ void AbstractDeclarative::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality)
++ {
++ emit shadowQualityChanged(ShadowQuality(quality));
++ }
++
++ void AbstractDeclarative::handleSelectedElementChange(QAbstract3DGraph::ElementType type)
++ {
++ emit selectedElementChanged(ElementType(type));
++ }
++
++ void AbstractDeclarative::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints)
++ {
++ int intHints = int(hints);
++ emit optimizationHintsChanged(OptimizationHints(intHints));
++ }
++
++ void AbstractDeclarative::render()
++ {
++ updateWindowParameters();
++
++ // If we're not rendering directly to the background, return
++ if (m_renderMode != RenderDirectToBackground && m_renderMode != RenderDirectToBackground_NoClear)
++ return;
++
++ // Clear the background once per window as that is not done by default
++ QQuickWindow *win = window();
++ win->beginExternalCommands();
++ activateOpenGLContext(win);
++
++ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
++
++ if (funcs && isVisible()) {
++ funcs->glDepthMask(GL_TRUE);
++ funcs->glEnable(GL_DEPTH_TEST);
++ funcs->glDepthFunc(GL_LESS);
++ funcs->glEnable(GL_CULL_FACE);
++ funcs->glCullFace(GL_BACK);
++ funcs->glDisable(GL_BLEND);
++
++ m_controller->render();
++
++ funcs->glEnable(GL_BLEND);
++ }
++
++ doneOpenGLContext(win);
++ win->endExternalCommands();
++ }
++
++ QAbstract3DInputHandler* AbstractDeclarative::inputHandler() const
++ {
++ return m_controller->activeInputHandler();
++ }
++
++ void AbstractDeclarative::setInputHandler(QAbstract3DInputHandler *inputHandler)
++ {
++ m_controller->setActiveInputHandler(inputHandler);
++ }
++
++ void AbstractDeclarative::mouseDoubleClickEvent(QMouseEvent *event)
++ {
++ m_controller->mouseDoubleClickEvent(event);
++ }
++
++ void AbstractDeclarative::touchEvent(QTouchEvent *event)
++ {
++ m_controller->touchEvent(event);
++ window()->update();
++ }
++
++ void AbstractDeclarative::mousePressEvent(QMouseEvent *event)
++ {
++ QPoint mousePos = event->pos();
++ m_controller->mousePressEvent(event, mousePos);
++ }
++
++ void AbstractDeclarative::mouseReleaseEvent(QMouseEvent *event)
++ {
++ QPoint mousePos = event->pos();
++ m_controller->mouseReleaseEvent(event, mousePos);
++ }
++
++ void AbstractDeclarative::mouseMoveEvent(QMouseEvent *event)
++ {
++ QPoint mousePos = event->pos();
++ m_controller->mouseMoveEvent(event, mousePos);
++ }
++
++ #if QT_CONFIG(wheelevent)
++ void AbstractDeclarative::wheelEvent(QWheelEvent *event)
++ {
++ m_controller->wheelEvent(event);
++ }
++ #endif
++
++ void AbstractDeclarative::checkWindowList(QQuickWindow *window)
++ {
++ QQuickWindow *oldWindow = graphWindowList.value(this);
++ graphWindowList[this] = window;
++
++ if (oldWindow != window && oldWindow) {
++ QObject::disconnect(oldWindow, &QObject::destroyed, this,
++ &AbstractDeclarative::windowDestroyed);
++ QObject::disconnect(oldWindow, &QQuickWindow::beforeSynchronizing, this,
++ &AbstractDeclarative::synchDataToRenderer);
++ QObject::disconnect(oldWindow, &QQuickWindow::beforeRenderPassRecording, this,
++ &AbstractDeclarative::render);
++ if (!m_controller.isNull()) {
++ QObject::disconnect(m_controller.data(), &Abstract3DController::needRender,
++ oldWindow, &QQuickWindow::update);
++ }
++ }
++
++ QList<QQuickWindow *> windowList;
++ const auto graphs = graphWindowList.keys();
++ for (AbstractDeclarative *graph : graphs) {
++ if (graph->m_renderMode == RenderDirectToBackground
++ || graph->m_renderMode == RenderDirectToBackground_NoClear) {
++ windowList.append(graphWindowList.value(graph));
++ }
++ }
++
++ if (!window) {
++ graphWindowList.remove(this);
++ return;
++ }
++ }
++
++ void AbstractDeclarative::setMeasureFps(bool enable)
++ {
++ m_controller->setMeasureFps(enable);
++ }
++
++ bool AbstractDeclarative::measureFps() const
++ {
++ return m_controller->measureFps();
++ }
++
++ qreal AbstractDeclarative::currentFps() const
++ {
++ return m_controller->currentFps();
++ }
++
++ void AbstractDeclarative::setOrthoProjection(bool enable)
++ {
++ m_controller->setOrthoProjection(enable);
++ }
++
++ bool AbstractDeclarative::isOrthoProjection() const
++ {
++ return m_controller->isOrthoProjection();
++ }
++
++ AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const
++ {
++ return ElementType(m_controller->selectedElement());
++ }
++
++ void AbstractDeclarative::setAspectRatio(qreal ratio)
++ {
++ m_controller->setAspectRatio(ratio);
++ }
++
++ qreal AbstractDeclarative::aspectRatio() const
++ {
++ return m_controller->aspectRatio();
++ }
++
++ void AbstractDeclarative::setOptimizationHints(OptimizationHints hints)
++ {
++ int intmode = int(hints);
++ m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode));
++ }
++
++ AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints() const
++ {
++ int intmode = int(m_controller->optimizationHints());
++ return OptimizationHints(intmode);
++ }
++
++ void AbstractDeclarative::setPolar(bool enable)
++ {
++ m_controller->setPolar(enable);
++ }
++
++ bool AbstractDeclarative::isPolar() const
++ {
++ return m_controller->isPolar();
++ }
++
++ void AbstractDeclarative::setRadialLabelOffset(float offset)
++ {
++ m_controller->setRadialLabelOffset(offset);
++ }
++
++ float AbstractDeclarative::radialLabelOffset() const
++ {
++ return m_controller->radialLabelOffset();
++ }
++
++ void AbstractDeclarative::setHorizontalAspectRatio(qreal ratio)
++ {
++ m_controller->setHorizontalAspectRatio(ratio);
++ }
++
++ qreal AbstractDeclarative::horizontalAspectRatio() const
++ {
++ return m_controller->horizontalAspectRatio();
++ }
++
++ void AbstractDeclarative::setReflection(bool enable)
++ {
++ m_controller->setReflection(enable);
++ }
++
++ bool AbstractDeclarative::isReflection() const
++ {
++ return m_controller->reflection();
++ }
++
++ void AbstractDeclarative::setReflectivity(qreal reflectivity)
++ {
++ m_controller->setReflectivity(reflectivity);
++ }
++
++ qreal AbstractDeclarative::reflectivity() const
++ {
++ return m_controller->reflectivity();
++ }
++
++ void AbstractDeclarative::setLocale(const QLocale &locale)
++ {
++ m_controller->setLocale(locale);
++ }
++
++ QLocale AbstractDeclarative::locale() const
++ {
++ return m_controller->locale();
++ }
++
++ QVector3D AbstractDeclarative::queriedGraphPosition() const
++ {
++ return m_controller->queriedGraphPosition();
++ }
++
++ void AbstractDeclarative::setMargin(qreal margin)
++ {
++ m_controller->setMargin(margin);
++ }
++
++ qreal AbstractDeclarative::margin() const
++ {
++ return m_controller->margin();
++ }
++
++ void AbstractDeclarative::windowDestroyed(QObject *obj)
++ {
++ // Remove destroyed window from window lists
++ QQuickWindow *win = static_cast<QQuickWindow *>(obj);
++ QQuickWindow *oldWindow = graphWindowList.value(this);
++
++ if (win == oldWindow)
++ graphWindowList.remove(this);
++ }
++
++ void AbstractDeclarative::destroyContext()
++ {
++ if (m_contextThread && m_contextThread != m_mainThread) {
++ if (m_contextOrStateStore)
++ m_contextOrStateStore->deleteLater();
++ } else {
++ delete m_contextOrStateStore;
++ }
++ m_contextOrStateStore = 0;
++
++ if (m_contextThread) {
++ QObject::disconnect(m_contextThread, &QThread::finished, this,
++ &AbstractDeclarative::destroyContext);
++ m_contextThread = 0;
++ }
++ }
++
++ QT_END_NAMESPACE