diff --git a/include/ignition/gui/GuiEvents.hh b/include/ignition/gui/GuiEvents.hh index c08d6cf3e..bf140c741 100644 --- a/include/ignition/gui/GuiEvents.hh +++ b/include/ignition/gui/GuiEvents.hh @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -310,6 +311,46 @@ namespace ignition /// \brief Private data pointer IGN_UTILS_IMPL_PTR(dataPtr) }; + + /// \brief Event which is called to broadcast the key release within + /// the scene. + class IGNITION_GUI_VISIBLE KeyReleaseOnScene : public QEvent + { + /// \brief Constructor + /// \param[in] _key The key released event within the scene + public: explicit KeyReleaseOnScene(const common::KeyEvent &_key); + + /// \brief Unique type for this event. + static const QEvent::Type kType = QEvent::Type(QEvent::MaxUser - 8); + + /// \brief Get the released key within the scene that the user released. + /// \return The key code. + public: common::KeyEvent Key() const; + + /// \internal + /// \brief Private data pointer + IGN_UTILS_IMPL_PTR(dataPtr) + }; + + /// \brief Event which is called to broadcast the key press within + /// the scene. + class IGNITION_GUI_VISIBLE KeyPressOnScene : public QEvent + { + /// \brief Constructor + /// \param[in] _key The pressed key within the scene + public: explicit KeyPressOnScene(const common::KeyEvent &_key); + + /// \brief Unique type for this event. + static const QEvent::Type kType = QEvent::Type(QEvent::MaxUser - 9); + + /// \brief Get the key within the scene that the user pressed + /// \return The key code. + public: common::KeyEvent Key() const; + + /// \internal + /// \brief Private data pointer + IGN_UTILS_IMPL_PTR(dataPtr) + }; } } } diff --git a/src/GuiEvents.cc b/src/GuiEvents.cc index ab5eb2bae..c02a6996e 100644 --- a/src/GuiEvents.cc +++ b/src/GuiEvents.cc @@ -29,6 +29,18 @@ class ignition::gui::events::RightClickOnScene::Implementation public: common::MouseEvent mouse; }; +class ignition::gui::events::KeyReleaseOnScene::Implementation +{ + /// \brief Key event + public: common::KeyEvent key; +}; + +class ignition::gui::events::KeyPressOnScene::Implementation +{ + /// \brief Key event + public: common::KeyEvent key; +}; + using namespace ignition; using namespace gui; using namespace events; @@ -59,3 +71,30 @@ const common::MouseEvent &LeftClickOnScene::Mouse() const return this->dataPtr->mouse; } +///////////////////////////////////////////////// +KeyReleaseOnScene::KeyReleaseOnScene( + const common::KeyEvent &_key) + : QEvent(kType), dataPtr(utils::MakeImpl()) +{ + this->dataPtr->key = _key; +} + +///////////////////////////////////////////////// +common::KeyEvent KeyReleaseOnScene::Key() const +{ + return this->dataPtr->key; +} + +///////////////////////////////////////////////// +KeyPressOnScene::KeyPressOnScene( + const common::KeyEvent &_key) + : QEvent(kType), dataPtr(utils::MakeImpl()) +{ + this->dataPtr->key = _key; +} + +///////////////////////////////////////////////// +common::KeyEvent KeyPressOnScene::Key() const +{ + return this->dataPtr->key; +} diff --git a/src/GuiEvents_TEST.cc b/src/GuiEvents_TEST.cc index 74d3d38b1..a8f0fd489 100644 --- a/src/GuiEvents_TEST.cc +++ b/src/GuiEvents_TEST.cc @@ -115,6 +115,40 @@ TEST(GuiEventsTest, RightClickOnScene) EXPECT_FALSE(event.Mouse().Shift()); } +///////////////////////////////////////////////// +TEST(GuiEventsTest, KeyPressOnScene) +{ + ignition::common::KeyEvent key; + key.SetKey(49); + key.SetControl(true); + key.SetAlt(false); + key.SetShift(false); + events::KeyPressOnScene event(key); + + EXPECT_LT(QEvent::User, event.type()); + EXPECT_EQ(49, event.Key().Key()); + EXPECT_TRUE(event.Key().Control()); + EXPECT_FALSE(event.Key().Shift()); + EXPECT_FALSE(event.Key().Alt()); +} + +///////////////////////////////////////////////// +TEST(GuiEventsTest, KeyReleaseOnScene) +{ + ignition::common::KeyEvent key; + key.SetKey(49); + key.SetControl(true); + key.SetAlt(true); + key.SetShift(true); + events::KeyReleaseOnScene event(key); + + EXPECT_LT(QEvent::User, event.type()); + EXPECT_EQ(49, event.Key().Key()); + EXPECT_TRUE(event.Key().Control()); + EXPECT_TRUE(event.Key().Shift()); + EXPECT_TRUE(event.Key().Alt()); +} + ///////////////////////////////////////////////// TEST(GuiEventsTest, DropdownMenuEnabled) { diff --git a/src/plugins/scene3d/Scene3D.cc b/src/plugins/scene3d/Scene3D.cc index af8c47dc2..cc4e3c3fe 100644 --- a/src/plugins/scene3d/Scene3D.cc +++ b/src/plugins/scene3d/Scene3D.cc @@ -918,6 +918,8 @@ void IgnRenderer::HandleMouseEvent() this->BroadcastHoverPos(); this->BroadcastLeftClick(); this->BroadcastRightClick(); + this->BroadcastKeyPress(); + this->BroadcastKeyRelease(); this->HandleMouseViewControl(); } @@ -1010,7 +1012,7 @@ void IgnRenderer::HandleKeyRelease(QKeyEvent *_e) std::lock_guard lock(this->dataPtr->mutex); - this->dataPtr->keyEvent.SetKey(0); + this->dataPtr->keyEvent.SetKey(_e->key()); this->dataPtr->keyEvent.SetControl( (_e->modifiers() & Qt::ControlModifier) @@ -1084,6 +1086,28 @@ void IgnRenderer::BroadcastRightClick() App()->sendEvent(App()->findChild(), &rightClickOnSceneEvent); } +///////////////////////////////////////////////// +void IgnRenderer::BroadcastKeyRelease() +{ + if (this->dataPtr->keyEvent.Type() == common::KeyEvent::RELEASE) + { + events::KeyReleaseOnScene keyRelease(this->dataPtr->keyEvent); + App()->sendEvent(App()->findChild(), &keyRelease); + this->dataPtr->keyEvent.SetType(common::KeyEvent::NO_EVENT); + } +} + +///////////////////////////////////////////////// +void IgnRenderer::BroadcastKeyPress() +{ + if (this->dataPtr->keyEvent.Type() == common::KeyEvent::PRESS) + { + events::KeyPressOnScene keyPress(this->dataPtr->keyEvent); + App()->sendEvent(App()->findChild(), &keyPress); + this->dataPtr->keyEvent.SetType(common::KeyEvent::NO_EVENT); + } +} + ///////////////////////////////////////////////// void IgnRenderer::Initialize() { @@ -1665,6 +1689,18 @@ void RenderWindowItem::wheelEvent(QWheelEvent *_e) this->dataPtr->mouseEvent, math::Vector2d(scroll, scroll)); } +//////////////////////////////////////////////// +void RenderWindowItem::keyPressEvent(QKeyEvent *_event) +{ + this->HandleKeyPress(_event); +} + +//////////////////////////////////////////////// +void RenderWindowItem::keyReleaseEvent(QKeyEvent *_event) +{ + this->HandleKeyRelease(_event); +} + //////////////////////////////////////////////// void RenderWindowItem::HandleKeyPress(QKeyEvent *_e) { diff --git a/src/plugins/scene3d/Scene3D.hh b/src/plugins/scene3d/Scene3D.hh index db09195c9..468bd295b 100644 --- a/src/plugins/scene3d/Scene3D.hh +++ b/src/plugins/scene3d/Scene3D.hh @@ -145,6 +145,12 @@ namespace plugins /// \brief Broadcasts a right click within the scene private: void BroadcastRightClick(); + /// \brief Broadcasts the current key release + private: void BroadcastKeyRelease(); + + /// \brief Broadcasts the current key press + private: void BroadcastKeyPress(); + /// \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. @@ -318,6 +324,12 @@ namespace plugins // Documentation inherited protected: virtual void wheelEvent(QWheelEvent *_e) override; + // Documentation inherited + protected: virtual void keyPressEvent(QKeyEvent *_event) override; + + // Documentation inherited + protected: virtual void keyReleaseEvent(QKeyEvent *_event) override; + /// \brief Overrides the paint event to render the render engine /// camera view /// \param[in] _oldNode The node passed in previous updatePaintNode diff --git a/test/integration/scene3d.cc b/test/integration/scene3d.cc index 403bf1396..24f732123 100644 --- a/test/integration/scene3d.cc +++ b/test/integration/scene3d.cc @@ -179,9 +179,19 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) bool receivedLeftControlEvent{false}; bool receivedLeftShiftEvent{false}; bool receivedHoverEvent{false}; + bool receivedKeyPressEvent{false}; + bool receivedKeyPressEventAlt{false}; + bool receivedKeyPressEventControl{false}; + bool receivedKeyPressEventShift{false}; + bool receivedKeyReleaseEvent{false}; + bool receivedKeyReleaseEventAlt{false}; + bool receivedKeyReleaseEventControl{false}; + bool receivedKeyReleaseEventShift{false}; // Position vectors reported by click events math::Vector3d leftClickPoint, rightClickPoint; + // key pressed or released + int keyPressedValue, keyReleasedValue; // Helper to filter events auto testHelper = std::make_unique(); @@ -221,28 +231,72 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) { receivedHoverEvent = true; } + else if (_event->type() == events::KeyReleaseOnScene::kType) + { + receivedKeyReleaseEvent = true; + auto keyReleased = static_cast(_event); + keyReleasedValue = keyReleased->Key().Key(); + receivedKeyReleaseEventAlt = keyReleased->Key().Alt(); + receivedKeyReleaseEventControl = keyReleased->Key().Control(); + receivedKeyReleaseEventShift = keyReleased->Key().Shift(); + } + else if (_event->type() == events::KeyPressOnScene::kType) + { + receivedKeyPressEvent = true; + auto keyPress = static_cast(_event); + keyPressedValue = keyPress->Key().Key(); + receivedKeyPressEventAlt = keyPress->Key().Alt(); + receivedKeyPressEventControl = keyPress->Key().Control(); + receivedKeyPressEventShift = keyPress->Key().Shift(); + } }; int sleep = 0; int maxSleep = 30; - while ((!receivedRenderEvent || !receivedRightEvent || - !receivedLeftEvent || !receivedHoverEvent) && sleep < maxSleep) + while (!receivedRenderEvent && sleep < maxSleep) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); - + sleep++; + } + sleep = 0; + while (!receivedHoverEvent && sleep < maxSleep) + { QTest::mouseMove(win->QuickWindow(), QPoint(70, 100), -1); std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); - + sleep++; + } + sleep = 0; + while (!receivedRightEvent && sleep < maxSleep) + { QTest::mouseClick(win->QuickWindow(), Qt::RightButton, Qt::ShiftModifier); std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); - + sleep++; + } + sleep = 0; + while (!receivedLeftEvent && sleep < maxSleep) + { QTest::mouseClick(win->QuickWindow(), Qt::LeftButton, Qt::AltModifier); std::this_thread::sleep_for(std::chrono::milliseconds(100)); QCoreApplication::processEvents(); - + sleep++; + } + sleep = 0; + while (!receivedKeyPressEvent && sleep < maxSleep) + { + QTest::keyPress(win->QuickWindow(), Qt::Key_A, Qt::AltModifier); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); + sleep++; + } + sleep = 0; + while (!receivedKeyReleaseEvent && sleep < maxSleep) + { + QTest::keyRelease(win->QuickWindow(), Qt::Key_Escape, Qt::NoModifier); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QCoreApplication::processEvents(); sleep++; } @@ -262,6 +316,17 @@ TEST(Scene3DTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(Events)) EXPECT_NEAR(11.942695, leftClickPoint.Y(), 1e-4); EXPECT_NEAR(4.159424, leftClickPoint.Z(), 1e-4); + EXPECT_TRUE(receivedKeyReleaseEvent); + EXPECT_FALSE(receivedKeyReleaseEventAlt); + EXPECT_FALSE(receivedKeyReleaseEventControl); + EXPECT_FALSE(receivedKeyReleaseEventShift); + EXPECT_EQ(Qt::Key_Escape, keyReleasedValue); + EXPECT_TRUE(receivedKeyPressEvent); + EXPECT_TRUE(receivedKeyPressEventAlt); + EXPECT_FALSE(receivedKeyPressEventControl); + EXPECT_FALSE(receivedKeyPressEventShift); + EXPECT_EQ(Qt::Key_A, keyPressedValue); + // Cleanups auto plugins = win->findChildren(); for (auto & p : plugins)