| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the Qt Data Visualization module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 or (at your option) any later version | 
| 20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by | 
| 21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 | 
| 22 | ** included in the packaging of this file. Please review the following | 
| 23 | ** information to ensure the GNU General Public License requirements will | 
| 24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 25 | ** | 
| 26 | ** $QT_END_LICENSE$ | 
| 27 | ** | 
| 28 | ****************************************************************************/ | 
| 29 |  | 
| 30 | #include "abstractdeclarative_p.h" | 
| 31 | #include "declarativetheme_p.h" | 
| 32 | #include "declarativerendernode_p.h" | 
| 33 | #include <QtGui/QGuiApplication> | 
| 34 | #if defined(Q_OS_IOS) | 
| 35 | #include <QtCore/QTimer> | 
| 36 | #endif | 
| 37 | #if defined(Q_OS_OSX) | 
| 38 | #include <qpa/qplatformnativeinterface.h> | 
| 39 | #endif | 
| 40 |  | 
| 41 | #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) | 
| 42 | #define USE_SHARED_CONTEXT | 
| 43 | #else | 
| 44 | #include "glstatestore_p.h" | 
| 45 | #endif | 
| 46 |  | 
| 47 | QT_BEGIN_NAMESPACE_DATAVISUALIZATION | 
| 48 |  | 
| 49 | static QList<const QQuickWindow *> clearList; | 
| 50 | static QHash<AbstractDeclarative *, QQuickWindow *> graphWindowList; | 
| 51 | static QHash<QQuickWindow *, bool> windowClearList; | 
| 52 |  | 
| 53 | AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) : | 
| 54 |     QQuickItem(parent), | 
| 55 |     m_controller(0), | 
| 56 |     m_contextWindow(0), | 
| 57 |     m_renderMode(RenderIndirect), | 
| 58 |     m_samples(0), | 
| 59 |     m_windowSamples(0), | 
| 60 |     m_initialisedSize(0, 0), | 
| 61 |     m_contextOrStateStore(0), | 
| 62 |     m_qtContext(0), | 
| 63 |     m_mainThread(QThread::currentThread()), | 
| 64 |     m_contextThread(0) | 
| 65 | { | 
| 66 |     m_nodeMutex = QSharedPointer<QMutex>::create(); | 
| 67 |  | 
| 68 |     connect(sender: this, signal: &QQuickItem::windowChanged, receiver: this, slot: &AbstractDeclarative::handleWindowChanged); | 
| 69 |  | 
| 70 |     // Set contents to false in case we are in qml designer to make component look nice | 
| 71 |     m_runningInDesigner = QGuiApplication::applicationDisplayName() == "Qml2Puppet" ; | 
| 72 |     setFlag(flag: ItemHasContents, enabled: !m_runningInDesigner); | 
| 73 | } | 
| 74 |  | 
| 75 | AbstractDeclarative::~AbstractDeclarative() | 
| 76 | { | 
| 77 |     destroyContext(); | 
| 78 |  | 
| 79 |     disconnect(sender: this, signal: 0, receiver: this, member: 0); | 
| 80 |     checkWindowList(window: 0); | 
| 81 |  | 
| 82 |     // Make sure not deleting locked mutex | 
| 83 |     QMutexLocker locker(&m_mutex); | 
| 84 |     locker.unlock(); | 
| 85 |  | 
| 86 |     m_nodeMutex.clear(); | 
| 87 | } | 
| 88 |  | 
| 89 | void AbstractDeclarative::setRenderingMode(AbstractDeclarative::RenderingMode mode) | 
| 90 | { | 
| 91 |     if (mode == m_renderMode) | 
| 92 |         return; | 
| 93 |  | 
| 94 |     RenderingMode previousMode = m_renderMode; | 
| 95 |  | 
| 96 |     m_renderMode = mode; | 
| 97 |  | 
| 98 |     QQuickWindow *win = window(); | 
| 99 |  | 
| 100 |     switch (mode) { | 
| 101 |     case RenderDirectToBackground: | 
| 102 |         // Intentional flowthrough | 
| 103 |     case RenderDirectToBackground_NoClear: | 
| 104 |         m_initialisedSize = QSize(0, 0); | 
| 105 |         if (previousMode == RenderIndirect) { | 
| 106 |             update(); | 
| 107 |             setFlag(flag: ItemHasContents, enabled: false); | 
| 108 |             if (win) { | 
| 109 |                 QObject::connect(sender: win, signal: &QQuickWindow::beforeRendering, receiver: this, | 
| 110 |                                  slot: &AbstractDeclarative::render, type: Qt::DirectConnection); | 
| 111 |                 checkWindowList(window: win); | 
| 112 |                 setAntialiasing(m_windowSamples > 0); | 
| 113 |                 if (m_windowSamples != m_samples) | 
| 114 |                     emit msaaSamplesChanged(samples: m_windowSamples); | 
| 115 |             } | 
| 116 |         } | 
| 117 |         break; | 
| 118 |     case RenderIndirect: | 
| 119 |         m_initialisedSize = QSize(0, 0); | 
| 120 |         setFlag(flag: ItemHasContents, enabled: !m_runningInDesigner); | 
| 121 |         update(); | 
| 122 |         if (win) { | 
| 123 |             QObject::disconnect(sender: win, signal: &QQuickWindow::beforeRendering, receiver: this, | 
| 124 |                                 slot: &AbstractDeclarative::render); | 
| 125 |             checkWindowList(window: win); | 
| 126 |         } | 
| 127 |         setAntialiasing(m_samples > 0); | 
| 128 |         if (m_windowSamples != m_samples) | 
| 129 |             emit msaaSamplesChanged(samples: m_samples); | 
| 130 |         break; | 
| 131 |     } | 
| 132 |  | 
| 133 |     updateWindowParameters(); | 
| 134 |  | 
| 135 |     emit renderingModeChanged(mode); | 
| 136 | } | 
| 137 |  | 
| 138 | AbstractDeclarative::RenderingMode AbstractDeclarative::renderingMode() const | 
| 139 | { | 
| 140 |     return m_renderMode; | 
| 141 | } | 
| 142 |  | 
| 143 | QSGNode *AbstractDeclarative::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) | 
| 144 | { | 
| 145 |     QSize boundingSize = boundingRect().size().toSize() * m_controller->scene()->devicePixelRatio(); | 
| 146 |     if (m_runningInDesigner || boundingSize.width() <= 0 || boundingSize.height() <= 0 | 
| 147 |             || m_controller.isNull() || !window()) { | 
| 148 |         delete oldNode; | 
| 149 |         return 0; | 
| 150 |     } | 
| 151 |     DeclarativeRenderNode *node = static_cast<DeclarativeRenderNode *>(oldNode); | 
| 152 |  | 
| 153 |     if (!node) { | 
| 154 |         node = new DeclarativeRenderNode(this, m_nodeMutex); | 
| 155 |         node->setController(m_controller.data()); | 
| 156 |         node->setQuickWindow(window()); | 
| 157 |     } | 
| 158 |  | 
| 159 |     node->setSize(boundingSize); | 
| 160 |     node->setSamples(m_samples); | 
| 161 |     node->update(); | 
| 162 |     node->markDirty(bits: QSGNode::DirtyMaterial); | 
| 163 |  | 
| 164 |     return node; | 
| 165 | } | 
| 166 |  | 
| 167 | Declarative3DScene* AbstractDeclarative::scene() const | 
| 168 | { | 
| 169 |     return static_cast<Declarative3DScene *>(m_controller->scene()); | 
| 170 | } | 
| 171 |  | 
| 172 | void AbstractDeclarative::setTheme(Q3DTheme *theme) | 
| 173 | { | 
| 174 |     m_controller->setActiveTheme(theme, force: isComponentComplete()); | 
| 175 | } | 
| 176 |  | 
| 177 | Q3DTheme *AbstractDeclarative::theme() const | 
| 178 | { | 
| 179 |     return m_controller->activeTheme(); | 
| 180 | } | 
| 181 |  | 
| 182 | void AbstractDeclarative::clearSelection() | 
| 183 | { | 
| 184 |     m_controller->clearSelection(); | 
| 185 | } | 
| 186 |  | 
| 187 | void AbstractDeclarative::setSelectionMode(SelectionFlags mode) | 
| 188 | { | 
| 189 |     int intmode = int(mode); | 
| 190 |     m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode)); | 
| 191 | } | 
| 192 |  | 
| 193 | AbstractDeclarative::SelectionFlags AbstractDeclarative::selectionMode() const | 
| 194 | { | 
| 195 |     int intmode = int(m_controller->selectionMode()); | 
| 196 |     return SelectionFlags(intmode); | 
| 197 | } | 
| 198 |  | 
| 199 | void AbstractDeclarative::setShadowQuality(ShadowQuality quality) | 
| 200 | { | 
| 201 |     m_controller->setShadowQuality(QAbstract3DGraph::ShadowQuality(quality)); | 
| 202 | } | 
| 203 |  | 
| 204 | AbstractDeclarative::ShadowQuality AbstractDeclarative::shadowQuality() const | 
| 205 | { | 
| 206 |     return ShadowQuality(m_controller->shadowQuality()); | 
| 207 | } | 
| 208 |  | 
| 209 | bool AbstractDeclarative::shadowsSupported() const | 
| 210 | { | 
| 211 |     return m_controller->shadowsSupported(); | 
| 212 | } | 
| 213 |  | 
| 214 | int AbstractDeclarative::addCustomItem(QCustom3DItem *item) | 
| 215 | { | 
| 216 |     return m_controller->addCustomItem(item); | 
| 217 | } | 
| 218 |  | 
| 219 | void AbstractDeclarative::removeCustomItems() | 
| 220 | { | 
| 221 |     m_controller->deleteCustomItems(); | 
| 222 | } | 
| 223 |  | 
| 224 | void AbstractDeclarative::removeCustomItem(QCustom3DItem *item) | 
| 225 | { | 
| 226 |     m_controller->deleteCustomItem(item); | 
| 227 | } | 
| 228 |  | 
| 229 | void AbstractDeclarative::removeCustomItemAt(const QVector3D &position) | 
| 230 | { | 
| 231 |     m_controller->deleteCustomItem(position); | 
| 232 | } | 
| 233 |  | 
| 234 | void AbstractDeclarative::releaseCustomItem(QCustom3DItem *item) | 
| 235 | { | 
| 236 |     m_controller->releaseCustomItem(item); | 
| 237 | } | 
| 238 |  | 
| 239 | int AbstractDeclarative::selectedLabelIndex() const | 
| 240 | { | 
| 241 |     return m_controller->selectedLabelIndex(); | 
| 242 | } | 
| 243 |  | 
| 244 | QAbstract3DAxis *AbstractDeclarative::selectedAxis() const | 
| 245 | { | 
| 246 |     return m_controller->selectedAxis(); | 
| 247 | } | 
| 248 |  | 
| 249 | int AbstractDeclarative::selectedCustomItemIndex() const | 
| 250 | { | 
| 251 |     return m_controller->selectedCustomItemIndex(); | 
| 252 | } | 
| 253 |  | 
| 254 | QCustom3DItem *AbstractDeclarative::selectedCustomItem() const | 
| 255 | { | 
| 256 |     return m_controller->selectedCustomItem(); | 
| 257 | } | 
| 258 |  | 
| 259 | QQmlListProperty<QCustom3DItem> AbstractDeclarative::customItemList() | 
| 260 | { | 
| 261 |     return QQmlListProperty<QCustom3DItem>(this, this, | 
| 262 |                                            &AbstractDeclarative::appendCustomItemFunc, | 
| 263 |                                            &AbstractDeclarative::countCustomItemFunc, | 
| 264 |                                            &AbstractDeclarative::atCustomItemFunc, | 
| 265 |                                            &AbstractDeclarative::clearCustomItemFunc); | 
| 266 | } | 
| 267 |  | 
| 268 | void AbstractDeclarative::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, | 
| 269 |                                                QCustom3DItem *item) | 
| 270 | { | 
| 271 |     AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data); | 
| 272 |     decl->addCustomItem(item); | 
| 273 | } | 
| 274 |  | 
| 275 | int AbstractDeclarative::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) | 
| 276 | { | 
| 277 |     return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.size(); | 
| 278 | } | 
| 279 |  | 
| 280 | QCustom3DItem *AbstractDeclarative::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, | 
| 281 |                                                      int index) | 
| 282 | { | 
| 283 |     return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.at(i: index); | 
| 284 | } | 
| 285 |  | 
| 286 | void AbstractDeclarative::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) | 
| 287 | { | 
| 288 |     AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data); | 
| 289 |     decl->removeCustomItems(); | 
| 290 | } | 
| 291 |  | 
| 292 | void AbstractDeclarative::setSharedController(Abstract3DController *controller) | 
| 293 | { | 
| 294 |     Q_ASSERT(controller); | 
| 295 |     m_controller = controller; | 
| 296 |  | 
| 297 |     if (!m_controller->isOpenGLES()) | 
| 298 |         m_samples = 4; | 
| 299 |     setAntialiasing(m_samples > 0); | 
| 300 |  | 
| 301 |     // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D. | 
| 302 |     DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D; | 
| 303 |     defaultTheme->d_ptr->setDefaultTheme(true); | 
| 304 |     defaultTheme->setType(Q3DTheme::ThemeQt); | 
| 305 |     m_controller->setActiveTheme(theme: defaultTheme); | 
| 306 |  | 
| 307 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::shadowQualityChanged, receiver: this, | 
| 308 |                      slot: &AbstractDeclarative::handleShadowQualityChange); | 
| 309 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeInputHandlerChanged, receiver: this, | 
| 310 |                      slot: &AbstractDeclarative::inputHandlerChanged); | 
| 311 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeThemeChanged, receiver: this, | 
| 312 |                      slot: &AbstractDeclarative::themeChanged); | 
| 313 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::selectionModeChanged, receiver: this, | 
| 314 |                      slot: &AbstractDeclarative::handleSelectionModeChange); | 
| 315 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::elementSelected, receiver: this, | 
| 316 |                      slot: &AbstractDeclarative::handleSelectedElementChange); | 
| 317 |  | 
| 318 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisXChanged, receiver: this, | 
| 319 |                      slot: &AbstractDeclarative::handleAxisXChanged); | 
| 320 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisYChanged, receiver: this, | 
| 321 |                      slot: &AbstractDeclarative::handleAxisYChanged); | 
| 322 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisZChanged, receiver: this, | 
| 323 |                      slot: &AbstractDeclarative::handleAxisZChanged); | 
| 324 |  | 
| 325 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::measureFpsChanged, receiver: this, | 
| 326 |                      slot: &AbstractDeclarative::measureFpsChanged); | 
| 327 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::currentFpsChanged, receiver: this, | 
| 328 |                      slot: &AbstractDeclarative::currentFpsChanged); | 
| 329 |  | 
| 330 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::orthoProjectionChanged, receiver: this, | 
| 331 |                      slot: &AbstractDeclarative::orthoProjectionChanged); | 
| 332 |  | 
| 333 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::aspectRatioChanged, receiver: this, | 
| 334 |                      slot: &AbstractDeclarative::aspectRatioChanged); | 
| 335 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::optimizationHintsChanged, receiver: this, | 
| 336 |                      slot: &AbstractDeclarative::handleOptimizationHintChange); | 
| 337 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::polarChanged, receiver: this, | 
| 338 |                      slot: &AbstractDeclarative::polarChanged); | 
| 339 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::radialLabelOffsetChanged, receiver: this, | 
| 340 |                      slot: &AbstractDeclarative::radialLabelOffsetChanged); | 
| 341 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::horizontalAspectRatioChanged, receiver: this, | 
| 342 |                      slot: &AbstractDeclarative::horizontalAspectRatioChanged); | 
| 343 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectionChanged, receiver: this, | 
| 344 |                      slot: &AbstractDeclarative::reflectionChanged); | 
| 345 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectivityChanged, receiver: this, | 
| 346 |                      slot: &AbstractDeclarative::reflectivityChanged); | 
| 347 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::localeChanged, receiver: this, | 
| 348 |                      slot: &AbstractDeclarative::localeChanged); | 
| 349 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::queriedGraphPositionChanged, receiver: this, | 
| 350 |                      slot: &AbstractDeclarative::queriedGraphPositionChanged); | 
| 351 |     QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::marginChanged, receiver: this, | 
| 352 |                      slot: &AbstractDeclarative::marginChanged); | 
| 353 | } | 
| 354 |  | 
| 355 | void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) | 
| 356 | { | 
| 357 |     // We can assume we are not in middle of AbstractDeclarative destructor when we are here, | 
| 358 |     // since m_context creation is always done when this function is called from | 
| 359 |     // synchDataToRenderer(), which blocks main thread -> no need to mutex. | 
| 360 |     if (!m_contextOrStateStore || !m_qtContext || m_contextWindow != window) { | 
| 361 |         QOpenGLContext *currentContext = QOpenGLContext::currentContext(); | 
| 362 |  | 
| 363 |         // Note: Changing graph to different window when using multithreaded renderer will break! | 
| 364 |  | 
| 365 |         delete m_contextOrStateStore; | 
| 366 |  | 
| 367 |         m_contextThread = QThread::currentThread(); | 
| 368 |         m_contextWindow = window; | 
| 369 |         m_qtContext = currentContext; | 
| 370 |  | 
| 371 | #ifdef USE_SHARED_CONTEXT | 
| 372 |         m_context = new QOpenGLContext(); | 
| 373 |         m_context->setFormat(m_qtContext->format()); | 
| 374 |         m_context->setShareContext(m_qtContext); | 
| 375 |         m_context->create(); | 
| 376 |         m_context->makeCurrent(surface: window); | 
| 377 | #else | 
| 378 |         // Shared contexts don't work properly in some platforms, so just store the | 
| 379 |         // context state on those | 
| 380 |         m_stateStore = new GLStateStore(QOpenGLContext::currentContext()); | 
| 381 |         m_stateStore->storeGLState(); | 
| 382 | #endif | 
| 383 |         m_controller->initializeOpenGL(); | 
| 384 |  | 
| 385 |         // Make sure context / state store gets deleted. | 
| 386 |         QObject::connect(sender: m_contextThread, signal: &QThread::finished, receiver: this, | 
| 387 |                          slot: &AbstractDeclarative::destroyContext, type: Qt::DirectConnection); | 
| 388 |     } else { | 
| 389 | #ifdef USE_SHARED_CONTEXT | 
| 390 |         m_context->makeCurrent(surface: window); | 
| 391 | #else | 
| 392 |         m_stateStore->storeGLState(); | 
| 393 | #endif | 
| 394 |     } | 
| 395 | } | 
| 396 |  | 
| 397 | void AbstractDeclarative::doneOpenGLContext(QQuickWindow *window) | 
| 398 | { | 
| 399 | #ifdef USE_SHARED_CONTEXT | 
| 400 |     m_qtContext->makeCurrent(surface: window); | 
| 401 | #else | 
| 402 |     Q_UNUSED(window) | 
| 403 |     m_stateStore->restoreGLState(); | 
| 404 | #endif | 
| 405 | } | 
| 406 |  | 
| 407 | void AbstractDeclarative::synchDataToRenderer() | 
| 408 | { | 
| 409 |     if (m_renderMode == RenderDirectToBackground && clearList.size()) | 
| 410 |         clearList.clear(); | 
| 411 |  | 
| 412 |     QQuickWindow *win = window(); | 
| 413 |     activateOpenGLContext(window: win); | 
| 414 |     m_controller->synchDataToRenderer(); | 
| 415 |     doneOpenGLContext(window: win); | 
| 416 | } | 
| 417 |  | 
| 418 | int AbstractDeclarative::msaaSamples() const | 
| 419 | { | 
| 420 |     if (m_renderMode == RenderIndirect) | 
| 421 |         return m_samples; | 
| 422 |     else | 
| 423 |         return m_windowSamples; | 
| 424 | } | 
| 425 |  | 
| 426 | void AbstractDeclarative::setMsaaSamples(int samples) | 
| 427 | { | 
| 428 |     if (m_renderMode != RenderIndirect) { | 
| 429 |         qWarning(msg: "Multisampling cannot be adjusted in this render mode" ); | 
| 430 |     } else { | 
| 431 |         if (m_controller->isOpenGLES()) { | 
| 432 |             if (samples > 0) | 
| 433 |                 qWarning(msg: "Multisampling is not supported in OpenGL ES2" ); | 
| 434 |         } else if (m_samples != samples) { | 
| 435 |             m_samples = samples; | 
| 436 |             setAntialiasing(m_samples > 0); | 
| 437 |             emit msaaSamplesChanged(samples); | 
| 438 |             update(); | 
| 439 |         } | 
| 440 |     } | 
| 441 | } | 
| 442 |  | 
| 443 | void AbstractDeclarative::handleWindowChanged(QQuickWindow *window) | 
| 444 | { | 
| 445 |     checkWindowList(window); | 
| 446 |  | 
| 447 |     if (!window) | 
| 448 |         return; | 
| 449 |  | 
| 450 | #if defined(Q_OS_OSX) | 
| 451 |     bool previousVisibility = window->isVisible(); | 
| 452 |     // Enable touch events for Mac touchpads | 
| 453 |     window->setVisible(true); | 
| 454 |     typedef void * (*EnableTouch)(QWindow*, bool); | 
| 455 |     EnableTouch enableTouch = | 
| 456 |             (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow" ); | 
| 457 |     if (enableTouch) | 
| 458 |         enableTouch(window, true); | 
| 459 |     window->setVisible(previousVisibility); | 
| 460 | #endif | 
| 461 |  | 
| 462 |     connect(sender: window, signal: &QObject::destroyed, receiver: this, slot: &AbstractDeclarative::windowDestroyed); | 
| 463 |  | 
| 464 |     int oldWindowSamples = m_windowSamples; | 
| 465 |     m_windowSamples = window->format().samples(); | 
| 466 |     if (m_windowSamples < 0) | 
| 467 |         m_windowSamples = 0; | 
| 468 |  | 
| 469 |     connect(sender: window, signal: &QQuickWindow::beforeSynchronizing, | 
| 470 |             receiver: this, slot: &AbstractDeclarative::synchDataToRenderer, | 
| 471 |             type: Qt::DirectConnection); | 
| 472 |  | 
| 473 |     if (m_renderMode == RenderDirectToBackground_NoClear | 
| 474 |             || m_renderMode == RenderDirectToBackground) { | 
| 475 |         connect(sender: window, signal: &QQuickWindow::beforeRendering, receiver: this, slot: &AbstractDeclarative::render, | 
| 476 |                 type: Qt::DirectConnection); | 
| 477 |         setAntialiasing(m_windowSamples > 0); | 
| 478 |         if (m_windowSamples != oldWindowSamples) | 
| 479 |             emit msaaSamplesChanged(samples: m_windowSamples); | 
| 480 |     } | 
| 481 |  | 
| 482 |     connect(sender: m_controller.data(), signal: &Abstract3DController::needRender, receiver: window, slot: &QQuickWindow::update); | 
| 483 |  | 
| 484 |     updateWindowParameters(); | 
| 485 |  | 
| 486 | #if defined(Q_OS_IOS) | 
| 487 |     // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal. | 
| 488 |     // This ensures we don't end up displaying the graph without any data, in case update is | 
| 489 |     // skipped after synchDataToRenderer. | 
| 490 |     QTimer::singleShot(0, window, SLOT(update())); | 
| 491 | #endif | 
| 492 | } | 
| 493 |  | 
| 494 | void AbstractDeclarative::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | 
| 495 | { | 
| 496 |     QQuickItem::geometryChanged(newGeometry, oldGeometry); | 
| 497 |  | 
| 498 |     m_cachedGeometry = newGeometry; | 
| 499 |  | 
| 500 |     updateWindowParameters(); | 
| 501 | } | 
| 502 |  | 
| 503 | void AbstractDeclarative::itemChange(ItemChange change, const ItemChangeData & value) | 
| 504 | { | 
| 505 |     QQuickItem::itemChange(change, value); | 
| 506 |     updateWindowParameters(); | 
| 507 | } | 
| 508 |  | 
| 509 | void AbstractDeclarative::updateWindowParameters() | 
| 510 | { | 
| 511 |     const QMutexLocker locker(&m_mutex); | 
| 512 |  | 
| 513 |     // Update the device pixel ratio, window size and bounding box | 
| 514 |     QQuickWindow *win = window(); | 
| 515 |     if (win && !m_controller.isNull()) { | 
| 516 |         Q3DScene *scene = m_controller->scene(); | 
| 517 |         if (win->devicePixelRatio() != scene->devicePixelRatio()) { | 
| 518 |             scene->setDevicePixelRatio(win->devicePixelRatio()); | 
| 519 |             win->update(); | 
| 520 |         } | 
| 521 |  | 
| 522 |         bool directRender = m_renderMode == RenderDirectToBackground | 
| 523 |                 || m_renderMode == RenderDirectToBackground_NoClear; | 
| 524 |         QSize windowSize; | 
| 525 |  | 
| 526 |         if (directRender) | 
| 527 |             windowSize = win->size(); | 
| 528 |         else | 
| 529 |             windowSize = m_cachedGeometry.size().toSize(); | 
| 530 |  | 
| 531 |         if (windowSize != scene->d_ptr->windowSize()) { | 
| 532 |             scene->d_ptr->setWindowSize(windowSize); | 
| 533 |             win->update(); | 
| 534 |         } | 
| 535 |  | 
| 536 |         if (directRender) { | 
| 537 |             // Origin mapping is needed when rendering directly to background | 
| 538 |             QPointF point = QQuickItem::mapToScene(point: QPointF(0.0, 0.0)); | 
| 539 |             scene->d_ptr->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f, | 
| 540 |                                             m_cachedGeometry.width() + 0.5f, | 
| 541 |                                             m_cachedGeometry.height() + 0.5f)); | 
| 542 |         } else { | 
| 543 |             // No translation needed when rendering to FBO | 
| 544 |             scene->d_ptr->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f, | 
| 545 |                                             m_cachedGeometry.height() + 0.5f)); | 
| 546 |         } | 
| 547 |     } | 
| 548 | } | 
| 549 |  | 
| 550 | void AbstractDeclarative::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode) | 
| 551 | { | 
| 552 |     int intmode = int(mode); | 
| 553 |     emit selectionModeChanged(mode: SelectionFlags(intmode)); | 
| 554 | } | 
| 555 |  | 
| 556 | void AbstractDeclarative::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality) | 
| 557 | { | 
| 558 |     emit shadowQualityChanged(quality: ShadowQuality(quality)); | 
| 559 | } | 
| 560 |  | 
| 561 | void AbstractDeclarative::handleSelectedElementChange(QAbstract3DGraph::ElementType type) | 
| 562 | { | 
| 563 |     emit selectedElementChanged(type: ElementType(type)); | 
| 564 | } | 
| 565 |  | 
| 566 | void AbstractDeclarative::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints) | 
| 567 | { | 
| 568 |     int intHints = int(hints); | 
| 569 |     emit optimizationHintsChanged(hints: OptimizationHints(intHints)); | 
| 570 | } | 
| 571 |  | 
| 572 | void AbstractDeclarative::render() | 
| 573 | { | 
| 574 |     updateWindowParameters(); | 
| 575 |  | 
| 576 |     // If we're not rendering directly to the background, return | 
| 577 |     if (m_renderMode != RenderDirectToBackground && m_renderMode != RenderDirectToBackground_NoClear) | 
| 578 |         return; | 
| 579 |  | 
| 580 |     // Clear the background once per window as that is not done by default | 
| 581 |     QQuickWindow *win = window(); | 
| 582 |     activateOpenGLContext(window: win); | 
| 583 |     QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); | 
| 584 |     if (m_renderMode == RenderDirectToBackground && !clearList.contains(t: win)) { | 
| 585 |         clearList.append(t: win); | 
| 586 |         QColor clearColor = win->color(); | 
| 587 |         funcs->glClearColor(red: clearColor.redF(), green: clearColor.greenF(), blue: clearColor.blueF(), alpha: 1.0f); | 
| 588 |         funcs->glClear(GL_COLOR_BUFFER_BIT); | 
| 589 |     } | 
| 590 |  | 
| 591 |     if (isVisible()) { | 
| 592 |         funcs->glDepthMask(GL_TRUE); | 
| 593 |         funcs->glEnable(GL_DEPTH_TEST); | 
| 594 |         funcs->glDepthFunc(GL_LESS); | 
| 595 |         funcs->glEnable(GL_CULL_FACE); | 
| 596 |         funcs->glCullFace(GL_BACK); | 
| 597 |         funcs->glDisable(GL_BLEND); | 
| 598 |  | 
| 599 |         m_controller->render(); | 
| 600 |  | 
| 601 |         funcs->glEnable(GL_BLEND); | 
| 602 |     } | 
| 603 |     doneOpenGLContext(window: win); | 
| 604 | } | 
| 605 |  | 
| 606 | QAbstract3DInputHandler* AbstractDeclarative::inputHandler() const | 
| 607 | { | 
| 608 |     return m_controller->activeInputHandler(); | 
| 609 | } | 
| 610 |  | 
| 611 | void AbstractDeclarative::setInputHandler(QAbstract3DInputHandler *inputHandler) | 
| 612 | { | 
| 613 |     m_controller->setActiveInputHandler(inputHandler); | 
| 614 | } | 
| 615 |  | 
| 616 | void AbstractDeclarative::mouseDoubleClickEvent(QMouseEvent *event) | 
| 617 | { | 
| 618 |     m_controller->mouseDoubleClickEvent(event); | 
| 619 | } | 
| 620 |  | 
| 621 | void AbstractDeclarative::touchEvent(QTouchEvent *event) | 
| 622 | { | 
| 623 |     m_controller->touchEvent(event); | 
| 624 |     window()->update(); | 
| 625 | } | 
| 626 |  | 
| 627 | void AbstractDeclarative::mousePressEvent(QMouseEvent *event) | 
| 628 | { | 
| 629 |     QPoint mousePos = event->pos(); | 
| 630 |     m_controller->mousePressEvent(event, mousePos); | 
| 631 | } | 
| 632 |  | 
| 633 | void AbstractDeclarative::mouseReleaseEvent(QMouseEvent *event) | 
| 634 | { | 
| 635 |     QPoint mousePos = event->pos(); | 
| 636 |     m_controller->mouseReleaseEvent(event, mousePos); | 
| 637 | } | 
| 638 |  | 
| 639 | void AbstractDeclarative::mouseMoveEvent(QMouseEvent *event) | 
| 640 | { | 
| 641 |     QPoint mousePos = event->pos(); | 
| 642 |     m_controller->mouseMoveEvent(event, mousePos); | 
| 643 | } | 
| 644 |  | 
| 645 | #if QT_CONFIG(wheelevent) | 
| 646 | void AbstractDeclarative::wheelEvent(QWheelEvent *event) | 
| 647 | { | 
| 648 |     m_controller->wheelEvent(event); | 
| 649 | } | 
| 650 | #endif | 
| 651 |  | 
| 652 | void AbstractDeclarative::checkWindowList(QQuickWindow *window) | 
| 653 | { | 
| 654 |     QQuickWindow *oldWindow = graphWindowList.value(akey: this); | 
| 655 |  | 
| 656 |     graphWindowList[this] = window; | 
| 657 |  | 
| 658 |     if (oldWindow != window && oldWindow) { | 
| 659 |         QObject::disconnect(sender: oldWindow, signal: &QObject::destroyed, receiver: this, | 
| 660 |                             slot: &AbstractDeclarative::windowDestroyed); | 
| 661 |         QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeSynchronizing, receiver: this, | 
| 662 |                             slot: &AbstractDeclarative::synchDataToRenderer); | 
| 663 |         QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeRendering, receiver: this, | 
| 664 |                             slot: &AbstractDeclarative::render); | 
| 665 |         if (!m_controller.isNull()) { | 
| 666 |             QObject::disconnect(sender: m_controller.data(), signal: &Abstract3DController::needRender, | 
| 667 |                                 receiver: oldWindow, slot: &QQuickWindow::update); | 
| 668 |         } | 
| 669 |     } | 
| 670 |  | 
| 671 |     QList<QQuickWindow *> windowList; | 
| 672 |  | 
| 673 |     foreach (AbstractDeclarative *graph, graphWindowList.keys()) { | 
| 674 |         if (graph->m_renderMode == RenderDirectToBackground | 
| 675 |                 || graph->m_renderMode == RenderDirectToBackground_NoClear) { | 
| 676 |             windowList.append(t: graphWindowList.value(akey: graph)); | 
| 677 |         } | 
| 678 |     } | 
| 679 |  | 
| 680 |     if (oldWindow && !windowList.contains(t: oldWindow) | 
| 681 |             && windowClearList.contains(akey: oldWindow)) { | 
| 682 |         // Return window clear value | 
| 683 |         oldWindow->setClearBeforeRendering(windowClearList.value(akey: oldWindow)); | 
| 684 |         windowClearList.remove(akey: oldWindow); | 
| 685 |     } | 
| 686 |  | 
| 687 |     if (!window) { | 
| 688 |         graphWindowList.remove(akey: this); | 
| 689 |         return; | 
| 690 |     } | 
| 691 |  | 
| 692 |     if ((m_renderMode == RenderDirectToBackground | 
| 693 |          || m_renderMode == RenderDirectToBackground_NoClear) | 
| 694 |             && !windowClearList.contains(akey: window)) { | 
| 695 |         // Save old clear value | 
| 696 |         windowClearList[window] = window->clearBeforeRendering(); | 
| 697 |         // Disable clearing of the window as we render underneath | 
| 698 |         window->setClearBeforeRendering(false); | 
| 699 |     } | 
| 700 | } | 
| 701 |  | 
| 702 | void AbstractDeclarative::setMeasureFps(bool enable) | 
| 703 | { | 
| 704 |     m_controller->setMeasureFps(enable); | 
| 705 | } | 
| 706 |  | 
| 707 | bool AbstractDeclarative::measureFps() const | 
| 708 | { | 
| 709 |     return m_controller->measureFps(); | 
| 710 | } | 
| 711 |  | 
| 712 | qreal AbstractDeclarative::currentFps() const | 
| 713 | { | 
| 714 |     return m_controller->currentFps(); | 
| 715 | } | 
| 716 |  | 
| 717 | void AbstractDeclarative::setOrthoProjection(bool enable) | 
| 718 | { | 
| 719 |     m_controller->setOrthoProjection(enable); | 
| 720 | } | 
| 721 |  | 
| 722 | bool AbstractDeclarative::isOrthoProjection() const | 
| 723 | { | 
| 724 |     return m_controller->isOrthoProjection(); | 
| 725 | } | 
| 726 |  | 
| 727 | AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const | 
| 728 | { | 
| 729 |     return ElementType(m_controller->selectedElement()); | 
| 730 | } | 
| 731 |  | 
| 732 | void AbstractDeclarative::setAspectRatio(qreal ratio) | 
| 733 | { | 
| 734 |     m_controller->setAspectRatio(ratio); | 
| 735 | } | 
| 736 |  | 
| 737 | qreal AbstractDeclarative::aspectRatio() const | 
| 738 | { | 
| 739 |     return m_controller->aspectRatio(); | 
| 740 | } | 
| 741 |  | 
| 742 | void AbstractDeclarative::setOptimizationHints(OptimizationHints hints) | 
| 743 | { | 
| 744 |     int intmode = int(hints); | 
| 745 |     m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode)); | 
| 746 | } | 
| 747 |  | 
| 748 | AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints() const | 
| 749 | { | 
| 750 |     int intmode = int(m_controller->optimizationHints()); | 
| 751 |     return OptimizationHints(intmode); | 
| 752 | } | 
| 753 |  | 
| 754 | void AbstractDeclarative::setPolar(bool enable) | 
| 755 | { | 
| 756 |     m_controller->setPolar(enable); | 
| 757 | } | 
| 758 |  | 
| 759 | bool AbstractDeclarative::isPolar() const | 
| 760 | { | 
| 761 |     return m_controller->isPolar(); | 
| 762 | } | 
| 763 |  | 
| 764 | void AbstractDeclarative::setRadialLabelOffset(float offset) | 
| 765 | { | 
| 766 |     m_controller->setRadialLabelOffset(offset); | 
| 767 | } | 
| 768 |  | 
| 769 | float AbstractDeclarative::radialLabelOffset() const | 
| 770 | { | 
| 771 |     return m_controller->radialLabelOffset(); | 
| 772 | } | 
| 773 |  | 
| 774 | void AbstractDeclarative::setHorizontalAspectRatio(qreal ratio) | 
| 775 | { | 
| 776 |     m_controller->setHorizontalAspectRatio(ratio); | 
| 777 | } | 
| 778 |  | 
| 779 | qreal AbstractDeclarative::horizontalAspectRatio() const | 
| 780 | { | 
| 781 |     return m_controller->horizontalAspectRatio(); | 
| 782 | } | 
| 783 |  | 
| 784 | void AbstractDeclarative::setReflection(bool enable) | 
| 785 | { | 
| 786 |     m_controller->setReflection(enable); | 
| 787 | } | 
| 788 |  | 
| 789 | bool AbstractDeclarative::isReflection() const | 
| 790 | { | 
| 791 |     return m_controller->reflection(); | 
| 792 | } | 
| 793 |  | 
| 794 | void AbstractDeclarative::setReflectivity(qreal reflectivity) | 
| 795 | { | 
| 796 |     m_controller->setReflectivity(reflectivity); | 
| 797 | } | 
| 798 |  | 
| 799 | qreal AbstractDeclarative::reflectivity() const | 
| 800 | { | 
| 801 |     return m_controller->reflectivity(); | 
| 802 | } | 
| 803 |  | 
| 804 | void AbstractDeclarative::setLocale(const QLocale &locale) | 
| 805 | { | 
| 806 |     m_controller->setLocale(locale); | 
| 807 | } | 
| 808 |  | 
| 809 | QLocale AbstractDeclarative::locale() const | 
| 810 | { | 
| 811 |     return m_controller->locale(); | 
| 812 | } | 
| 813 |  | 
| 814 | QVector3D AbstractDeclarative::queriedGraphPosition() const | 
| 815 | { | 
| 816 |     return m_controller->queriedGraphPosition(); | 
| 817 | } | 
| 818 |  | 
| 819 | void AbstractDeclarative::setMargin(qreal margin) | 
| 820 | { | 
| 821 |     m_controller->setMargin(margin); | 
| 822 | } | 
| 823 |  | 
| 824 | qreal AbstractDeclarative::margin() const | 
| 825 | { | 
| 826 |     return m_controller->margin(); | 
| 827 | } | 
| 828 |  | 
| 829 | void AbstractDeclarative::windowDestroyed(QObject *obj) | 
| 830 | { | 
| 831 |     // Remove destroyed window from window lists | 
| 832 |     QQuickWindow *win = static_cast<QQuickWindow *>(obj); | 
| 833 |     QQuickWindow *oldWindow = graphWindowList.value(akey: this); | 
| 834 |  | 
| 835 |     if (win == oldWindow) | 
| 836 |         graphWindowList.remove(akey: this); | 
| 837 |  | 
| 838 |     windowClearList.remove(akey: win); | 
| 839 | } | 
| 840 |  | 
| 841 | void AbstractDeclarative::destroyContext() | 
| 842 | { | 
| 843 |     if (m_contextThread && m_contextThread != m_mainThread) { | 
| 844 |         if (m_contextOrStateStore) | 
| 845 |             m_contextOrStateStore->deleteLater(); | 
| 846 |     } else { | 
| 847 |         delete m_contextOrStateStore; | 
| 848 |     } | 
| 849 |     m_contextOrStateStore = 0; | 
| 850 |  | 
| 851 |     if (m_contextThread) { | 
| 852 |         QObject::disconnect(sender: m_contextThread, signal: &QThread::finished, receiver: this, | 
| 853 |                             slot: &AbstractDeclarative::destroyContext); | 
| 854 |         m_contextThread = 0; | 
| 855 |     } | 
| 856 | } | 
| 857 |  | 
| 858 | QT_END_NAMESPACE_DATAVISUALIZATION | 
| 859 |  |