From d478204722ef14eb70215bcb324d79a0d6a2e1e0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Apr 2018 13:33:37 -0700 Subject: [PATCH] Working on splash rendering --- interface/src/Application.cpp | 310 ++++++++++-------- interface/src/Application.h | 3 +- .../stereo/StereoDisplayPlugin.cpp | 20 +- .../stereo/StereoDisplayPlugin.h | 2 +- libraries/gl/src/gl/GLWidget.cpp | 4 + libraries/gl/src/gl/GLWidget.h | 1 + .../src/ui-plugins/PluginContainer.cpp | 144 ++++++-- .../src/ui-plugins/PluginContainer.h | 4 +- 8 files changed, 302 insertions(+), 186 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6ffb7f6dee..37485f1b10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1266,9 +1266,32 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure the window is set to the correct size by processing the pending events QCoreApplication::processEvents(); _glWidget->createContext(); - _glWidget->makeCurrent(); + // Create the main thread context, the GPU backend, and the display plugins initializeGL(); + qCDebug(interfaceapp, "Initialized Display."); + // Create the rendering engine. This can be slow on some machines due to lots of + // GPU pipeline creation. + initializeRenderEngine(); + qCDebug(interfaceapp, "Initialized Render Engine."); + + // Initialize the user interface and menu system + // Needs to happen AFTER the render engine initialization to access its configuration + initializeUi(); + + init(); + qCDebug(interfaceapp, "init() complete."); + + // create thread for parsing of octree data independent of the main network and rendering threads + _octreeProcessor.initialize(_enableProcessOctreeThread); + connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); + _entityEditSender.initialize(_enableProcessOctreeThread); + + _idleLoopStdev.reset(); + + // update before the first render + update(0); + // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2430,48 +2453,47 @@ void Application::initializeGL() { _isGLInitialized = true; } - // Build a shared canvas / context for the Chromium processes _glWidget->makeCurrent(); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glClear(GL_COLOR_BUFFER_BIT); + _glWidget->swapBuffers(); -#if !defined(DISABLE_QML) - // Chromium rendering uses some GL functions that prevent nSight from capturing - // frames, so we only create the shared context if nsight is NOT active. - if (!nsightActive()) { - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); - _chromiumShareContext->makeCurrent(); - if (!_chromiumShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make chromium shared context current"); - } - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - } else { - qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; + // Build an offscreen GL context for the main thread. + _offscreenContext = new OffscreenGLCanvas(); + _offscreenContext->setObjectName("MainThreadContext"); + _offscreenContext->create(_glWidget->qglContext()); + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); } -#endif + _offscreenContext->doneCurrent(); + _offscreenContext->setThreadContext(); - // Build a shared canvas / context for the QML rendering - _glWidget->makeCurrent(); - _qmlShareContext = new OffscreenGLCanvas(); - _qmlShareContext->setObjectName("QmlShareContext"); - _qmlShareContext->create(_glWidget->qglContext()); - if (!_qmlShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make QML shared context current"); + // Move the GL widget context to the render event handler thread + _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); } - OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); - _qmlShareContext->doneCurrent(); + // Create the GPU backend + + // Requires the window context, because that's what's used in the actual rendering + // and the GPU backend will make things like the VAO which cannot be shared across + // contexts _glWidget->makeCurrent(); gpu::Context::init(); qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram))); - _gpuContext = std::make_shared(); - // The gpu context can make child contexts for transfers, so - // we need to restore primary rendering context _glWidget->makeCurrent(); + _gpuContext = std::make_shared(); - initDisplay(); - qCDebug(interfaceapp, "Initialized Display."); + // Restore the default main thread context + _offscreenContext->makeCurrent(); + + updateDisplayMode(); +} + +void Application::initializeRenderEngine() { + _offscreenContext->makeCurrent(); // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { @@ -2488,66 +2510,44 @@ void Application::initializeGL() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); }); - - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->setObjectName("MainThreadContext"); - _offscreenContext->create(_glWidget->qglContext()); - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - _offscreenContext->doneCurrent(); - _offscreenContext->setThreadContext(); - _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); - - // The UI can't be created until the primary OpenGL - // context is created, because it needs to share - // texture resources - // Needs to happen AFTER the render engine initialization to access its configuration - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - - initializeUi(); - qCDebug(interfaceapp, "Initialized Offscreen UI."); - - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - init(); - qCDebug(interfaceapp, "init() complete."); - - // create thread for parsing of octree data independent of the main network and rendering threads - _octreeProcessor.initialize(_enableProcessOctreeThread); - connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); - _entityEditSender.initialize(_enableProcessOctreeThread); - - _idleLoopStdev.reset(); - - - // Restore the primary GL content for the main thread - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - - // update before the first render - update(0); } extern void setupPreferences(); void Application::initializeUi() { + // Build a shared canvas / context for the Chromium processes +#if !defined(DISABLE_QML) + // Chromium rendering uses some GL functions that prevent nSight from capturing + // frames, so we only create the shared context if nsight is NOT active. + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_offscreenContext->getContext()); + if (!_chromiumShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make chromium shared context current"); + } + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _chromiumShareContext->doneCurrent(); + // Restore the GL widget context + _offscreenContext->makeCurrent(); + } else { + qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; + } +#endif + + // Build a shared canvas / context for the QML rendering + _qmlShareContext = new OffscreenGLCanvas(); + _qmlShareContext->setObjectName("QmlShareContext"); + _qmlShareContext->create(_offscreenContext->getContext()); + if (!_qmlShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make QML shared context current"); + } + OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); + _qmlShareContext->doneCurrent(); + // Restore the GL widget context + _offscreenContext->makeCurrent(); // Make sure all QML surfaces share the main thread GL context OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext()); - OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "OverlayWindowTest.qml" }, - [](QQmlContext* context) { - qDebug() << "Whitelist OverlayWindow worked"; - context->setContextProperty("OverlayWindowTestString", "TestWorked"); - }); - OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "hifi/audio/Audio.qml" }, - [](QQmlContext* context) { - qDebug() << "QQQ" << __FUNCTION__ << "Whitelist Audio worked"; - }); - AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -2650,6 +2650,10 @@ void Application::initializeUi() { auto offscreenSurfaceCache = DependencyManager::get(); offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); + + // Now that the menu is instantiated, ensure the display plugin menu is properly updated + updateDisplayMode(); + flushMenuUpdates(); } @@ -4607,11 +4611,8 @@ QVector Application::pasteEntities(float x, float y, float z) { return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); } -void Application::initDisplay() { -} - void Application::init() { - + _offscreenContext->makeCurrent(); // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); if (!DISABLE_DEFERRED) { @@ -7500,21 +7501,34 @@ void Application::updateDisplayMode() { qFatal("Attempted to switch display plugins from a non-main thread"); } - auto menu = Menu::getInstance(); auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + // Once time initialization code static std::once_flag once; std::call_once(once, [&] { - bool first = true; - - // first sort the plugins into groupings: standard, advanced, developer - DisplayPluginList standard; - DisplayPluginList advanced; - DisplayPluginList developer; foreach(auto displayPlugin, displayPlugins) { displayPlugin->setContext(_gpuContext); - auto grouping = displayPlugin->getGrouping(); - switch (grouping) { + QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, + [this](const QSize& size) { resizeGL(); }); + QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); + } + }); + + // Once time initialization code that depends on the UI being available + auto menu = Menu::getInstance(); + if (menu) { + static std::once_flag onceUi; + std::call_once(onceUi, [&] { + bool first = true; + + // first sort the plugins into groupings: standard, advanced, developer + DisplayPluginList standard; + DisplayPluginList advanced; + DisplayPluginList developer; + foreach(auto displayPlugin, displayPlugins) { + displayPlugin->setContext(_gpuContext); + auto grouping = displayPlugin->getGrouping(); + switch (grouping) { case Plugin::ADVANCED: advanced.push_back(displayPlugin); break; @@ -7524,42 +7538,40 @@ void Application::updateDisplayMode() { default: standard.push_back(displayPlugin); break; + } } - } - // concatenate the groupings into a single list in the order: standard, advanced, developer - standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); - standard.insert(std::end(standard), std::begin(developer), std::end(developer)); + // concatenate the groupings into a single list in the order: standard, advanced, developer + standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); + standard.insert(std::end(standard), std::begin(developer), std::end(developer)); - foreach(auto displayPlugin, standard) { - addDisplayPluginToMenu(displayPlugin, first); - auto displayPluginName = displayPlugin->getName(); - QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { - resizeGL(); - }); - QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); - first = false; - } + foreach(auto displayPlugin, standard) { + addDisplayPluginToMenu(displayPlugin, first); + first = false; + } - // after all plugins have been added to the menu, add a separator to the menu - auto menu = Menu::getInstance(); - auto parent = menu->getMenu(MenuOption::OutputMenu); - parent->addSeparator(); - }); + // after all plugins have been added to the menu, add a separator to the menu + auto parent = menu->getMenu(MenuOption::OutputMenu); + parent->addSeparator(); + }); + + } // Default to the first item on the list, in case none of the menu items match DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); - // Menu might have been removed if the display plugin lost - if (!action) { - continue; - } - if (action->isChecked()) { - newDisplayPlugin = displayPlugin; - break; + if (menu) { + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + QString name = displayPlugin->getName(); + QAction* action = menu->getActionForOption(name); + // Menu might have been removed if the display plugin lost + if (!action) { + continue; + } + if (action->isChecked()) { + newDisplayPlugin = displayPlugin; + break; + } } } @@ -7567,8 +7579,13 @@ void Application::updateDisplayMode() { return; } + setDisplayPlugin(newDisplayPlugin); +} + +void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { auto offscreenUi = DependencyManager::get(); auto desktop = offscreenUi->getDesktop(); + auto menu = Menu::getInstance(); // Make the switch atomic from the perspective of other threads { @@ -7589,6 +7606,8 @@ void Application::updateDisplayMode() { bool active = newDisplayPlugin->activate(); if (!active) { + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + // If the new plugin fails to activate, fallback to last display qWarning() << "Failed to activate display: " << newDisplayPlugin->getName(); newDisplayPlugin = oldDisplayPlugin; @@ -7609,13 +7628,6 @@ void Application::updateDisplayMode() { if (!active) { qFatal("Failed to activate fallback plugin"); } - - // We've changed the selection - it should be reflected in the menu - QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); - if (!action) { - qFatal("Failed to find activated plugin"); - } - action->setChecked(true); } offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); @@ -7642,14 +7654,21 @@ void Application::updateDisplayMode() { getMyAvatar()->reset(false); // switch to first person if entering hmd and setting is checked - if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - cameraMenuChanged(); - } + if (menu) { + QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); + if (action) { + action->setChecked(true); + } - // Remove the mirror camera option from menu if in HMD mode - auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); - mirrorAction->setVisible(!isHmd); + if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + cameraMenuChanged(); + } + + // Remove the mirror camera option from menu if in HMD mode + auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); + mirrorAction->setVisible(!isHmd); + } Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } @@ -7725,15 +7744,18 @@ void Application::unresponsiveApplication() { } void Application::setActiveDisplayPlugin(const QString& pluginName) { - auto menu = Menu::getInstance(); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + DisplayPluginPointer newDisplayPlugin; + for (DisplayPluginPointer displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); if (pluginName == name) { - action->setChecked(true); + newDisplayPlugin = displayPlugin; + break; } } - updateDisplayMode(); + + if (newDisplayPlugin) { + setDisplayPlugin(newDisplayPlugin); + } } void Application::handleLocalServerConnection() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index d7fbb48a58..769658b0d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -145,6 +145,7 @@ public: Q_INVOKABLE QString getUserAgent(); void initializeGL(); + void initializeRenderEngine(); void initializeUi(); void updateCamera(RenderArgs& renderArgs, float deltaTime); @@ -437,6 +438,7 @@ private slots: static void packetSent(quint64 length); static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName); void updateDisplayMode(); + void setDisplayPlugin(DisplayPluginPointer newPlugin); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); void addAssetToWorldCheckModelSize(); @@ -449,7 +451,6 @@ private slots: void switchDisplayMode(); private: - static void initDisplay(); void init(); bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); bool handleFileOpenEvent(QFileOpenEvent* event); diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index cfdfb1fc21..cb69d3a514 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -56,10 +56,8 @@ glm::mat4 StereoDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& basePr static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; -std::vector _screenActions; bool StereoDisplayPlugin::internalActivate() { auto screens = qApp->screens(); - _screenActions.resize(screens.size()); for (int i = 0; i < screens.size(); ++i) { auto screen = screens.at(i); QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name()); @@ -67,9 +65,9 @@ bool StereoDisplayPlugin::internalActivate() { if (screen == qApp->primaryScreen()) { checked = true; } - auto action = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name, - [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); - _screenActions[i] = action; + const uint32_t screenIndex = i; + _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name, + [=](bool clicked) { updateScreen(screenIndex); }, true, checked, "Screens"); } _container->removeMenu(FRAMERATE); @@ -80,18 +78,12 @@ bool StereoDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void StereoDisplayPlugin::updateScreen() { - for (uint32_t i = 0; i < _screenActions.size(); ++i) { - if (_screenActions[i]->isChecked()) { - _screen = qApp->screens().at(i); - _container->setFullscreen(_screen); - break; - } - } +void StereoDisplayPlugin::updateScreen(uint32_t i) { + _screen = qApp->screens().at(i); + _container->setFullscreen(_screen); } void StereoDisplayPlugin::internalDeactivate() { - _screenActions.clear(); _container->unsetFullscreen(); Parent::internalDeactivate(); } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index c4205ea1db..5a7ca24059 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -31,7 +31,7 @@ public: protected: virtual bool internalActivate() override; virtual void internalDeactivate() override; - void updateScreen(); + void updateScreen(uint32_t i); float _ipd{ 0.064f }; QScreen* _screen; diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index c1d049f7f3..1c0ad1a85e 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -72,6 +72,10 @@ void GLWidget::createContext() { _context->doneCurrent(); } +void GLWidget::swapBuffers() { + _context->swapBuffers(); +} + bool GLWidget::makeCurrent() { gl::Context::makeCurrent(_context->qglContext(), windowHandle()); return _context->makeCurrent(); diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h index 21dffc1b75..a0bf8ea0b0 100644 --- a/libraries/gl/src/gl/GLWidget.h +++ b/libraries/gl/src/gl/GLWidget.h @@ -32,6 +32,7 @@ public: void createContext(); bool makeCurrent(); void doneCurrent(); + void swapBuffers(); gl::Context* context() { return _context; } QOpenGLContext* qglContext(); diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp index 6d54351743..58dc971cb9 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp @@ -34,48 +34,140 @@ PluginContainer::~PluginContainer() { INSTANCE = nullptr; }; +struct MenuCache { + QSet menus; + struct Item { + QString path; + std::function onClicked; + bool checkable; + bool checked; + QString groupName; + }; + QHash items; + std::map _exclusiveGroups; + + void addMenu(ui::Menu* menu, const QString& menuName) { + if (!menu) { + menus.insert(menuName); + return; + } + + flushCache(menu); + menu->addMenu(menuName); + } + + void removeMenu(ui::Menu* menu, const QString& menuName) { + if (!menu) { + menus.remove(menuName); + return; + } + flushCache(menu); + menu->removeMenu(menuName); + } + + void addMenuItem(ui::Menu* menu, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { + if (!menu) { + items[name] = Item{ path, onClicked, checkable, checked, groupName }; + return; + } + flushCache(menu); + MenuWrapper* parentItem = menu->getMenu(path); + QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); + if (!groupName.isEmpty()) { + QActionGroup* group{ nullptr }; + if (!_exclusiveGroups.count(groupName)) { + group = _exclusiveGroups[groupName] = new QActionGroup(menu); + group->setExclusive(true); + } else { + group = _exclusiveGroups[groupName]; + } + group->addAction(action); + } + QObject::connect(action, &QAction::triggered, [=] { + onClicked(action->isChecked()); + }); + action->setCheckable(checkable); + action->setChecked(checked); + } + void removeMenuItem(ui::Menu* menu, const QString& menuName, const QString& menuItemName) { + if (!menu) { + items.remove(menuItemName); + return; + } + flushCache(menu); + menu->removeMenuItem(menuName, menuItemName); + } + + bool isOptionChecked(ui::Menu* menu, const QString& name) { + if (!menu) { + return items.contains(name) && items[name].checked; + } + flushCache(menu); + return menu->isOptionChecked(name); + } + + void setIsOptionChecked(ui::Menu* menu, const QString& name, bool checked) { + if (!menu) { + if (items.contains(name)) { + items[name].checked = checked; + } + return; + } + flushCache(menu); + + } + + void flushCache(ui::Menu* menu) { + if (!menu) { + return; + } + static bool flushed = false; + if (flushed) { + return; + } + flushed = true; + for (const auto& menuName : menus) { + addMenu(menu, menuName); + } + menus.clear(); + + for (const auto& menuItemName : items.keys()) { + const auto menuItem = items[menuItemName]; + addMenuItem(menu, menuItem.path, menuItemName, menuItem.onClicked, menuItem.checkable, menuItem.checked, menuItem.groupName); + } + items.clear(); + } +}; + + +static MenuCache& getMenuCache() { + static MenuCache cache; + return cache; +} void PluginContainer::addMenu(const QString& menuName) { - getPrimaryMenu()->addMenu(menuName); + getMenuCache().addMenu(getPrimaryMenu(), menuName); } void PluginContainer::removeMenu(const QString& menuName) { - getPrimaryMenu()->removeMenu(menuName); + getMenuCache().removeMenu(getPrimaryMenu(), menuName); } -QAction* PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { - auto menu = getPrimaryMenu(); - MenuWrapper* parentItem = menu->getMenu(path); - QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); - if (!groupName.isEmpty()) { - QActionGroup* group { nullptr }; - if (!_exclusiveGroups.count(groupName)) { - group = _exclusiveGroups[groupName] = new QActionGroup(menu); - group->setExclusive(true); - } else { - group = _exclusiveGroups[groupName]; - } - group->addAction(action); - } - QObject::connect(action, &QAction::triggered, [=] { - onClicked(action->isChecked()); - }); - action->setCheckable(checkable); - action->setChecked(checked); +void PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { + getMenuCache().addMenuItem(getPrimaryMenu(), path, name, onClicked, checkable, checked, groupName); if (type == PluginType::DISPLAY_PLUGIN) { _currentDisplayPluginActions.push_back({ path, name }); } else { _currentInputPluginActions.push_back({ path, name }); } - return action; } void PluginContainer::removeMenuItem(const QString& menuName, const QString& menuItem) { - getPrimaryMenu()->removeMenuItem(menuName, menuItem); + getMenuCache().removeMenuItem(getPrimaryMenu(), menuName, menuItem); } bool PluginContainer::isOptionChecked(const QString& name) { - return getPrimaryMenu()->isOptionChecked(name); + return getMenuCache().isOptionChecked(getPrimaryMenu(), name); } void PluginContainer::setIsOptionChecked(const QString& path, bool checked) { @@ -161,3 +253,7 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) { Setting::Handle settingValue(settingName, value); return settingValue.set(value); } + +void PluginContainer::flushMenuUpdates() { + getMenuCache().flushCache(getPrimaryMenu()); +} diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h index 167af100b3..da9ea46cf4 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h @@ -46,7 +46,7 @@ public: void addMenu(const QString& menuName); void removeMenu(const QString& menuName); - QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = ""); + void addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = ""); void removeMenuItem(const QString& menuName, const QString& menuItem); bool isOptionChecked(const QString& name); void setIsOptionChecked(const QString& path, bool checked); @@ -77,9 +77,9 @@ public: } protected: + void flushMenuUpdates(); QVector> _currentDisplayPluginActions; QVector> _currentInputPluginActions; - std::map _exclusiveGroups; QRect _savedGeometry { 10, 120, 800, 600 }; };