From 2eb2ee054c984b6573c6fbf4f7edcf84f2fc29de Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Fri, 16 Apr 2021 17:53:12 -0700 Subject: [PATCH 01/13] Start porting events from ign-gazebo Signed-off-by: Louise Poubel --- src/plugins/scene3d/CMakeLists.txt | 2 +- src/plugins/scene3d/Scene3D.cc | 201 +++++++++++++++++++++++++--- src/plugins/scene3d/Scene3D.hh | 49 +++++++ src/plugins/scene3d/Scene3D_TEST.cc | 87 ++++++------ 4 files changed, 275 insertions(+), 64 deletions(-) diff --git a/src/plugins/scene3d/CMakeLists.txt b/src/plugins/scene3d/CMakeLists.txt index 469226031..e16e8d536 100644 --- a/src/plugins/scene3d/CMakeLists.txt +++ b/src/plugins/scene3d/CMakeLists.txt @@ -4,7 +4,7 @@ ign_gui_add_plugin(Scene3D QT_HEADERS Scene3D.hh TEST_SOURCES - # Scene3D_TEST.cc + Scene3D_TEST.cc PUBLIC_LINK_LIBS ${IGNITION-RENDERING_LIBRARIES} ) diff --git a/src/plugins/scene3d/Scene3D.cc b/src/plugins/scene3d/Scene3D.cc index 93a9b0180..fe0a2b519 100644 --- a/src/plugins/scene3d/Scene3D.cc +++ b/src/plugins/scene3d/Scene3D.cc @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -207,9 +208,15 @@ namespace plugins /// \brief Flag to indicate if mouse event is dirty public: bool mouseDirty = false; + /// \brief Flag to indicate if hover event is dirty + public: bool hoverDirty = false; + /// \brief Mouse event public: common::MouseEvent mouseEvent; + /// \brief Key event + public: common::KeyEvent keyEvent; + /// \brief Mouse move distance since last event. public: math::Vector2d drag; @@ -222,6 +229,9 @@ namespace plugins /// \brief Camera orbit controller public: rendering::OrbitViewController viewControl; + /// \brief The currently hovered mouse position in screen coordinates + public: math::Vector2i mouseHoverPos{math::Vector2i::Zero}; + /// \brief Ray query for mouse clicks public: rendering::RayQueryPtr rayQuery; @@ -885,6 +895,15 @@ void IgnRenderer::Render() void IgnRenderer::HandleMouseEvent() { std::lock_guard lock(this->dataPtr->mutex); + this->BroadcastHoverPos(); + this->BroadcastLeftClick(); + this->BroadcastRightClick(); + this->HandleMouseViewControl(); +} + +///////////////////////////////////////////////// +void IgnRenderer::HandleMouseViewControl() +{ if (!this->dataPtr->mouseDirty) return; @@ -939,6 +958,106 @@ void IgnRenderer::HandleMouseEvent() this->dataPtr->mouseDirty = false; } +//////////////////////////////////////////////// +void IgnRenderer::HandleKeyPress(QKeyEvent *_e) +{ + if (_e->isAutoRepeat()) + return; + + std::lock_guard lock(this->dataPtr->mutex); + + this->dataPtr->keyEvent.SetKey(_e->key()); + this->dataPtr->keyEvent.SetText(_e->text().toStdString()); + + this->dataPtr->keyEvent.SetControl( + (_e->modifiers() & Qt::ControlModifier)); + this->dataPtr->keyEvent.SetShift( + (_e->modifiers() & Qt::ShiftModifier)); + this->dataPtr->keyEvent.SetAlt( + (_e->modifiers() & Qt::AltModifier)); + + this->dataPtr->mouseEvent.SetControl(this->dataPtr->keyEvent.Control()); + this->dataPtr->mouseEvent.SetShift(this->dataPtr->keyEvent.Shift()); + this->dataPtr->mouseEvent.SetAlt(this->dataPtr->keyEvent.Alt()); + this->dataPtr->keyEvent.SetType(common::KeyEvent::PRESS); +} + +//////////////////////////////////////////////// +void IgnRenderer::HandleKeyRelease(QKeyEvent *_e) +{ + if (_e->isAutoRepeat()) + return; + + std::lock_guard lock(this->dataPtr->mutex); + + this->dataPtr->keyEvent.SetKey(0); + + this->dataPtr->keyEvent.SetControl( + (_e->modifiers() & Qt::ControlModifier) + && (_e->key() != Qt::Key_Control)); + this->dataPtr->keyEvent.SetShift( + (_e->modifiers() & Qt::ShiftModifier) + && (_e->key() != Qt::Key_Shift)); + this->dataPtr->keyEvent.SetAlt( + (_e->modifiers() & Qt::AltModifier) + && (_e->key() != Qt::Key_Alt)); + + this->dataPtr->mouseEvent.SetControl(this->dataPtr->keyEvent.Control()); + this->dataPtr->mouseEvent.SetShift(this->dataPtr->keyEvent.Shift()); + this->dataPtr->mouseEvent.SetAlt(this->dataPtr->keyEvent.Alt()); + this->dataPtr->keyEvent.SetType(common::KeyEvent::RELEASE); +} + +///////////////////////////////////////////////// +void IgnRenderer::BroadcastHoverPos() +{ + if (!this->dataPtr->hoverDirty) + return; + + auto pos = this->ScreenToScene(this->dataPtr->mouseHoverPos); + + events::HoverToScene hoverToSceneEvent(pos); + App()->sendEvent(App()->findChild(), &hoverToSceneEvent); +} + +///////////////////////////////////////////////// +void IgnRenderer::BroadcastLeftClick() +{ + if (!this->dataPtr->mouseDirty) + return; + + if (this->dataPtr->mouseEvent.Dragging()) + return; + + if (this->dataPtr->mouseEvent.Button() != common::MouseEvent::LEFT || + this->dataPtr->mouseEvent.Type() != common::MouseEvent::RELEASE) + return; + + auto pos = this->ScreenToScene(this->dataPtr->mouseEvent.Pos()); + + events::LeftClickToScene leftClickToSceneEvent(pos); + App()->sendEvent(App()->findChild(), &leftClickToSceneEvent); +} + +///////////////////////////////////////////////// +void IgnRenderer::BroadcastRightClick() +{ + if (!this->dataPtr->mouseDirty) + return; + + if (this->dataPtr->mouseEvent.Dragging()) + return; + + if (this->dataPtr->mouseEvent.Button() != common::MouseEvent::RIGHT || + this->dataPtr->mouseEvent.Type() != common::MouseEvent::RELEASE) + return; + + auto pos = this->ScreenToScene(this->dataPtr->mouseEvent.Pos()); + + events::RightClickToScene rightClickToSceneEvent(pos); + App()->sendEvent(App()->findChild(), &rightClickToSceneEvent); +} + ///////////////////////////////////////////////// void IgnRenderer::Initialize() { @@ -1016,6 +1135,14 @@ void IgnRenderer::Destroy() } } +///////////////////////////////////////////////// +void IgnRenderer::NewHoverEvent(const math::Vector2i &_hoverPos) +{ + std::lock_guard lock(this->dataPtr->mutex); + this->dataPtr->mouseHoverPos = _hoverPos; + this->dataPtr->hoverDirty = true; +} + ///////////////////////////////////////////////// void IgnRenderer::NewMouseEvent(const common::MouseEvent &_e, const math::Vector2d &_drag) @@ -1455,6 +1582,12 @@ void Scene3D::LoadConfig(const tinyxml2::XMLElement *_pluginElem) } } +///////////////////////////////////////////////// +void RenderWindowItem::OnHovered(const ignition::math::Vector2i &_hoverPos) +{ + this->dataPtr->renderThread->ignRenderer.NewHoverEvent(_hoverPos); +} + ///////////////////////////////////////////////// void RenderWindowItem::mousePressEvent(QMouseEvent *_e) { @@ -1505,23 +1638,57 @@ void RenderWindowItem::wheelEvent(QWheelEvent *_e) this->dataPtr->mouseEvent, math::Vector2d(scroll, scroll)); } -/////////////////////////////////////////////////// -// void Scene3D::resizeEvent(QResizeEvent *_e) -// { -// if (this->dataPtr->renderWindow) -// { -// this->dataPtr->renderWindow->OnResize(_e->size().width(), -// _e->size().height()); -// } -// -// if (this->dataPtr->camera) -// { -// this->dataPtr->camera->SetAspectRatio( -// static_cast(this->width()) / this->height()); -// this->dataPtr->camera->SetHFOV(M_PI * 0.5); -// } -// } -// +//////////////////////////////////////////////// +void RenderWindowItem::HandleKeyPress(QKeyEvent *_e) +{ + this->dataPtr->renderThread->ignRenderer.HandleKeyPress(_e); +} + +//////////////////////////////////////////////// +void RenderWindowItem::HandleKeyRelease(QKeyEvent *_e) +{ + this->dataPtr->renderThread->ignRenderer.HandleKeyRelease(_e); +} + +///////////////////////////////////////////////// +bool Scene3D::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(_event); + if (keyEvent) + { + auto renderWindow = this->PluginItem()->findChild(); + renderWindow->HandleKeyPress(keyEvent); + } + } + else if (_event->type() == QEvent::KeyRelease) + { + QKeyEvent *keyEvent = static_cast(_event); + if (keyEvent) + { + auto renderWindow = this->PluginItem()->findChild(); + renderWindow->HandleKeyRelease(keyEvent); + } + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} + +///////////////////////////////////////////////// +void Scene3D::OnHovered(int _mouseX, int _mouseY) +{ + auto renderWindow = this->PluginItem()->findChild(); + renderWindow->OnHovered({_mouseX, _mouseY}); +} + +///////////////////////////////////////////////// +void Scene3D::OnFocusWindow() +{ + auto renderWindow = this->PluginItem()->findChild(); + renderWindow->forceActiveFocus(); +} // Register this plugin IGNITION_ADD_PLUGIN(ignition::gui::plugins::Scene3D, diff --git a/src/plugins/scene3d/Scene3D.hh b/src/plugins/scene3d/Scene3D.hh index 1c51054be..859d39605 100644 --- a/src/plugins/scene3d/Scene3D.hh +++ b/src/plugins/scene3d/Scene3D.hh @@ -68,6 +68,18 @@ namespace plugins /// \brief Destructor public: virtual ~Scene3D(); + /// \brief Callback when the mouse hovers to a new position. + /// \param[in] _mouseX x coordinate of the hovered mouse position. + /// \param[in] _mouseY y coordinate of the hovered mouse position. + public slots: void OnHovered(int _mouseX, int _mouseY); + + /// \brief Callback when the mouse enters the render window to + /// focus the window for mouse/key events + public slots: void OnFocusWindow(); + + // Documentation inherited + protected: bool eventFilter(QObject *_obj, QEvent *_event) override; + // Documentation inherited public: virtual void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override; @@ -106,9 +118,33 @@ namespace plugins public: void NewMouseEvent(const common::MouseEvent &_e, const math::Vector2d &_drag = math::Vector2d::Zero); + /// \brief New hover event triggered. + /// \param[in] _hoverPos Mouse hover screen position + public: void NewHoverEvent(const math::Vector2i &_hoverPos); + + /// \brief Handle key press event for snapping + /// \param[in] _e The key event to process. + public: void HandleKeyPress(QKeyEvent *_e); + + /// \brief Handle key release event for snapping + /// \param[in] _e The key event to process. + public: void HandleKeyRelease(QKeyEvent *_e); + /// \brief Handle mouse event for view control private: void HandleMouseEvent(); + /// \brief Handle mouse event for view control + private: void HandleMouseViewControl(); + + /// \brief Broadcasts the currently hovered 3d scene location. + private: void BroadcastHoverPos(); + + /// \brief Broadcasts a left click within the scene + private: void BroadcastLeftClick(); + + /// \brief Broadcasts a right click within the scene + private: void BroadcastRightClick(); + /// \brief Retrieve the first point on a surface in the 3D scene hit by a /// ray cast from the given 2D screen coordinates. /// \param[in] _screenPos 2D coordinates on the screen, in pixels. @@ -254,9 +290,22 @@ namespace plugins /// \param[in] _topic Scene topic public: void SetSceneTopic(const std::string &_topic); + /// \brief Called when the mouse hovers to a new position. + /// \param[in] _hoverPos 2D coordinates of the hovered mouse position on + /// the render window. + public: void OnHovered(const ignition::math::Vector2i &_hoverPos); + /// \brief Slot called when thread is ready to be started public Q_SLOTS: void Ready(); + /// \brief Handle key press event for snapping + /// \param[in] _e The key event to process. + public: void HandleKeyPress(QKeyEvent *_e); + + /// \brief Handle key release event for snapping + /// \param[in] _e The key event to process. + public: void HandleKeyRelease(QKeyEvent *_e); + // Documentation inherited protected: virtual void mousePressEvent(QMouseEvent *_e) override; diff --git a/src/plugins/scene3d/Scene3D_TEST.cc b/src/plugins/scene3d/Scene3D_TEST.cc index 8db1ad7ee..b461cc475 100644 --- a/src/plugins/scene3d/Scene3D_TEST.cc +++ b/src/plugins/scene3d/Scene3D_TEST.cc @@ -19,64 +19,57 @@ #include #include #include -#include - -#include "ignition/gui/Iface.hh" -#include "ignition/gui/MainWindow.hh" +#include +#include +#include +#include +#include + +#include "test_config.h" // NOLINT(build/include) +#include "ignition/gui/Application.hh" #include "ignition/gui/Plugin.hh" +#include "ignition/gui/MainWindow.hh" + +#include "Scene3D.hh" + +int g_argc = 1; +char **g_argv = new char *[g_argc]; using namespace ignition; using namespace gui; ///////////////////////////////////////////////// -TEST(Scene3DTest, Load) +TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Load)) { - setVerbosity(4); - EXPECT_TRUE(initApp()); - - EXPECT_TRUE(loadPlugin("Scene3D")); + common::Console::SetVerbosity(4); - EXPECT_TRUE(stop()); -} + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); -///////////////////////////////////////////////// -TEST(Scene3DTest, Resize) -{ - setVerbosity(4); - EXPECT_TRUE(initApp()); + EXPECT_TRUE(app.LoadPlugin("Scene3D")); - // Load plugin - EXPECT_TRUE(loadPlugin("Scene3D")); + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); - // Create main window - EXPECT_TRUE(createMainWindow()); + // Get plugin + auto plugins = win->findChildren(); + EXPECT_EQ(plugins.size(), 1); - // Close window after some time - auto win = mainWindow(); - ASSERT_NE(nullptr, win); + auto plugin = plugins[0]; + EXPECT_EQ(plugin->Title(), "3D Scene"); - QTimer::singleShot(300, [&win]() - { - // Check there are no segfaults when resizing - for (auto i : {100, 300, 200, 500, 400}) - { - win->resize(i + (qrand() % 100), i + (qrand() % 100)); - QCoreApplication::processEvents(); - } - win->close(); - }); - - // Show window - EXPECT_TRUE(runMainWindow()); - - EXPECT_TRUE(stop()); + // Cleanup + plugins.clear(); } ///////////////////////////////////////////////// -TEST(Scene3DTest, Config) +TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Config)) { - setVerbosity(4); - EXPECT_TRUE(initApp()); + common::Console::SetVerbosity(4); + + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); // Load plugin const char *pluginStr = @@ -90,11 +83,15 @@ TEST(Scene3DTest, Config) tinyxml2::XMLDocument pluginDoc; pluginDoc.Parse(pluginStr); - EXPECT_TRUE(ignition::gui::loadPlugin("Scene3D", + EXPECT_TRUE(app.LoadPlugin("Scene3D", pluginDoc.FirstChildElement("plugin"))); - // Create main window - EXPECT_TRUE(createMainWindow()); + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); + + // Show, but don't exec, so we don't block + win->QuickWindow()->show(); // Check scene auto engine = rendering::engine("ogre"); @@ -116,7 +113,5 @@ TEST(Scene3DTest, Config) ASSERT_NE(nullptr, camera); EXPECT_EQ(math::Pose3d(1, 2, 3, 0, 0, 1.57), camera->WorldPose()); - - EXPECT_TRUE(stop()); } From 8299a15bf5459adb05810afccd6254e9b61c12b5 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Thu, 29 Apr 2021 11:27:37 -0700 Subject: [PATCH 02/13] Remove test that fails due to #216 Signed-off-by: Louise Poubel --- src/plugins/scene3d/Scene3D_TEST.cc | 58 ----------------------------- 1 file changed, 58 deletions(-) diff --git a/src/plugins/scene3d/Scene3D_TEST.cc b/src/plugins/scene3d/Scene3D_TEST.cc index b461cc475..8120ed751 100644 --- a/src/plugins/scene3d/Scene3D_TEST.cc +++ b/src/plugins/scene3d/Scene3D_TEST.cc @@ -17,12 +17,6 @@ #include #include -#include -#include -#include -#include -#include -#include #include #include "test_config.h" // NOLINT(build/include) @@ -63,55 +57,3 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Load)) plugins.clear(); } -///////////////////////////////////////////////// -TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Config)) -{ - common::Console::SetVerbosity(4); - - Application app(g_argc, g_argv); - app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); - - // Load plugin - const char *pluginStr = - "" - "ogre" - "banana" - "1.0 0 0" - "0 1 0" - "1 2 3 0 0 1.57" - ""; - - tinyxml2::XMLDocument pluginDoc; - pluginDoc.Parse(pluginStr); - EXPECT_TRUE(app.LoadPlugin("Scene3D", - pluginDoc.FirstChildElement("plugin"))); - - // Get main window - auto win = app.findChild(); - ASSERT_NE(nullptr, win); - - // Show, but don't exec, so we don't block - win->QuickWindow()->show(); - - // Check scene - auto engine = rendering::engine("ogre"); - ASSERT_NE(nullptr, engine); - - auto scene = engine->SceneByName("banana"); - ASSERT_NE(nullptr, scene); - - EXPECT_EQ(math::Color(0, 1, 0), scene->BackgroundColor()); - EXPECT_EQ(math::Color(1, 0, 0), scene->AmbientLight()); - - auto root = scene->RootVisual(); - ASSERT_NE(nullptr, root); - EXPECT_EQ(1u, root->ChildCount()); - - // Check camera - auto camera = std::dynamic_pointer_cast( - root->ChildByIndex(0)); - ASSERT_NE(nullptr, camera); - - EXPECT_EQ(math::Pose3d(1, 2, 3, 0, 0, 1.57), camera->WorldPose()); -} - From 899b97a0ce15efbbd93640669592a367c68a510c Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Thu, 6 May 2021 11:51:05 -0700 Subject: [PATCH 03/13] Move Scene3d_TEST.cc to test/integration There seems to be a problem with loading the ignition-rendering-ogre plugin from the Scene3D test if it links to that plugin. Making Scene3D_TEST.cc into an integration test works because it doesn't directly call any plugin methods. This also changes the linking for the Grid3D plugin to only link to the ignition-rendering core library target instead of the IGNITION-RENDERING_LIBRARIES variable which includes the ogre component library plugins. Signed-off-by: Steve Peters --- src/plugins/grid_3d/CMakeLists.txt | 2 +- src/plugins/scene3d/CMakeLists.txt | 4 +--- test/integration/CMakeLists.txt | 7 ++++++- .../scene3d/Scene3D_TEST.cc => test/integration/scene3d.cc | 3 +-- 4 files changed, 9 insertions(+), 7 deletions(-) rename src/plugins/scene3d/Scene3D_TEST.cc => test/integration/scene3d.cc (98%) diff --git a/src/plugins/grid_3d/CMakeLists.txt b/src/plugins/grid_3d/CMakeLists.txt index 6ce5eb707..444b3f566 100644 --- a/src/plugins/grid_3d/CMakeLists.txt +++ b/src/plugins/grid_3d/CMakeLists.txt @@ -6,6 +6,6 @@ ign_gui_add_plugin(Grid3D TEST_SOURCES # Grid3D_TEST.cc PUBLIC_LINK_LIBS - ${IGNITION-RENDERING_LIBRARIES} + ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER} ) diff --git a/src/plugins/scene3d/CMakeLists.txt b/src/plugins/scene3d/CMakeLists.txt index e16e8d536..5679bfcf7 100644 --- a/src/plugins/scene3d/CMakeLists.txt +++ b/src/plugins/scene3d/CMakeLists.txt @@ -3,9 +3,7 @@ ign_gui_add_plugin(Scene3D Scene3D.cc QT_HEADERS Scene3D.hh - TEST_SOURCES - Scene3D_TEST.cc PUBLIC_LINK_LIBS - ${IGNITION-RENDERING_LIBRARIES} + ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER} ) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 397fabacb..38a25dc78 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -1,3 +1,8 @@ ign_get_sources(tests) -ign_build_tests(TYPE INTEGRATION SOURCES ${tests}) +ign_build_tests( + TYPE INTEGRATION + SOURCES ${tests} + LIB_DEPS + ignition-plugin${IGN_PLUGIN_VER}::loader + ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER}) diff --git a/src/plugins/scene3d/Scene3D_TEST.cc b/test/integration/scene3d.cc similarity index 98% rename from src/plugins/scene3d/Scene3D_TEST.cc rename to test/integration/scene3d.cc index b461cc475..78fac6d10 100644 --- a/src/plugins/scene3d/Scene3D_TEST.cc +++ b/test/integration/scene3d.cc @@ -30,8 +30,6 @@ #include "ignition/gui/Plugin.hh" #include "ignition/gui/MainWindow.hh" -#include "Scene3D.hh" - int g_argc = 1; char **g_argv = new char *[g_argc]; @@ -97,6 +95,7 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Config)) auto engine = rendering::engine("ogre"); ASSERT_NE(nullptr, engine); + EXPECT_EQ(1u, engine->SceneCount()); auto scene = engine->SceneByName("banana"); ASSERT_NE(nullptr, scene); From 341db2ada3959e18230a4904de0e8db18575fd0a Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Fri, 7 May 2021 15:58:57 -0700 Subject: [PATCH 04/13] process qt events to allow scene to initialize Signed-off-by: Steve Peters --- test/integration/scene3d.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 78fac6d10..5a78cc096 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -95,6 +95,15 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Config)) auto engine = rendering::engine("ogre"); ASSERT_NE(nullptr, engine); + int sleep = 0; + int maxSleep = 30; + while (0 == engine->SceneCount() && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + EXPECT_EQ(1u, engine->SceneCount()); auto scene = engine->SceneByName("banana"); ASSERT_NE(nullptr, scene); From 23c057865ed3f50268bd4a94f6ac57c03fb25973 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Fri, 14 May 2021 16:42:56 -0700 Subject: [PATCH 05/13] Add test helper to check event Signed-off-by: Louise Poubel --- src/plugins/scene3d/Scene3D.qml | 17 +++++++++- test/CMakeLists.txt | 1 + test/helpers/CMakeLists.txt | 14 ++++++++ test/helpers/TestHelper.hh | 57 +++++++++++++++++++++++++++++++ test/integration/CMakeLists.txt | 1 + test/integration/scene3d.cc | 60 +++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 test/helpers/CMakeLists.txt create mode 100644 test/helpers/TestHelper.hh diff --git a/src/plugins/scene3d/Scene3D.qml b/src/plugins/scene3d/Scene3D.qml index 304ccf186..cb7a399e7 100644 --- a/src/plugins/scene3d/Scene3D.qml +++ b/src/plugins/scene3d/Scene3D.qml @@ -14,7 +14,7 @@ * limitations under the License. * */ -import QtQuick 2.0 +import QtQuick 2.9 import QtQuick.Controls 2.0 import RenderWindow 1.0 import QtGraphicalEffects 1.0 @@ -28,6 +28,21 @@ Rectangle { */ property bool gammaCorrect: false + /** + * Get mouse position on 3D widget + */ + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: { + Scene3D.OnFocusWindow() + } + onPositionChanged: { + Scene3D.OnHovered(mouseArea.mouseX, mouseArea.mouseY); + } + } RenderWindow { id: renderWindow diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6dd8ad952..d0d40bd18 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,6 +18,7 @@ execute_process(COMMAND cmake -E remove_directory ${CMAKE_BINARY_DIR}/test_resul execute_process(COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/test_results) include_directories(${GTEST_INCLUDE_DIRS}) +add_subdirectory(helpers) add_subdirectory(integration) add_subdirectory(performance) add_subdirectory(plugins) diff --git a/test/helpers/CMakeLists.txt b/test/helpers/CMakeLists.txt new file mode 100644 index 000000000..571812a88 --- /dev/null +++ b/test/helpers/CMakeLists.txt @@ -0,0 +1,14 @@ +set (qt_headers + TestHelper.hh +) + +QT5_WRAP_CPP(headers_MOC ${qt_headers}) + +add_library(${PROJECT_NAME}_test_helpers SHARED + ${headers_MOC} +) +target_link_libraries(${PROJECT_NAME}_test_helpers + PUBLIC + ${PROJECT_LIBRARY_TARGET_NAME} +) + diff --git a/test/helpers/TestHelper.hh b/test/helpers/TestHelper.hh new file mode 100644 index 000000000..36b12077d --- /dev/null +++ b/test/helpers/TestHelper.hh @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef IGNITION_GUI_TESTHELPER_HH_ +#define IGNITION_GUI_TESTHELPER_HH_ + +#include +#include +#include + +namespace ignition +{ +namespace gui +{ +/// \brief +class IGNITION_GUI_VISIBLE TestHelper : public QObject +{ + Q_OBJECT + + /// \brief Constructor + public: TestHelper() + { + App()->findChild()->installEventFilter(this); + }; + + /// \brief Destructor + public: ~TestHelper() = default; + + /// \brief Documentation inherited + public: bool eventFilter(QObject *_obj, QEvent *_event) override + { + if (this->forwardEvent) + this->forwardEvent(_event); + + // Standard event processing + return QObject::eventFilter(_obj, _event); + } + + public: std::function forwardEvent; +}; +} +} + +#endif diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 38a25dc78..2ee1ac044 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -4,5 +4,6 @@ ign_build_tests( TYPE INTEGRATION SOURCES ${tests} LIB_DEPS + ${PROJECT_NAME}_test_helpers ignition-plugin${IGN_PLUGIN_VER}::loader ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER}) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 5a78cc096..676a8d6d4 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -26,7 +26,9 @@ #include #include "test_config.h" // NOLINT(build/include) +#include "../helpers/TestHelper.hh" #include "ignition/gui/Application.hh" +#include "ignition/gui/GuiEvents.hh" #include "ignition/gui/Plugin.hh" #include "ignition/gui/MainWindow.hh" @@ -123,3 +125,61 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Config)) EXPECT_EQ(math::Pose3d(1, 2, 3, 0, 0, 1.57), camera->WorldPose()); } +///////////////////////////////////////////////// +TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) +{ + common::Console::SetVerbosity(4); + + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + // Load plugin + const char *pluginStr = + "" + "ogre" + "banana" + "1.0 0 0" + "0 1 0" + "1 2 3 0 0 1.57" + ""; + + tinyxml2::XMLDocument pluginDoc; + pluginDoc.Parse(pluginStr); + EXPECT_TRUE(app.LoadPlugin("Scene3D", + pluginDoc.FirstChildElement("plugin"))); + + // Get main window + auto win = app.findChild(); + ASSERT_NE(nullptr, win); + + // Show, but don't exec, so we don't block + win->QuickWindow()->show(); + + // Flags to check if events were received + bool receivedRenderEvent{false}; + + // Helper to filter events + auto testHelper = std::make_unique(); + testHelper->forwardEvent = [&](QEvent *_event) + { + if (_event->type() == events::Render::kType) + { + receivedRenderEvent = true; + } + }; + + int sleep = 0; + int maxSleep = 30; + while (!receivedRenderEvent && sleep < maxSleep) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + + EXPECT_TRUE(receivedRenderEvent); + + // TODO(anyone) Test more events +} + + From a38108bc97bc59e9b3f07eb3443cedf2b26831e4 Mon Sep 17 00:00:00 2001 From: ahcorde Date: Tue, 25 May 2021 20:57:17 +0200 Subject: [PATCH 06/13] added more tests Signed-off-by: ahcorde --- test/integration/CMakeLists.txt | 6 ++++- test/integration/scene3d.cc | 39 ++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 2ee1ac044..aeb5c8f1f 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -1,9 +1,13 @@ ign_get_sources(tests) +find_package(Qt5Test REQUIRED) + ign_build_tests( TYPE INTEGRATION SOURCES ${tests} LIB_DEPS ${PROJECT_NAME}_test_helpers ignition-plugin${IGN_PLUGIN_VER}::loader - ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER}) + ignition-rendering${IGN_RENDERING_VER}::ignition-rendering${IGN_RENDERING_VER} + Qt5::Test +) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 676a8d6d4..af57541bb 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -157,6 +158,9 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) // Flags to check if events were received bool receivedRenderEvent{false}; + bool receivedRightEvent{false}; + bool receivedLeftEvent{false}; + bool receivedHoverEvent{false}; // Helper to filter events auto testHelper = std::make_unique(); @@ -166,20 +170,45 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) { receivedRenderEvent = true; } + else if (_event->type() == events::RightClickToScene::kType) + { + receivedRightEvent = true; + } + else if (_event->type() == events::LeftClickToScene::kType) + { + receivedLeftEvent = true; + } + else if (_event->type() == events::HoverToScene::kType) + { + receivedHoverEvent = true; + } }; int sleep = 0; int maxSleep = 30; - while (!receivedRenderEvent && sleep < maxSleep) + while ((!receivedRenderEvent || !receivedRightEvent || + !receivedLeftEvent || !receivedHoverEvent) && sleep < maxSleep) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); + + QTest::mouseMove(win->QuickWindow(), QPoint(70 + sleep, 100), -1); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + + QTest::mouseClick(win->QuickWindow(), Qt::RightButton, Qt::NoModifier); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + + QTest::mouseClick(win->QuickWindow(), Qt::LeftButton, Qt::NoModifier); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; } EXPECT_TRUE(receivedRenderEvent); - - // TODO(anyone) Test more events + EXPECT_TRUE(receivedLeftEvent); + EXPECT_TRUE(receivedRightEvent); + EXPECT_TRUE(receivedHoverEvent); } - - From acfa9b82a0a7437111ac2de1b7ca5468471f1bc7 Mon Sep 17 00:00:00 2001 From: ahcorde Date: Tue, 25 May 2021 22:03:49 +0200 Subject: [PATCH 07/13] make linters happy Signed-off-by: ahcorde --- src/plugins/scene3d/Scene3D.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/scene3d/Scene3D.hh b/src/plugins/scene3d/Scene3D.hh index 859d39605..db09195c9 100644 --- a/src/plugins/scene3d/Scene3D.hh +++ b/src/plugins/scene3d/Scene3D.hh @@ -325,7 +325,7 @@ namespace plugins /// \param[in] _data The node transformation data. /// \return Updated node. private: QSGNode *updatePaintNode(QSGNode *_oldNode, - QQuickItem::UpdatePaintNodeData *_data) override; + QQuickItem::UpdatePaintNodeData *_data) override; /// \internal /// \brief Pointer to private data. From 67ed9a773e20147bc3f783027c6f958e9e04e55b Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Tue, 25 May 2021 14:05:00 -0700 Subject: [PATCH 08/13] Move TestHelper code to .cc file, fix windows? Signed-off-by: Steve Peters --- test/helpers/CMakeLists.txt | 1 + test/helpers/TestHelper.cc | 38 +++++++++++++++++++++++++++++++++++++ test/helpers/TestHelper.hh | 14 ++------------ 3 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 test/helpers/TestHelper.cc diff --git a/test/helpers/CMakeLists.txt b/test/helpers/CMakeLists.txt index 571812a88..6476d26d1 100644 --- a/test/helpers/CMakeLists.txt +++ b/test/helpers/CMakeLists.txt @@ -5,6 +5,7 @@ set (qt_headers QT5_WRAP_CPP(headers_MOC ${qt_headers}) add_library(${PROJECT_NAME}_test_helpers SHARED + TestHelper.cc ${headers_MOC} ) target_link_libraries(${PROJECT_NAME}_test_helpers diff --git a/test/helpers/TestHelper.cc b/test/helpers/TestHelper.cc new file mode 100644 index 000000000..e7d70984d --- /dev/null +++ b/test/helpers/TestHelper.cc @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "TestHelper.hh" + +namespace ignition +{ +namespace gui +{ +TestHelper::TestHelper() +{ + App()->findChild()->installEventFilter(this); +} + +bool TestHelper::eventFilter(QObject *_obj, QEvent *_event) +{ + if (this->forwardEvent) + this->forwardEvent(_event); + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} +} +} diff --git a/test/helpers/TestHelper.hh b/test/helpers/TestHelper.hh index 36b12077d..ccb841768 100644 --- a/test/helpers/TestHelper.hh +++ b/test/helpers/TestHelper.hh @@ -31,23 +31,13 @@ class IGNITION_GUI_VISIBLE TestHelper : public QObject Q_OBJECT /// \brief Constructor - public: TestHelper() - { - App()->findChild()->installEventFilter(this); - }; + public: TestHelper(); /// \brief Destructor public: ~TestHelper() = default; /// \brief Documentation inherited - public: bool eventFilter(QObject *_obj, QEvent *_event) override - { - if (this->forwardEvent) - this->forwardEvent(_event); - - // Standard event processing - return QObject::eventFilter(_obj, _event); - } + public: bool eventFilter(QObject *_obj, QEvent *_event) override; public: std::function forwardEvent; }; From d925bee5900de94fd032db49a411f072385ccad1 Mon Sep 17 00:00:00 2001 From: ahcorde Date: Wed, 26 May 2021 09:17:59 +0200 Subject: [PATCH 09/13] Fix windows? Signed-off-by: ahcorde --- test/helpers/TestHelper.hh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/helpers/TestHelper.hh b/test/helpers/TestHelper.hh index ccb841768..8c2fee0f9 100644 --- a/test/helpers/TestHelper.hh +++ b/test/helpers/TestHelper.hh @@ -21,12 +21,22 @@ #include #include +#ifndef _WIN32 +# define TestHelper_EXPORTS_API +#else +# if (defined(TestHelper_EXPORTS)) +# define TestHelper_EXPORTS_API __declspec(dllexport) +# else +# define TestHelper_EXPORTS_API __declspec(dllimport) +# endif +#endif + namespace ignition { namespace gui { /// \brief -class IGNITION_GUI_VISIBLE TestHelper : public QObject +class TestHelper_EXPORTS_API TestHelper : public QObject { Q_OBJECT From 53cbb419c851e9da58e17b133dcd579a044321d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Herna=CC=81ndez?= Date: Wed, 26 May 2021 15:48:52 +0200 Subject: [PATCH 10/13] Fix windows? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alejandro Hernández --- test/helpers/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/CMakeLists.txt b/test/helpers/CMakeLists.txt index 6476d26d1..46af75c2a 100644 --- a/test/helpers/CMakeLists.txt +++ b/test/helpers/CMakeLists.txt @@ -8,8 +8,8 @@ add_library(${PROJECT_NAME}_test_helpers SHARED TestHelper.cc ${headers_MOC} ) +target_compile_definitions(${PROJECT_NAME}_test_helpers PRIVATE TestHelper_EXPORTS) target_link_libraries(${PROJECT_NAME}_test_helpers PUBLIC ${PROJECT_LIBRARY_TARGET_NAME} ) - From 48171dd6ff162271cc8161ef529bcb3320f9a7b3 Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Wed, 26 May 2021 23:55:07 -0700 Subject: [PATCH 11/13] Expect values of Vector3 point in click events Signed-off-by: Steve Peters --- test/integration/scene3d.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index af57541bb..8377bd435 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -162,6 +162,9 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) bool receivedLeftEvent{false}; bool receivedHoverEvent{false}; + // Position vectors reported by click events + math::Vector3d leftClickPoint, rightClickPoint; + // Helper to filter events auto testHelper = std::make_unique(); testHelper->forwardEvent = [&](QEvent *_event) @@ -173,14 +176,21 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) else if (_event->type() == events::RightClickToScene::kType) { receivedRightEvent = true; + auto rightClickToScene = static_cast(_event); + rightClickPoint = rightClickToScene->Point(); } else if (_event->type() == events::LeftClickToScene::kType) { receivedLeftEvent = true; + auto leftClickToScene = static_cast(_event); + leftClickPoint = leftClickToScene->Point(); } else if (_event->type() == events::HoverToScene::kType) { receivedHoverEvent = true; + auto hoverToScene = static_cast(_event); + std::cerr << "hoverToScene point[" + << hoverToScene->Point() << "]" << std::endl; } }; @@ -192,7 +202,7 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); - QTest::mouseMove(win->QuickWindow(), QPoint(70 + sleep, 100), -1); + QTest::mouseMove(win->QuickWindow(), QPoint(70, 100), -1); std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); @@ -211,4 +221,9 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) EXPECT_TRUE(receivedLeftEvent); EXPECT_TRUE(receivedRightEvent); EXPECT_TRUE(receivedHoverEvent); + + EXPECT_EQ(leftClickPoint, rightClickPoint); + EXPECT_NEAR(1.0, leftClickPoint.X(), 1e-4); + EXPECT_NEAR(11.942695, leftClickPoint.Y(), 1e-4); + EXPECT_NEAR(4.159424, leftClickPoint.Z(), 1e-4); } From 039ef43ec668fbc9adf99acc44eafc3f221af3dc Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Thu, 27 May 2021 00:10:47 -0700 Subject: [PATCH 12/13] Remove debug message Signed-off-by: Steve Peters --- test/integration/scene3d.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 8377bd435..7def7bd73 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -189,8 +189,6 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) { receivedHoverEvent = true; auto hoverToScene = static_cast(_event); - std::cerr << "hoverToScene point[" - << hoverToScene->Point() << "]" << std::endl; } }; From e75cff43e6d64fa0ce5d3ec3823b5c0d3cd51282 Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Thu, 27 May 2021 00:24:45 -0700 Subject: [PATCH 13/13] Remove unused variable Signed-off-by: Steve Peters --- test/integration/scene3d.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 7def7bd73..ea9dc239e 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -188,7 +188,6 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) else if (_event->type() == events::HoverToScene::kType) { receivedHoverEvent = true; - auto hoverToScene = static_cast(_event); } };