diff --git a/doc/changelog.dox b/doc/changelog.dox index 236dc9aeb..d953209f0 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1394,7 +1394,8 @@ See also: @relativeref{Platform::Sdl2Application,pointerMoveEvent()}, @relativeref{Platform::Sdl2Application,PointerEvent} and @relativeref{Platform::Sdl2Application,PointerMoveEvent} APIs that provide - a better abstraction over general pointer input, not just a mouse alone + a better abstraction over general pointer input, not just a mouse alone. + The same change is done in @ref Platform::GlfwApplication. - @cpp Shaders::DistanceFieldVector @ce, @cpp Shaders::Flat @ce, @cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce, @cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce, diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index d48cfa8df..27f621ab3 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -606,6 +606,52 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf } #endif +namespace { + +GlfwApplication::Pointer buttonToPointer(const int button) { + switch(button) { + case GLFW_MOUSE_BUTTON_LEFT: + return GlfwApplication::Pointer::MouseLeft; + case GLFW_MOUSE_BUTTON_MIDDLE: + return GlfwApplication::Pointer::MouseMiddle; + case GLFW_MOUSE_BUTTON_RIGHT: + return GlfwApplication::Pointer::MouseRight; + case GLFW_MOUSE_BUTTON_4: + return GlfwApplication::Pointer::MouseButton4; + case GLFW_MOUSE_BUTTON_5: + return GlfwApplication::Pointer::MouseButton5; + case GLFW_MOUSE_BUTTON_6: + return GlfwApplication::Pointer::MouseButton6; + case GLFW_MOUSE_BUTTON_7: + return GlfwApplication::Pointer::MouseButton7; + case GLFW_MOUSE_BUTTON_8: + return GlfwApplication::Pointer::MouseButton8; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); +} + +GlfwApplication::Pointers currentGlfwPointers(GLFWwindow* const window) { + GlfwApplication::Pointers pointers; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseLeft; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseMiddle; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseRight; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_4) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseButton4; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_5) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseButton5; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_6) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseButton6; + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_7) == GLFW_PRESS) + pointers |= GlfwApplication::Pointer::MouseButton7; + return pointers; +} + +} + void GlfwApplication::setupCallbacks() { glfwSetWindowUserPointer(_window, this); glfwSetWindowCloseCallback(_window, [](GLFWwindow* const window){ @@ -644,25 +690,41 @@ void GlfwApplication::setupCallbacks() { glfwSetMouseButtonCallback(_window, [](GLFWwindow* const window, const int button, const int action, const int mods) { auto& app = *static_cast(glfwGetWindowUserPointer(window)); + const Pointer pointer = buttonToPointer(button); double x, y; glfwGetCursorPos(window, &x, &y); - MouseEvent e(static_cast(button), {Int(x), Int(y)}, {static_cast(mods)}); - - if(action == GLFW_PRESS) /* we don't handle GLFW_REPEAT */ - app.mousePressEvent(e); - else if(action == GLFW_RELEASE) - app.mouseReleaseEvent(e); + const Vector2 position{Float(x), Float(y)}; + + /* If an additional mouse button was pressed or some buttons are still + left pressed after a release, call a move event instead */ + const Pointers pointers = currentGlfwPointers(window); + if((action == GLFW_PRESS && (pointers & ~pointer)) || + (action == GLFW_RELEASE && pointers)) { + PointerMoveEvent e{window, pointer, position, {}}; + /* We had to query the pointers already and get the modifiers in + the callback, set them directly instead of having them lazily + populated later */ + e._pointers = pointers; + e._modifiers = InputEvent::Modifiers{mods}; + app.pointerMoveEvent(e); + } else { + PointerEvent e{pointer, position, InputEvent::Modifiers{mods}}; + if(action == GLFW_PRESS) /* we don't handle GLFW_REPEAT */ + app.pointerPressEvent(e); + else if(action == GLFW_RELEASE) + app.pointerReleaseEvent(e); + } }); glfwSetCursorPosCallback(_window, [](GLFWwindow* const window, const double x, const double y) { auto& app = *static_cast(glfwGetWindowUserPointer(window)); /* Avoid bogus offset at first -- report 0 when the event is called for the first time */ - Vector2i position{Int(x), Int(y)}; - MouseMoveEvent e{window, position, - app._previousMouseMovePosition == Vector2i{-1} ? Vector2i{} : + const Vector2 position{Float(x), Float(y)}; + PointerMoveEvent e{window, {}, position, + Math::isNan(app._previousMouseMovePosition).all() ? Vector2{} : position - app._previousMouseMovePosition}; app._previousMouseMovePosition = position; - app.mouseMoveEvent(e); + app.pointerMoveEvent(e); }); glfwSetScrollCallback(_window, [](GLFWwindow* window, double xoffset, double yoffset) { MouseScrollEvent e(window, Vector2{Float(xoffset), Float(yoffset)}); @@ -903,9 +965,113 @@ void GlfwApplication::tickEvent() { void GlfwApplication::viewportEvent(ViewportEvent&) {} void GlfwApplication::keyPressEvent(KeyEvent&) {} void GlfwApplication::keyReleaseEvent(KeyEvent&) {} + +#ifdef MAGNUM_BUILD_DEPRECATED +namespace { + +CORRADE_IGNORE_DEPRECATED_PUSH +GlfwApplication::MouseEvent::Button pointerToButton(const GlfwApplication::Pointer pointer) { + switch(pointer) { + case GlfwApplication::Pointer::MouseLeft: + return GlfwApplication::MouseEvent::Button::Left; + case GlfwApplication::Pointer::MouseMiddle: + return GlfwApplication::MouseEvent::Button::Middle; + case GlfwApplication::Pointer::MouseRight: + return GlfwApplication::MouseEvent::Button::Right; + case GlfwApplication::Pointer::MouseButton4: + return GlfwApplication::MouseEvent::Button::Button4; + case GlfwApplication::Pointer::MouseButton5: + return GlfwApplication::MouseEvent::Button::Button5; + case GlfwApplication::Pointer::MouseButton6: + return GlfwApplication::MouseEvent::Button::Button6; + case GlfwApplication::Pointer::MouseButton7: + return GlfwApplication::MouseEvent::Button::Button7; + case GlfwApplication::Pointer::MouseButton8: + return GlfwApplication::MouseEvent::Button::Button8; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); +} +CORRADE_IGNORE_DEPRECATED_POP + +} +#endif + +void GlfwApplication::pointerPressEvent(PointerEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + MouseEvent mouseEvent{pointerToButton(event.pointer()), Vector2i{Math::round(event.position())}, event.modifiers()}; + mousePressEvent(mouseEvent); + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void GlfwApplication::mousePressEvent(MouseEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +void GlfwApplication::pointerReleaseEvent(PointerEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + MouseEvent mouseEvent{pointerToButton(event.pointer()), Vector2i{Math::round(event.position())}, event.modifiers()}; + mouseReleaseEvent(mouseEvent); + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void GlfwApplication::mouseReleaseEvent(MouseEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +void GlfwApplication::pointerMoveEvent(PointerMoveEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + const Vector2i roundedPosition{Math::round(event.position())}; + + /* If the event is due to some button being additionally pressed or one + button from a larger set being released, delegate to a press/release + event instead */ + if(event.pointer()) { + /* GLFW reports either a move or a press/release, so there shouldn't be + any move in this case */ + CORRADE_INTERNAL_ASSERT(event.relativePosition() == Vector2{}); + MouseEvent mouseEvent{pointerToButton(*event.pointer()), + roundedPosition, event.modifiers()}; + event.pointers() >= *event.pointer() ? + mousePressEvent(mouseEvent) : mouseReleaseEvent(mouseEvent); + } else { + /* Can't do just Math::round(event.relativePosition()) because if the + previous position was 4.6 and the new 5.3, they both round to 5 but + the relativePosition is 0.6 and rounds to 1. Conversely, if it'd be + 5.3 and 5.6, the positions round to 5 and 6 but relative position + stays 0. */ + const Vector2i previousRoundedPosition{Math::round(event.position() - event.relativePosition())}; + /* Call the event only if the integer values actually changed */ + if(roundedPosition != previousRoundedPosition) { + MouseMoveEvent mouseEvent{_window, roundedPosition, roundedPosition - previousRoundedPosition}; + mouseMoveEvent(mouseEvent); + } + } + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void GlfwApplication::mouseMoveEvent(MouseMoveEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + void GlfwApplication::mouseScrollEvent(MouseScrollEvent&) {} void GlfwApplication::textInputEvent(TextInputEvent&) {} @@ -952,6 +1118,20 @@ Containers::StringView GlfwApplication::KeyEvent::keyName() const { } #endif +GlfwApplication::Pointers GlfwApplication::PointerMoveEvent::pointers() { + if(!_pointers) + _pointers = currentGlfwPointers(_window); + + return *_pointers; +} + +auto GlfwApplication::PointerMoveEvent::modifiers() -> Modifiers { + if(!_modifiers) _modifiers = currentGlfwModifiers(_window); + return *_modifiers; +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH auto GlfwApplication::MouseMoveEvent::buttons() -> Buttons { if(!_buttons) { _buttons = Buttons{}; @@ -970,6 +1150,8 @@ auto GlfwApplication::MouseMoveEvent::modifiers() -> Modifiers { if(!_modifiers) _modifiers = currentGlfwModifiers(_window); return *_modifiers; } +CORRADE_IGNORE_DEPRECATED_POP +#endif Vector2i GlfwApplication::MouseScrollEvent::position() { if(!_position) { diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index d3a6089d7..1542213ad 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -178,11 +178,28 @@ class GlfwApplication { class ViewportEvent; class InputEvent; class KeyEvent; + class PointerEvent; + class PointerMoveEvent; + #ifdef MAGNUM_BUILD_DEPRECATED class MouseEvent; class MouseMoveEvent; + #endif class MouseScrollEvent; class TextInputEvent; + /* The damn thing cannot handle forward enum declarations */ + #ifndef DOXYGEN_GENERATING_OUTPUT + enum class Pointer: UnsignedByte; + #endif + + /** + * @brief Set of pointer types + * @m_since_latest + * + * @see @ref PointerMoveEvent::pointers() + */ + typedef Containers::EnumSet Pointers; + #ifdef MAGNUM_TARGET_GL /** * @brief Construct with an OpenGL context @@ -596,7 +613,7 @@ class GlfwApplication { * @} */ - /** @{ @name Mouse handling */ + /** @{ @name Pointer handling */ public: /** @@ -674,19 +691,90 @@ class GlfwApplication { } private: - /** @copydoc Sdl2Application::mousePressEvent() */ - virtual void mousePressEvent(MouseEvent& event); + /** + * @brief Pointer press event + * @m_since_latest + * + * Called when a mouse is pressed. Note that if at least one mouse + * button is already pressed and another button gets pressed in + * addition, @ref pointerMoveEvent() with the new combination is + * called, not this function. + * + * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default + * implementation delegates to @ref mousePressEvent(). On builds with + * deprecated functionality disabled, default implementation does + * nothing. + */ + virtual void pointerPressEvent(PointerEvent& event); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Mouse press event + * @m_deprecated_since_latest Use @ref pointerPressEvent() instead, + * which is a better abstraction for covering both mouse and touch + * / pen input. + * + * Default implementation does nothing. + */ + virtual CORRADE_DEPRECATED("use pointerPressEvent() instead") void mousePressEvent(MouseEvent& event); + #endif + + /** + * @brief Pointer release event + * @m_since_latest + * + * Called when a mouse is released. Note that if multiple mouse buttons + * are pressed and one of these is released, @ref pointerMoveEvent() + * with the new combination is called, not this function. + * + * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default + * implementation delegates to @ref mouseReleaseEvent(). On builds with + * deprecated functionality disabled, default implementation does + * nothing. + */ + virtual void pointerReleaseEvent(PointerEvent& event); - /** @copydoc Sdl2Application::mouseReleaseEvent() */ - virtual void mouseReleaseEvent(MouseEvent& event); + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Mouse release event + * @m_deprecated_since_latest Use @ref pointerReleaseEvent() instead, + * which is a better abstraction for covering both mouse and touch + * / pen input. + * + * Default implementation does nothing. + */ + virtual CORRADE_DEPRECATED("use pointerReleaseEvent() instead") void mouseReleaseEvent(MouseEvent& event); + #endif + /** + * @brief Pointer move event + * @m_since_latest + * + * Called when any of the currently pressed pointers is moved or + * changes its properties. Gets called also if the set of pressed mouse + * buttons changes. + * + * On builds with @ref MAGNUM_BUILD_DEPRECATED enabled, default + * implementation delegates to @ref mouseMoveEvent(), or if + * @ref PointerMoveEvent::pointer() is not + * @relativeref{Corrade,Containers::NullOpt}, to either + * @ref mousePressEvent() or @ref mouseReleaseEvent(). On builds with + * deprecated functionality disabled, default implementation does + * nothing. + */ + virtual void pointerMoveEvent(PointerMoveEvent& event); + + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Mouse move event + * @m_deprecated_since_latest Use @ref pointerMoveEvent() instead, + * which is a better abstraction for covering both mouse and touch + * / pen input. * - * Called when any mouse button is pressed and mouse is moved. Default - * implementation does nothing. + * Default implementation does nothing. */ - virtual void mouseMoveEvent(MouseMoveEvent& event); + virtual CORRADE_DEPRECATED("use pointerMoveEvent() instead") void mouseMoveEvent(MouseMoveEvent& event); + #endif /** @copydoc Sdl2Application::mouseScrollEvent() */ virtual void mouseScrollEvent(MouseScrollEvent& event); @@ -816,9 +904,59 @@ class GlfwApplication { int _exitCode = 0; Vector2i _minWindowSize, _maxWindowSize; - Vector2i _previousMouseMovePosition{-1}; + Vector2 _previousMouseMovePosition{Constants::nan()}; }; +/** +@brief Pointer type +@m_since_latest + +@see @ref Pointers, @ref PointerEvent::pointer(), + @ref PointerMoveEvent::pointer(), @ref PointerMoveEvent::pointers() +*/ +enum class GlfwApplication::Pointer: UnsignedByte { + /** + * Left mouse button. Corresponds to `GLFW_MOUSE_BUTTON_LEFT` or + * `GLFW_MOUSE_BUTTON_1`. + */ + MouseLeft = 1 << 0, + + /** + * Middle mouse button. Corresponds to `GLFW_MOUSE_BUTTON_MIDDLE` or + * `GLFW_MOUSE_BUTTON_2`. + */ + MouseMiddle = 1 << 1, + + /** + * Right mouse button. Corresponds to `GLFW_MOUSE_BUTTON_RIGHT` or + * `GLFW_MOUSE_BUTTON_3`. + */ + MouseRight = 1 << 2, + + /** + * Fourth mouse button, such as wheel left. Corresponds to + * `GLFW_MOUSE_BUTTON_4`. + */ + MouseButton4 = 1 << 3, + + /** + * Fifth mouse button, such as wheel right. Corresponds to + * `GLFW_MOUSE_BUTTON_5`. + */ + MouseButton5 = 1 << 4, + + /** Sixth mouse button. Corresponds to `GLFW_MOUSE_BUTTON_6`. */ + MouseButton6 = 1 << 5, + + /** Seventh mouse button. Corresponds to `GLFW_MOUSE_BUTTON_7`. */ + MouseButton7 = 1 << 6, + + /** Eighth mouse button. Corresponds to `GLFW_MOUSE_BUTTON_8`. */ + MouseButton8 = 1 << 7 +}; + +CORRADE_ENUMSET_OPERATORS(GlfwApplication::Pointers) + #ifdef MAGNUM_TARGET_GL /** @brief OpenGL context configuration @@ -1552,8 +1690,10 @@ class GlfwApplication::ViewportEvent { /** @brief Base for input events -@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), - @ref mousePressEvent(), @ref mouseReleaseEvent(), @ref mouseMoveEvent() +@see @ref KeyEvent, @ref PointerEvent, @ref PointerMoveEvent, + @ref MouseScrollEvent, @ref keyPressEvent(), @ref keyReleaseEvent(), + @ref pointerPressEvent(), @ref pointerReleaseEvent(), + @ref pointerMoveEvent(), @ref mouseScrollEvent() */ class GlfwApplication::InputEvent { public: @@ -1561,7 +1701,9 @@ class GlfwApplication::InputEvent { * @brief Modifier * * @see @ref Modifiers, @ref KeyEvent::modifiers(), - * @ref MouseEvent::modifiers() + * @ref PointerEvent::modifiers(), + * @ref PointerMoveEvent::modifiers(), + * @ref MouseScrollEvent::modifiers() */ enum class Modifier: Int { /** @@ -1596,8 +1738,9 @@ class GlfwApplication::InputEvent { /** * @brief Set of modifiers * - * @see @ref KeyEvent::modifiers(), @ref MouseEvent::modifiers(), - * @ref MouseMoveEvent::modifiers() + * @see @ref KeyEvent::modifiers(), @ref PointerEvent::modifiers(), + * @ref PointerMoveEvent::modifiers(), + * @ref MouseScrollEvent::modifiers() */ typedef Containers::EnumSet Modifiers; @@ -1939,13 +2082,63 @@ class GlfwApplication::KeyEvent: public GlfwApplication::InputEvent { const bool _repeated; }; +/** +@brief Pointer event +@m_since_latest + +@see @ref PointerMoveEvent, @ref MouseScrollEvent, @ref pointerPressEvent(), + @ref pointerReleaseEvent() +*/ +class GlfwApplication::PointerEvent: public InputEvent { + public: + /** @brief Copying is not allowed */ + PointerEvent(const PointerEvent&) = delete; + + /** @brief Moving is not allowed */ + PointerEvent(PointerEvent&&) = delete; + + /** @brief Copying is not allowed */ + PointerEvent& operator=(const PointerEvent&) = delete; + + /** @brief Moving is not allowed */ + PointerEvent& operator=(PointerEvent&&) = delete; + + /** @brief Pointer type that was pressed or released */ + Pointer pointer() const { return _pointer; } + + /** + * @brief Position + * + * May return fractional values for example on HiDPI systems where + * window size is smaller than actual framebuffer size. Use + * @ref Math::round() to snap them to the nearest window pixel. + */ + Vector2 position() const { return _position; } + + /** @brief Modifiers */ + Modifiers modifiers() const { return _modifiers; } + + private: + friend GlfwApplication; + + explicit PointerEvent(Pointer pointer, const Vector2& position, Modifiers modifiers): _pointer(pointer), _position{position}, _modifiers{modifiers} {} + + const Pointer _pointer; + const Vector2 _position; + const Modifiers _modifiers; +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief Mouse event +@m_deprecated_since_latest Use @ref PointerEvent, @ref pointerPressEvent() and + @ref pointerReleaseEvent() instead, which is a better abstraction for + covering both mouse and touch input. @see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() */ -class GlfwApplication::MouseEvent: public GlfwApplication::InputEvent { +class CORRADE_DEPRECATED("use PointerEvent, pointerPressEvent() and pointerReleaseEvent() instead") GlfwApplication::MouseEvent: public InputEvent { public: /** * @brief Mouse button @@ -1984,13 +2177,99 @@ class GlfwApplication::MouseEvent: public GlfwApplication::InputEvent { const Vector2i _position; const Modifiers _modifiers; }; +#endif + +/** +@brief Pointer move event +@m_since_latest + +@see @ref PointerEvent, @ref MouseScrollEvent, @ref pointerMoveEvent() +*/ +class GlfwApplication::PointerMoveEvent: public InputEvent { + public: + /** @brief Copying is not allowed */ + PointerMoveEvent(const PointerMoveEvent&) = delete; + + /** @brief Moving is not allowed */ + PointerMoveEvent(PointerMoveEvent&&) = delete; + + /** @brief Copying is not allowed */ + PointerMoveEvent& operator=(const PointerMoveEvent&) = delete; + + /** @brief Moving is not allowed */ + PointerMoveEvent& operator=(PointerMoveEvent&&) = delete; + + /** + * @brief Pointer type that was added or removed from the set of pressed pointers + * + * Is non-empty only in case a mouse button was pressed in addition to + * an already pressed button, or if one mouse button from multiple + * pressed buttons was released. If non-empty and @ref pointers() don't + * contain given @ref Pointer value, the button was released, if they + * contain given value, the button was pressed. + */ + Containers::Optional pointer() const { return _pointer; } + + /** + * @brief Pointer types pressed in this event + * + * Returns an empty set if no pointers are pressed, which happens for + * example when a mouse is just moved around. Lazily populated on first + * request. + * @see @ref pointer() + */ + Pointers pointers(); + + /** + * @brief Position + * + * May return fractional values for example on HiDPI systems where + * window size is smaller than actual framebuffer size. Use + * @ref Math::round() to snap them to the nearest window pixel. + */ + Vector2 position() const { return _position; } + + /** + * @brief Position relative to the previous touch event + * + * May return fractional values for example on HiDPI systems where + * window size is smaller than actual framebuffer size. Use + * @ref Math::round() to snap them to the nearest window pixel. Unlike + * @ref Sdl2Application, GLFW doesn't provide relative position + * directly, so this is calculated explicitly as a delta from previous + * move event position. + */ + Vector2 relativePosition() const { return _relativePosition; } + + /** + * @brief Modifiers + * + * Lazily populated on first request. + */ + Modifiers modifiers(); + + private: + friend GlfwApplication; + + explicit PointerMoveEvent(GLFWwindow* window, Containers::Optional pointer, const Vector2& position, const Vector2& relativePosition): _window{window}, _pointer{pointer}, _position{position}, _relativePosition{relativePosition} {} + + GLFWwindow* const _window; + const Containers::Optional _pointer; + Containers::Optional _pointers; + const Vector2 _position, _relativePosition; + Containers::Optional _modifiers; +}; +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief Mouse move event +@m_deprecated_since_latest Use @ref PointerMoveEvent and + @ref pointerMoveEvent() instead, which is a better abstraction for covering + both mouse and touch / pen input. @see @ref MouseEvent, @ref MouseScrollEvent, @ref mouseMoveEvent() */ -class GlfwApplication::MouseMoveEvent: public GlfwApplication::InputEvent { +class CORRADE_DEPRECATED("use PointerMoveEvent and pointerMoveEvent() instead") GlfwApplication::MouseMoveEvent: public GlfwApplication::InputEvent { public: /** * @brief Mouse button @@ -2049,12 +2328,15 @@ class GlfwApplication::MouseMoveEvent: public GlfwApplication::InputEvent { Containers::Optional _modifiers; }; +CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ENUMSET_OPERATORS(GlfwApplication::MouseMoveEvent::Buttons) +CORRADE_IGNORE_DEPRECATED_POP +#endif /** @brief Mouse scroll event -@see @ref MouseEvent, @ref MouseMoveEvent, @ref mouseScrollEvent() +@see @ref PointerEvent, @ref PointerMoveEvent, @ref mouseScrollEvent() */ class GlfwApplication::MouseScrollEvent: public GlfwApplication::InputEvent { public: diff --git a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp index f5f2e6ac1..5b2f4c52f 100644 --- a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp +++ b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp @@ -57,7 +57,28 @@ static Debug& operator<<(Debug& debug, Application::InputEvent::Modifier value) return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; } -static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) { +static Debug& operator<<(Debug& debug, Application::Pointer value) { + debug << "Pointer" << Debug::nospace; + + switch(value) { + #define _c(value) case Application::Pointer::value: return debug << "::" #value; + _c(MouseLeft) + _c(MouseMiddle) + _c(MouseRight) + _c(MouseButton4) + _c(MouseButton5) + _c(MouseButton6) + _c(MouseButton7) + _c(MouseButton8) + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_UNUSED static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) { debug << "Button" << Debug::nospace; switch(value) { @@ -70,6 +91,8 @@ static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; } +CORRADE_IGNORE_DEPRECATED_POP +#endif namespace Test { namespace { @@ -82,7 +105,22 @@ Debug& operator<<(Debug& debug, Application::InputEvent::Modifiers value) { }); } -Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { +Debug& operator<<(Debug& debug, Application::Pointers value) { + return Containers::enumSetDebugOutput(debug, value, "Pointers{}", { + Application::Pointer::MouseLeft, + Application::Pointer::MouseMiddle, + Application::Pointer::MouseRight, + Application::Pointer::MouseButton4, + Application::Pointer::MouseButton5, + Application::Pointer::MouseButton6, + Application::Pointer::MouseButton7, + Application::Pointer::MouseButton8, + }); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { debug << "Button" << Debug::nospace; switch(value) { @@ -101,13 +139,15 @@ Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; } -Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) { +CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) { return Containers::enumSetDebugOutput(debug, value, "Buttons{}", { Application::MouseMoveEvent::Button::Left, Application::MouseMoveEvent::Button::Middle, Application::MouseMoveEvent::Button::Right, }); } +CORRADE_IGNORE_DEPRECATED_POP +#endif Debug& operator<<(Debug& debug, const Application::KeyEvent::Key value) { debug << "Key" << Debug::nospace; @@ -312,17 +352,30 @@ struct GlfwApplicationTest: Platform::Application { << event.modifiers(); } + /* Set to 0 to test the deprecated mouse events instead */ + #if 1 + void pointerPressEvent(PointerEvent& event) override { + Debug{} << "pointer press:" << event.pointer() << event.modifiers() << Debug::packed << event.position(); + } + void pointerReleaseEvent(PointerEvent& event) override { + Debug{} << "pointer release:" << event.pointer() << event.modifiers() << Debug::packed << event.position(); + } + void pointerMoveEvent(PointerMoveEvent& event) override { + Debug{} << "pointer move:" << event.pointer() << event.pointers() << event.modifiers() << Debug::packed << event.position() << Debug::packed << event.relativePosition(); + } + #else + CORRADE_IGNORE_DEPRECATED_PUSH void mousePressEvent(MouseEvent& event) override { Debug{} << "mouse press:" << event.button() << event.modifiers() << Debug::packed << event.position(); } - void mouseReleaseEvent(MouseEvent& event) override { Debug{} << "mouse release:" << event.button() << event.modifiers() << Debug::packed << event.position(); } - void mouseMoveEvent(MouseMoveEvent& event) override { Debug{} << "mouse move:" << event.buttons() << event.modifiers() << Debug::packed << event.position() << Debug::packed << event.relativePosition(); } + CORRADE_IGNORE_DEPRECATED_POP + #endif void mouseScrollEvent(MouseScrollEvent& event) override { Debug{} << "mouse scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position();