From 0cc5704b6ad9dbe7af495938e452d7099f69fc89 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 8 Jun 2022 19:49:09 +0200 Subject: [PATCH] Add isGuiWindow check for mouse and keyboard callbacks Fix interaction problems when hovering/clicking with the mouse in a non-GUI window, when having a setup with more than one window (e.g. the single_gui.json setup) Previously, clicking with the mouse in the rendering window without the GUI also triggered mouse clicks in all the other windows, including the GUI windows. This was disturbing for interaction in the single_gui setup Now, the GUI modules check to see if the clicked window is actually an interaction window --- apps/OpenSpace/main.cpp | 37 ++++++++++---- include/openspace/engine/globalscallbacks.h | 23 ++++++--- include/openspace/engine/openspaceengine.h | 14 ++++-- modules/cefwebgui/src/guikeyboardhandler.cpp | 4 +- modules/imgui/imguimodule.cpp | 44 ++++++++++++----- modules/skybrowser/skybrowsermodule.cpp | 5 +- modules/webbrowser/src/eventhandler.cpp | 45 ++++++++++------- src/engine/globalscallbacks.cpp | 49 ++++++++---------- src/engine/openspaceengine.cpp | 52 +++++++++++--------- 9 files changed, 170 insertions(+), 103 deletions(-) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 8b581c6d20..1a9700ce4b 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -276,6 +276,15 @@ void checkJoystickStatus() { } } +bool isGuiWindow(sgct::Window* window) { + if (Engine::instance().windows().size() == 1) { + // If we only have one window, assume it's also the GUI window. + // It might not have been given the 'GUI' tag + return true; + } + return window->hasTag("GUI"); +} + // // Init function // @@ -574,7 +583,7 @@ void mainPostDrawFunc() { void mainKeyboardCallback(sgct::Key key, sgct::Modifier modifiers, sgct::Action action, - int) + int, sgct::Window* window) { ZoneScoped LTRACE("main::mainKeyboardCallback(begin)"); @@ -582,7 +591,9 @@ void mainKeyboardCallback(sgct::Key key, sgct::Modifier modifiers, sgct::Action const openspace::Key k = openspace::Key(key); const KeyModifier m = KeyModifier(modifiers); const KeyAction a = KeyAction(action); - global::openSpaceEngine->keyboardCallback(k, m, a); + const IsGuiWindow isGui = IsGuiWindow(isGuiWindow(window)); + + global::openSpaceEngine->keyboardCallback(k, m, a, isGui); LTRACE("main::mainKeyboardCallback(begin)"); } @@ -590,7 +601,7 @@ void mainKeyboardCallback(sgct::Key key, sgct::Modifier modifiers, sgct::Action void mainMouseButtonCallback(sgct::MouseButton key, sgct::Modifier modifiers, - sgct::Action action) + sgct::Action action, sgct::Window* window) { ZoneScoped LTRACE("main::mainMouseButtonCallback(begin)"); @@ -598,36 +609,42 @@ void mainMouseButtonCallback(sgct::MouseButton key, sgct::Modifier modifiers, const openspace::MouseButton k = openspace::MouseButton(key); const openspace::MouseAction a = openspace::MouseAction(action); const openspace::KeyModifier m = openspace::KeyModifier(modifiers); - global::openSpaceEngine->mouseButtonCallback(k, a, m); + const IsGuiWindow isGui = IsGuiWindow(isGuiWindow(window)); + + global::openSpaceEngine->mouseButtonCallback(k, a, m, isGui); LTRACE("main::mainMouseButtonCallback(end)"); } -void mainMousePosCallback(double x, double y) { +void mainMousePosCallback(double x, double y, sgct::Window* window) { ZoneScoped - global::openSpaceEngine->mousePositionCallback(x, y); + const IsGuiWindow isGui = IsGuiWindow(isGuiWindow(window)); + global::openSpaceEngine->mousePositionCallback(x, y, isGui); } -void mainMouseScrollCallback(double posX, double posY) { +void mainMouseScrollCallback(double posX, double posY, sgct::Window* window) { ZoneScoped LTRACE("main::mainMouseScrollCallback(begin"); - global::openSpaceEngine->mouseScrollWheelCallback(posX, posY); + const IsGuiWindow isGui = IsGuiWindow(isGuiWindow(window)); + global::openSpaceEngine->mouseScrollWheelCallback(posX, posY, isGui); LTRACE("main::mainMouseScrollCallback(end)"); } -void mainCharCallback(unsigned int codepoint, int modifiers) { +void mainCharCallback(unsigned int codepoint, int modifiers, sgct::Window* window) { ZoneScoped const KeyModifier m = KeyModifier(modifiers); - global::openSpaceEngine->charCallback(codepoint, m); + const IsGuiWindow isGui = IsGuiWindow(isGuiWindow(window)); + + global::openSpaceEngine->charCallback(codepoint, m, isGui); } diff --git a/include/openspace/engine/globalscallbacks.h b/include/openspace/engine/globalscallbacks.h index fe095749ae..c6363785a8 100644 --- a/include/openspace/engine/globalscallbacks.h +++ b/include/openspace/engine/globalscallbacks.h @@ -28,11 +28,23 @@ #include #include #include +#include #include #include +namespace openspace { + BooleanType(IsGuiWindow); +} // namespace openspace + namespace openspace::global::callback { +using KeyboardCallback = std::function; +using CharacterCallback = std::function; +using MouseButtonCallback = + std::function; +using MousePositionCallback = std::function; +using MouseScrollWheelCallback = std::function; + inline std::vector>* initialize; inline std::vector>* deinitialize; inline std::vector>* initializeGL; @@ -42,12 +54,11 @@ inline std::vector>* postSyncPreDraw; inline std::vector>* render; inline std::vector>* draw2D; inline std::vector>* postDraw; -inline std::vector>* keyboard; -inline std::vector>* character; -inline std::vector>* - mouseButton; -inline std::vector>* mousePosition; -inline std::vector>* mouseScrollWheel; +inline std::vector* keyboard; +inline std::vector* character; +inline std::vector* mouseButton; +inline std::vector* mousePosition; +inline std::vector* mouseScrollWheel; inline std::vector>* touchDetected; inline std::vector>* touchUpdated; inline std::vector>* touchExit; diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 6bd1de4781..7f715b4629 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -25,6 +25,7 @@ #ifndef __OPENSPACE_CORE___OPENSPACEENGINE___H__ #define __OPENSPACE_CORE___OPENSPACEENGINE___H__ +#include #include #include #include @@ -92,11 +93,14 @@ public: void drawOverlays(); void postDraw(); void resetPropertyChangeFlags(); - void keyboardCallback(Key key, KeyModifier mod, KeyAction action); - void charCallback(unsigned int codepoint, KeyModifier modifier); - void mouseButtonCallback(MouseButton button, MouseAction action, KeyModifier mods); - void mousePositionCallback(double x, double y); - void mouseScrollWheelCallback(double posX, double posY); + void keyboardCallback(Key key, KeyModifier mod, KeyAction action, + IsGuiWindow isGuiWindow); + void charCallback(unsigned int codepoint, KeyModifier modifier, + IsGuiWindow isGuiWindow); + void mouseButtonCallback(MouseButton button, MouseAction action, + KeyModifier mods, IsGuiWindow isGuiWindow); + void mousePositionCallback(double x, double y, IsGuiWindow isGuiWindow); + void mouseScrollWheelCallback(double posX, double posY, IsGuiWindow isGuiWindow); void touchDetectionCallback(TouchInput input); void touchUpdateCallback(TouchInput input); void touchExitCallback(TouchInput input); diff --git a/modules/cefwebgui/src/guikeyboardhandler.cpp b/modules/cefwebgui/src/guikeyboardhandler.cpp index 82b7e75d50..7672649fdc 100644 --- a/modules/cefwebgui/src/guikeyboardhandler.cpp +++ b/modules/cefwebgui/src/guikeyboardhandler.cpp @@ -32,10 +32,10 @@ GUIKeyboardHandler::GUIKeyboardHandler() { _keyConsumed = false; global::callback::keyboard->emplace_back( - [&](Key, KeyModifier, KeyAction) -> bool { + [&](Key, KeyModifier, KeyAction, IsGuiWindow isGuiWindow) -> bool { const bool previous = _keyConsumed; _keyConsumed = false; - return previous; + return isGuiWindow ? previous : false; } ); } diff --git a/modules/imgui/imguimodule.cpp b/modules/imgui/imguimodule.cpp index fd94491b86..621154902a 100644 --- a/modules/imgui/imguimodule.cpp +++ b/modules/imgui/imguimodule.cpp @@ -153,31 +153,50 @@ ImGUIModule::ImGUIModule() }); global::callback::keyboard->emplace_back( - [&](Key key, KeyModifier mod, KeyAction action) -> bool { + [&](Key key, KeyModifier mod, KeyAction action, + IsGuiWindow isGuiWindow) -> bool + { ZoneScopedN("ImGUI") - return _isEnabled ? keyCallback(key, mod, action) : false; + if (!isGuiWindow || !_isEnabled) { + return false; + } + return keyCallback(key, mod, action); } ); global::callback::character->emplace_back( - [&](unsigned int codepoint, KeyModifier modifier) -> bool { + [&](unsigned int codepoint, KeyModifier modifier, + IsGuiWindow isGuiWindow) -> bool + { ZoneScopedN("ImGUI") - return _isEnabled ? charCallback(codepoint, modifier) : false; + if (!isGuiWindow || !_isEnabled) { + return false; + } + return charCallback(codepoint, modifier); } ); global::callback::mousePosition->emplace_back( - [&](double x, double y) { + [&](double x, double y, IsGuiWindow isGuiWindow) { + if (!isGuiWindow) { + return; // do nothing + } _mousePosition = glm::vec2(static_cast(x), static_cast(y)); } ); global::callback::mouseButton->emplace_back( - [&](MouseButton button, MouseAction action, KeyModifier) -> bool { + [&](MouseButton button, MouseAction action, KeyModifier, + IsGuiWindow isGuiWindow) -> bool + { ZoneScopedN("ImGUI") + if (!isGuiWindow) { + return false; + } + if (action == MouseAction::Press) { _mouseButtons |= (1 << static_cast(button)); } @@ -190,10 +209,13 @@ ImGUIModule::ImGUIModule() ); global::callback::mouseScrollWheel->emplace_back( - [&](double, double posY) -> bool { + [&](double, double posY, IsGuiWindow isGuiWindow) -> bool { ZoneScopedN("ImGUI") - return _isEnabled ? mouseWheelCallback(posY) : false; + if (!isGuiWindow || !_isEnabled) { + return false; + } + return mouseWheelCallback(posY); } ); @@ -225,7 +247,7 @@ void ImGUIModule::internalInitialize(const ghoul::Dictionary&) { const std::vector& nodes = scene ? scene->allSceneGraphNodes() : std::vector(); - + return std::vector(nodes.begin(), nodes.end()); } ); @@ -412,7 +434,7 @@ void ImGUIModule::internalInitializeGL() { sizeof(ImDrawVert), nullptr ); - + glEnableVertexAttribArray(uvAttrib); glVertexAttribPointer( uvAttrib, @@ -422,7 +444,7 @@ void ImGUIModule::internalInitializeGL() { sizeof(ImDrawVert), reinterpret_cast(offsetof(ImDrawVert, uv)) ); - + glEnableVertexAttribArray(colorAttrib); glVertexAttribPointer( colorAttrib, diff --git a/modules/skybrowser/skybrowsermodule.cpp b/modules/skybrowser/skybrowsermodule.cpp index 7fdb129768..9bac48e218 100644 --- a/modules/skybrowser/skybrowsermodule.cpp +++ b/modules/skybrowser/skybrowsermodule.cpp @@ -166,8 +166,9 @@ SkyBrowserModule::SkyBrowserModule() _wwtImageCollectionUrl.setReadOnly(true); // Set callback functions - global::callback::mouseButton->emplace(global::callback::mouseButton->begin(), - [&](MouseButton, MouseAction action, KeyModifier) -> bool { + global::callback::mouseButton->emplace( + global::callback::mouseButton->begin(), + [&](MouseButton button, MouseAction action, KeyModifier, IsGuiWindow) -> bool { if (action == MouseAction::Press) { _cameraRotation.stop(); } diff --git a/modules/webbrowser/src/eventhandler.cpp b/modules/webbrowser/src/eventhandler.cpp index a1f91d369d..b8f963a939 100644 --- a/modules/webbrowser/src/eventhandler.cpp +++ b/modules/webbrowser/src/eventhandler.cpp @@ -149,33 +149,41 @@ namespace { namespace openspace { void EventHandler::initialize() { - global::callback::character->emplace(global::callback::character->begin(), - [this](unsigned int charCode, KeyModifier mod) -> bool { - if (_browserInstance) { + global::callback::character->emplace( + global::callback::character->begin(), + [this](unsigned int charCode, KeyModifier mod, IsGuiWindow isGuiWindow) -> bool { + if (_browserInstance && isGuiWindow) { return charCallback(charCode, mod); } return false; } ); - global::callback::keyboard->emplace(global::callback::keyboard->begin(), - [this](Key key, KeyModifier mod, KeyAction action) -> bool { - if (_browserInstance) { + global::callback::keyboard->emplace( + global::callback::keyboard->begin(), + [this](Key key, KeyModifier mod, KeyAction action, + IsGuiWindow isGuiWindow) -> bool + { + if (_browserInstance && isGuiWindow) { return keyboardCallback(key, mod, action); } return false; } ); - global::callback::mousePosition->emplace(global::callback::mousePosition->begin(), - [this](double x, double y) -> bool { - if (_browserInstance) { + global::callback::mousePosition->emplace( + global::callback::mousePosition->begin(), + [this](double x, double y, IsGuiWindow isGuiWindow) -> bool { + if (_browserInstance && isGuiWindow) { return mousePositionCallback(x, y); } return false; } ); - [this](MouseButton button, MouseAction action, KeyModifier mods) -> bool { - if (_browserInstance) { - global::callback::mouseButton->emplace(global::callback::mouseButton->begin(), + global::callback::mouseButton->emplace( + global::callback::mouseButton->begin(), + [this](MouseButton button, MouseAction action, + KeyModifier mods, IsGuiWindow isGuiWindow) -> bool + { + if (_browserInstance && isGuiWindow) { return mouseButtonCallback(button, action, mods); } return false; @@ -183,15 +191,16 @@ void EventHandler::initialize() { ); global::callback::mouseScrollWheel->emplace( global::callback::mouseScrollWheel->begin(), - [this](double x, double y) -> bool { - if (_browserInstance) { + [this](double x, double y, IsGuiWindow isGuiWindow) -> bool { + if (_browserInstance && isGuiWindow) { return mouseWheelCallback(glm::ivec2(x, y)); } return false; } ); - global::callback::touchDetected->emplace(global::callback::touchDetected->begin(), + global::callback::touchDetected->emplace( + global::callback::touchDetected->begin(), [&](TouchInput input) -> bool { if (!_browserInstance) { return false; @@ -230,7 +239,8 @@ void EventHandler::initialize() { } ); - global::callback::touchUpdated->emplace(global::callback::touchUpdated->begin(), + global::callback::touchUpdated->emplace( + global::callback::touchUpdated->begin(), [&](TouchInput input) -> bool { if (!_browserInstance || _validTouchStates.empty()) { return false; @@ -268,7 +278,8 @@ void EventHandler::initialize() { } ); - global::callback::touchExit->emplace(global::callback::touchExit->begin(), + global::callback::touchExit->emplace( + global::callback::touchExit->begin(), [&](TouchInput input) { if (!_browserInstance) { return; diff --git a/src/engine/globalscallbacks.cpp b/src/engine/globalscallbacks.cpp index e5cfbb329c..f04aacff05 100644 --- a/src/engine/globalscallbacks.cpp +++ b/src/engine/globalscallbacks.cpp @@ -43,11 +43,11 @@ namespace { sizeof(std::vector>) + sizeof(std::vector>) + sizeof(std::vector>) + - sizeof(std::vector>) + - sizeof(std::vector>) + - sizeof(std::vector>) + - sizeof(std::vector>) + - sizeof(std::vector>) + + sizeof(std::vector) + + sizeof(std::vector) + + sizeof(std::vector) + + sizeof(std::vector) + + sizeof(std::vector) + sizeof(std::vector>) + sizeof(std::vector>) + sizeof(std::vector>); @@ -138,51 +138,44 @@ void create() { #endif // WIN32 #ifdef WIN32 - keyboard = - new (currentPos) std::vector>; + keyboard = new (currentPos) std::vector ghoul_assert(keyboard, "No keyboard"); - currentPos += sizeof(std::vector>); + currentPos += sizeof(std::vector); #else // ^^^ WIN32 / !WIN32 vvv - keyboard = new std::vector>; + keyboard = new std::vector; #endif // WIN32 #ifdef WIN32 - character = - new (currentPos) std::vector>; + character = new (currentPos) std::vector; ghoul_assert(character, "No character"); - currentPos += sizeof(std::vector>); + currentPos += sizeof(std::vector); #else // ^^^ WIN32 / !WIN32 vvv - character = new std::vector>; + character = new std::vector; #endif // WIN32 #ifdef WIN32 - mouseButton = - new (currentPos) std::vector< - std::function - >; + mouseButton = new (currentPos) std::vector; ghoul_assert(mouseButton, "No mouseButton"); - currentPos += sizeof( - std::vector> - ); + currentPos += sizeof(std::vector); #else // ^^^ WIN32 / !WIN32 vvv - mouseButton = - new std::vector>; + mouseButton = new std::vector; #endif // WIN32 #ifdef WIN32 - mousePosition = new (currentPos) std::vector>; + mousePosition = + new (currentPos) std::vector; ghoul_assert(mousePosition, "No mousePosition"); - currentPos += sizeof(std::vector>); + currentPos += sizeof(std::vector); #else // ^^^ WIN32 / !WIN32 vvv - mousePosition = new std::vector>; + mousePosition = new std::vector; #endif // WIN32 #ifdef WIN32 - mouseScrollWheel = new (currentPos) std::vector>; + mouseScrollWheel = new (currentPos) std::vector; ghoul_assert(mouseScrollWheel, "No mouseScrollWheel"); - currentPos += sizeof(std::vector>); + currentPos += sizeof(std::vector); #else // ^^^ WIN32 / !WIN32 vvv - mouseScrollWheel = new std::vector>; + mouseScrollWheel = new std::vector; #endif // WIN32 #ifdef WIN32 diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 0d4e371ef9..8e9308742a 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -1408,7 +1407,9 @@ void OpenSpaceEngine::resetPropertyChangeFlagsOfSubowners(properties::PropertyOw } } -void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action) { +void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action, + IsGuiWindow isGuiWindow) +{ ZoneScoped if (_loadingScreen) { @@ -1421,9 +1422,9 @@ void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction actio return; } - using F = std::function; + using F = global::callback::KeyboardCallback; for (const F& func : *global::callback::keyboard) { - const bool isConsumed = func(key, mod, action); + const bool isConsumed = func(key, mod, action, isGuiWindow); if (isConsumed) { return; } @@ -1441,12 +1442,14 @@ void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction actio global::interactionMonitor->markInteraction(); } -void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) { +void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier, + IsGuiWindow isGuiWindow) +{ ZoneScoped - using F = std::function; + using F = global::callback::CharacterCallback; for (const F& func : *global::callback::character) { - bool isConsumed = func(codepoint, modifier); + bool isConsumed = func(codepoint, modifier, isGuiWindow); if (isConsumed) { return; } @@ -1456,22 +1459,22 @@ void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) global::interactionMonitor->markInteraction(); } -void OpenSpaceEngine::mouseButtonCallback(MouseButton button, - MouseAction action, - KeyModifier mods) +void OpenSpaceEngine::mouseButtonCallback(MouseButton button, MouseAction action, + KeyModifier mods, IsGuiWindow isGuiWindow) { ZoneScoped - using F = std::function; + using F = global::callback::MouseButtonCallback; for (const F& func : *global::callback::mouseButton) { - bool isConsumed = func(button, action, mods); + bool isConsumed = func(button, action, mods, isGuiWindow); if (isConsumed) { // If the mouse was released, we still want to forward it to the navigation - // handler in order to reliably terminate a rotation or zoom. Accidentally + // handler in order to reliably terminate a rotation or zoom, or to the other + // callbacks to for example release a drag and drop of a UI window. Accidentally // moving the cursor over a UI window is easy to miss and leads to weird // continuing movement if (action == MouseAction::Release) { - break; + continue; } else { return; @@ -1479,8 +1482,9 @@ void OpenSpaceEngine::mouseButtonCallback(MouseButton button, } } - // Check if the user clicked on one of the 'buttons' the RenderEngine is drawing - if (action == MouseAction::Press) { + // Check if the user clicked on one of the 'buttons' the RenderEngine is drawing. + // Only handle the clicks if we are in a GUI window + if (action == MouseAction::Press && isGuiWindow) { bool isConsumed = global::renderEngine->mouseActivationCallback(_mousePosition); if (isConsumed) { return; @@ -1491,12 +1495,14 @@ void OpenSpaceEngine::mouseButtonCallback(MouseButton button, global::interactionMonitor->markInteraction(); } -void OpenSpaceEngine::mousePositionCallback(double x, double y) { +void OpenSpaceEngine::mousePositionCallback(double x, double y, + IsGuiWindow isGuiWindow) +{ ZoneScoped - using F = std::function; + using F = global::callback::MousePositionCallback; for (const F& func : *global::callback::mousePosition) { - func(x, y); + func(x, y, isGuiWindow); } global::navigationHandler->mousePositionCallback(x, y); @@ -1505,12 +1511,14 @@ void OpenSpaceEngine::mousePositionCallback(double x, double y) { _mousePosition = glm::vec2(static_cast(x), static_cast(y)); } -void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) { +void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY, + IsGuiWindow isGuiWindow) +{ ZoneScoped - using F = std::function; + using F = global::callback::MouseScrollWheelCallback; for (const F& func : *global::callback::mouseScrollWheel) { - bool isConsumed = func(posX, posY); + bool isConsumed = func(posX, posY, isGuiWindow); if (isConsumed) { return; }