Working on splash rendering

This commit is contained in:
Brad Davis 2018-04-04 13:33:37 -07:00
parent a0e1d2e54e
commit d478204722
8 changed files with 302 additions and 186 deletions

View file

@ -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 // Make sure the window is set to the correct size by processing the pending events
QCoreApplication::processEvents(); QCoreApplication::processEvents();
_glWidget->createContext(); _glWidget->createContext();
_glWidget->makeCurrent();
// Create the main thread context, the GPU backend, and the display plugins
initializeGL(); 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 // Make sure we don't time out during slow operations at startup
updateHeartbeat(); updateHeartbeat();
@ -2430,48 +2453,47 @@ void Application::initializeGL() {
_isGLInitialized = true; _isGLInitialized = true;
} }
// Build a shared canvas / context for the Chromium processes
_glWidget->makeCurrent(); _glWidget->makeCurrent();
glClearColor(0.2f, 0.2f, 0.2f, 1);
glClear(GL_COLOR_BUFFER_BIT);
_glWidget->swapBuffers();
#if !defined(DISABLE_QML) // Build an offscreen GL context for the main thread.
// Chromium rendering uses some GL functions that prevent nSight from capturing _offscreenContext = new OffscreenGLCanvas();
// frames, so we only create the shared context if nsight is NOT active. _offscreenContext->setObjectName("MainThreadContext");
if (!nsightActive()) { _offscreenContext->create(_glWidget->qglContext());
_chromiumShareContext = new OffscreenGLCanvas(); if (!_offscreenContext->makeCurrent()) {
_chromiumShareContext->setObjectName("ChromiumShareContext"); qFatal("Unable to make offscreen context current");
_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";
} }
#endif _offscreenContext->doneCurrent();
_offscreenContext->setThreadContext();
// Build a shared canvas / context for the QML rendering // Move the GL widget context to the render event handler thread
_glWidget->makeCurrent(); _renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
_qmlShareContext = new OffscreenGLCanvas(); if (!_offscreenContext->makeCurrent()) {
_qmlShareContext->setObjectName("QmlShareContext"); qFatal("Unable to make offscreen context current");
_qmlShareContext->create(_glWidget->qglContext());
if (!_qmlShareContext->makeCurrent()) {
qCWarning(interfaceapp, "Unable to make QML shared 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(); _glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>(); gpu::Context::init<gpu::gl::GLBackend>();
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram))); QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram)));
_gpuContext = std::make_shared<gpu::Context>();
// The gpu context can make child contexts for transfers, so
// we need to restore primary rendering context
_glWidget->makeCurrent(); _glWidget->makeCurrent();
_gpuContext = std::make_shared<gpu::Context>();
initDisplay(); // Restore the default main thread context
qCDebug(interfaceapp, "Initialized Display."); _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. // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
DeadlockWatchdogThread::withPause([&] { 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. // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines(); DependencyManager::get<GeometryCache>()->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(); extern void setupPreferences();
void Application::initializeUi() { 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 // Make sure all QML surfaces share the main thread GL context
OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext()); 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(); AddressBarDialog::registerType();
ErrorDialog::registerType(); ErrorDialog::registerType();
@ -2650,6 +2650,10 @@ void Application::initializeUi() {
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>(); auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); 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<EntityItemID> Application::pasteEntities(float x, float y, float z) {
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z);
} }
void Application::initDisplay() {
}
void Application::init() { void Application::init() {
_offscreenContext->makeCurrent();
// Make sure Login state is up to date // Make sure Login state is up to date
DependencyManager::get<DialogsManager>()->toggleLoginDialog(); DependencyManager::get<DialogsManager>()->toggleLoginDialog();
if (!DISABLE_DEFERRED) { if (!DISABLE_DEFERRED) {
@ -7500,21 +7501,34 @@ void Application::updateDisplayMode() {
qFatal("Attempted to switch display plugins from a non-main thread"); qFatal("Attempted to switch display plugins from a non-main thread");
} }
auto menu = Menu::getInstance();
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
// Once time initialization code
static std::once_flag once; static std::once_flag once;
std::call_once(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) { foreach(auto displayPlugin, displayPlugins) {
displayPlugin->setContext(_gpuContext); displayPlugin->setContext(_gpuContext);
auto grouping = displayPlugin->getGrouping(); QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
switch (grouping) { [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: case Plugin::ADVANCED:
advanced.push_back(displayPlugin); advanced.push_back(displayPlugin);
break; break;
@ -7524,42 +7538,40 @@ void Application::updateDisplayMode() {
default: default:
standard.push_back(displayPlugin); standard.push_back(displayPlugin);
break; break;
}
} }
}
// concatenate the groupings into a single list in the order: standard, advanced, 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(advanced), std::end(advanced));
standard.insert(std::end(standard), std::begin(developer), std::end(developer)); standard.insert(std::end(standard), std::begin(developer), std::end(developer));
foreach(auto displayPlugin, standard) { foreach(auto displayPlugin, standard) {
addDisplayPluginToMenu(displayPlugin, first); addDisplayPluginToMenu(displayPlugin, first);
auto displayPluginName = displayPlugin->getName(); first = false;
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { }
resizeGL();
});
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
first = false;
}
// after all plugins have been added to the menu, add a separator to the menu // 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);
auto parent = menu->getMenu(MenuOption::OutputMenu); parent->addSeparator();
parent->addSeparator(); });
});
}
// Default to the first item on the list, in case none of the menu items match // Default to the first item on the list, in case none of the menu items match
DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0); DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { if (menu) {
QString name = displayPlugin->getName(); foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
QAction* action = menu->getActionForOption(name); QString name = displayPlugin->getName();
// Menu might have been removed if the display plugin lost QAction* action = menu->getActionForOption(name);
if (!action) { // Menu might have been removed if the display plugin lost
continue; if (!action) {
} continue;
if (action->isChecked()) { }
newDisplayPlugin = displayPlugin; if (action->isChecked()) {
break; newDisplayPlugin = displayPlugin;
break;
}
} }
} }
@ -7567,8 +7579,13 @@ void Application::updateDisplayMode() {
return; return;
} }
setDisplayPlugin(newDisplayPlugin);
}
void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto desktop = offscreenUi->getDesktop(); auto desktop = offscreenUi->getDesktop();
auto menu = Menu::getInstance();
// Make the switch atomic from the perspective of other threads // Make the switch atomic from the perspective of other threads
{ {
@ -7589,6 +7606,8 @@ void Application::updateDisplayMode() {
bool active = newDisplayPlugin->activate(); bool active = newDisplayPlugin->activate();
if (!active) { if (!active) {
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
// If the new plugin fails to activate, fallback to last display // If the new plugin fails to activate, fallback to last display
qWarning() << "Failed to activate display: " << newDisplayPlugin->getName(); qWarning() << "Failed to activate display: " << newDisplayPlugin->getName();
newDisplayPlugin = oldDisplayPlugin; newDisplayPlugin = oldDisplayPlugin;
@ -7609,13 +7628,6 @@ void Application::updateDisplayMode() {
if (!active) { if (!active) {
qFatal("Failed to activate fallback plugin"); 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())); offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
@ -7642,14 +7654,21 @@ void Application::updateDisplayMode() {
getMyAvatar()->reset(false); getMyAvatar()->reset(false);
// switch to first person if entering hmd and setting is checked // switch to first person if entering hmd and setting is checked
if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { if (menu) {
menu->setIsOptionChecked(MenuOption::FirstPerson, true); QAction* action = menu->getActionForOption(newDisplayPlugin->getName());
cameraMenuChanged(); if (action) {
} action->setChecked(true);
}
// Remove the mirror camera option from menu if in HMD mode if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) {
auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); menu->setIsOptionChecked(MenuOption::FirstPerson, true);
mirrorAction->setVisible(!isHmd); 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"); 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) { void Application::setActiveDisplayPlugin(const QString& pluginName) {
auto menu = Menu::getInstance(); DisplayPluginPointer newDisplayPlugin;
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { for (DisplayPluginPointer displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) {
QString name = displayPlugin->getName(); QString name = displayPlugin->getName();
QAction* action = menu->getActionForOption(name);
if (pluginName == name) { if (pluginName == name) {
action->setChecked(true); newDisplayPlugin = displayPlugin;
break;
} }
} }
updateDisplayMode();
if (newDisplayPlugin) {
setDisplayPlugin(newDisplayPlugin);
}
} }
void Application::handleLocalServerConnection() const { void Application::handleLocalServerConnection() const {

View file

@ -145,6 +145,7 @@ public:
Q_INVOKABLE QString getUserAgent(); Q_INVOKABLE QString getUserAgent();
void initializeGL(); void initializeGL();
void initializeRenderEngine();
void initializeUi(); void initializeUi();
void updateCamera(RenderArgs& renderArgs, float deltaTime); void updateCamera(RenderArgs& renderArgs, float deltaTime);
@ -437,6 +438,7 @@ private slots:
static void packetSent(quint64 length); static void packetSent(quint64 length);
static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName); static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName);
void updateDisplayMode(); void updateDisplayMode();
void setDisplayPlugin(DisplayPluginPointer newPlugin);
void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo);
void addAssetToWorldCheckModelSize(); void addAssetToWorldCheckModelSize();
@ -449,7 +451,6 @@ private slots:
void switchDisplayMode(); void switchDisplayMode();
private: private:
static void initDisplay();
void init(); void init();
bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event);
bool handleFileOpenEvent(QFileOpenEvent* event); bool handleFileOpenEvent(QFileOpenEvent* event);

View file

@ -56,10 +56,8 @@ glm::mat4 StereoDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& basePr
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
std::vector<QAction*> _screenActions;
bool StereoDisplayPlugin::internalActivate() { bool StereoDisplayPlugin::internalActivate() {
auto screens = qApp->screens(); auto screens = qApp->screens();
_screenActions.resize(screens.size());
for (int i = 0; i < screens.size(); ++i) { for (int i = 0; i < screens.size(); ++i) {
auto screen = screens.at(i); auto screen = screens.at(i);
QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name()); QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name());
@ -67,9 +65,9 @@ bool StereoDisplayPlugin::internalActivate() {
if (screen == qApp->primaryScreen()) { if (screen == qApp->primaryScreen()) {
checked = true; checked = true;
} }
auto action = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name, const uint32_t screenIndex = i;
[this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name,
_screenActions[i] = action; [=](bool clicked) { updateScreen(screenIndex); }, true, checked, "Screens");
} }
_container->removeMenu(FRAMERATE); _container->removeMenu(FRAMERATE);
@ -80,18 +78,12 @@ bool StereoDisplayPlugin::internalActivate() {
return Parent::internalActivate(); return Parent::internalActivate();
} }
void StereoDisplayPlugin::updateScreen() { void StereoDisplayPlugin::updateScreen(uint32_t i) {
for (uint32_t i = 0; i < _screenActions.size(); ++i) { _screen = qApp->screens().at(i);
if (_screenActions[i]->isChecked()) { _container->setFullscreen(_screen);
_screen = qApp->screens().at(i);
_container->setFullscreen(_screen);
break;
}
}
} }
void StereoDisplayPlugin::internalDeactivate() { void StereoDisplayPlugin::internalDeactivate() {
_screenActions.clear();
_container->unsetFullscreen(); _container->unsetFullscreen();
Parent::internalDeactivate(); Parent::internalDeactivate();
} }

View file

@ -31,7 +31,7 @@ public:
protected: protected:
virtual bool internalActivate() override; virtual bool internalActivate() override;
virtual void internalDeactivate() override; virtual void internalDeactivate() override;
void updateScreen(); void updateScreen(uint32_t i);
float _ipd{ 0.064f }; float _ipd{ 0.064f };
QScreen* _screen; QScreen* _screen;

View file

@ -72,6 +72,10 @@ void GLWidget::createContext() {
_context->doneCurrent(); _context->doneCurrent();
} }
void GLWidget::swapBuffers() {
_context->swapBuffers();
}
bool GLWidget::makeCurrent() { bool GLWidget::makeCurrent() {
gl::Context::makeCurrent(_context->qglContext(), windowHandle()); gl::Context::makeCurrent(_context->qglContext(), windowHandle());
return _context->makeCurrent(); return _context->makeCurrent();

View file

@ -32,6 +32,7 @@ public:
void createContext(); void createContext();
bool makeCurrent(); bool makeCurrent();
void doneCurrent(); void doneCurrent();
void swapBuffers();
gl::Context* context() { return _context; } gl::Context* context() { return _context; }
QOpenGLContext* qglContext(); QOpenGLContext* qglContext();

View file

@ -34,48 +34,140 @@ PluginContainer::~PluginContainer() {
INSTANCE = nullptr; INSTANCE = nullptr;
}; };
struct MenuCache {
QSet<QString> menus;
struct Item {
QString path;
std::function<void(bool)> onClicked;
bool checkable;
bool checked;
QString groupName;
};
QHash<QString, Item> items;
std::map<QString, QActionGroup*> _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<void(bool)> 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) { void PluginContainer::addMenu(const QString& menuName) {
getPrimaryMenu()->addMenu(menuName); getMenuCache().addMenu(getPrimaryMenu(), menuName);
} }
void PluginContainer::removeMenu(const QString& 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<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) { void PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
auto menu = getPrimaryMenu(); getMenuCache().addMenuItem(getPrimaryMenu(), path, name, onClicked, checkable, checked, groupName);
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);
if (type == PluginType::DISPLAY_PLUGIN) { if (type == PluginType::DISPLAY_PLUGIN) {
_currentDisplayPluginActions.push_back({ path, name }); _currentDisplayPluginActions.push_back({ path, name });
} else { } else {
_currentInputPluginActions.push_back({ path, name }); _currentInputPluginActions.push_back({ path, name });
} }
return action;
} }
void PluginContainer::removeMenuItem(const QString& menuName, const QString& menuItem) { void PluginContainer::removeMenuItem(const QString& menuName, const QString& menuItem) {
getPrimaryMenu()->removeMenuItem(menuName, menuItem); getMenuCache().removeMenuItem(getPrimaryMenu(), menuName, menuItem);
} }
bool PluginContainer::isOptionChecked(const QString& name) { bool PluginContainer::isOptionChecked(const QString& name) {
return getPrimaryMenu()->isOptionChecked(name); return getMenuCache().isOptionChecked(getPrimaryMenu(), name);
} }
void PluginContainer::setIsOptionChecked(const QString& path, bool checked) { void PluginContainer::setIsOptionChecked(const QString& path, bool checked) {
@ -161,3 +253,7 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) {
Setting::Handle<bool> settingValue(settingName, value); Setting::Handle<bool> settingValue(settingName, value);
return settingValue.set(value); return settingValue.set(value);
} }
void PluginContainer::flushMenuUpdates() {
getMenuCache().flushCache(getPrimaryMenu());
}

View file

@ -46,7 +46,7 @@ public:
void addMenu(const QString& menuName); void addMenu(const QString& menuName);
void removeMenu(const QString& menuName); void removeMenu(const QString& menuName);
QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = ""); void addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "");
void removeMenuItem(const QString& menuName, const QString& menuItem); void removeMenuItem(const QString& menuName, const QString& menuItem);
bool isOptionChecked(const QString& name); bool isOptionChecked(const QString& name);
void setIsOptionChecked(const QString& path, bool checked); void setIsOptionChecked(const QString& path, bool checked);
@ -77,9 +77,9 @@ public:
} }
protected: protected:
void flushMenuUpdates();
QVector<QPair<QString, QString>> _currentDisplayPluginActions; QVector<QPair<QString, QString>> _currentDisplayPluginActions;
QVector<QPair<QString, QString>> _currentInputPluginActions; QVector<QPair<QString, QString>> _currentInputPluginActions;
std::map<QString, QActionGroup*> _exclusiveGroups;
QRect _savedGeometry { 10, 120, 800, 600 }; QRect _savedGeometry { 10, 120, 800, 600 };
}; };