diff --git a/doc/changelog.dox b/doc/changelog.dox index 69464350f..236dc9aeb 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1386,6 +1386,15 @@ See also: - @cpp Platform::Sdl2Application::setMinimalLoopPeriod(UnsignedInt) @ce taking an untyped millisecond value is deprecated in favor of @relativeref{Platform::Sdl2Application,setMinimalLoopPeriod(Nanoseconds)} +- @cpp Platform::Sdl2Application::mousePressEvent() @ce, + @cpp mouseReleaseEvent() @ce, @cpp mouseMoveEvent() @ce, + @cpp MouseEvent @ce and @cpp MouseMoveEvent @ce are deprecated in favor of + new @ref Platform::Sdl2Application::pointerPressEvent(), + @relativeref{Platform::Sdl2Application,pointerReleaseEvent()}, + @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 - @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/doc/snippets/SceneGraph-gl.cpp b/doc/snippets/SceneGraph-gl.cpp index 0f048965c..58a7dd243 100644 --- a/doc/snippets/SceneGraph-gl.cpp +++ b/doc/snippets/SceneGraph-gl.cpp @@ -51,7 +51,7 @@ struct MyApplication: Platform::Application { explicit MyApplication(const Arguments& arguments); void drawEvent() override; - void mousePressEvent(MouseEvent& event) override; + void pointerPressEvent(PointerEvent& event) override; SceneGraph::AnimableGroup3D animables; Timeline timeline; @@ -75,9 +75,9 @@ void MyApplication::drawEvent() { } /* [Animable-usage-timeline] */ -void MyApplication::mousePressEvent(MouseEvent& event) { +void MyApplication::pointerPressEvent(PointerEvent& event) { /* [Camera-projectionSize] */ -Vector2 position = (Vector2{event.position()}/Vector2{GL::defaultFramebuffer.viewport().size()} +Vector2 position = (event.position()/Vector2{framebufferSize()} - Vector2{0.5f})*Vector2::yScale(-1.0f)*camera.projectionSize(); /* [Camera-projectionSize] */ diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 7e2ddb234..cd1c4eace 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -893,6 +893,42 @@ void Sdl2Application::exit(const int exitCode) { _exitCode = exitCode; } +namespace { + +Sdl2Application::Pointer buttonToPointer(const Uint8 button) { + switch(button) { + case SDL_BUTTON_LEFT: + return Sdl2Application::Pointer::MouseLeft; + case SDL_BUTTON_MIDDLE: + return Sdl2Application::Pointer::MouseMiddle; + case SDL_BUTTON_RIGHT: + return Sdl2Application::Pointer::MouseRight; + case SDL_BUTTON_X1: + return Sdl2Application::Pointer::MouseButton4; + case SDL_BUTTON_X2: + return Sdl2Application::Pointer::MouseButton5; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); +} + +Sdl2Application::Pointers buttonsToPointers(const Uint32 buttons) { + Sdl2Application::Pointers pointers; + if(buttons & SDL_BUTTON_LMASK) + pointers |= Sdl2Application::Pointer::MouseLeft; + if(buttons & SDL_BUTTON_MMASK) + pointers |= Sdl2Application::Pointer::MouseMiddle; + if(buttons & SDL_BUTTON_RMASK) + pointers |= Sdl2Application::Pointer::MouseRight; + if(buttons & SDL_BUTTON_X1MASK) + pointers |= Sdl2Application::Pointer::MouseButton4; + if(buttons & SDL_BUTTON_X2MASK) + pointers |= Sdl2Application::Pointer::MouseButton5; + return pointers; +} + +} + bool Sdl2Application::mainLoopIteration() { /* If exit was requested directly in the constructor, exit immediately without calling anything else */ @@ -982,12 +1018,27 @@ bool Sdl2Application::mainLoopIteration() { case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { - MouseEvent e{event, static_cast(event.button.button), {event.button.x, event.button.y} - #ifndef CORRADE_TARGET_EMSCRIPTEN - , event.button.clicks - #endif + const Pointer pointer = buttonToPointer(event.button.button); + const Vector2 position{Float(event.button.x), Float(event.button.y)}; + + /* If an additional mouse button was pressed or some buttons + are still left pressed after a release, call a move event + instead */ + const Uint32 buttons = SDL_GetMouseState(nullptr, nullptr); + if((event.type == SDL_MOUSEBUTTONDOWN && (buttons & ~SDL_BUTTON(event.button.button))) || + (event.type == SDL_MOUSEBUTTONUP && buttons)) { + Pointers pointers = buttonsToPointers(buttons); + PointerMoveEvent e{event, pointer, pointers, position, {}}; + pointerMoveEvent(e); + } else { + PointerEvent e{event, pointer, position + #ifndef CORRADE_TARGET_EMSCRIPTEN + , event.button.clicks + #endif }; - event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e); + event.type == SDL_MOUSEBUTTONDOWN ? + pointerPressEvent(e) : pointerReleaseEvent(e); + } } break; case SDL_MOUSEWHEEL: { @@ -996,10 +1047,11 @@ bool Sdl2Application::mainLoopIteration() { } break; case SDL_MOUSEMOTION: { - MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast(event.motion.state)}; - mouseMoveEvent(e); - break; - } + PointerMoveEvent e{event, {}, buttonsToPointers(event.motion.state), + {Float(event.motion.x), Float(event.motion.y)}, + {Float(event.motion.xrel), Float(event.motion.yrel)}}; + pointerMoveEvent(e); + } break; case SDL_MULTIGESTURE: { MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; @@ -1224,9 +1276,131 @@ void Sdl2Application::anyEvent(SDL_Event&) { void Sdl2Application::viewportEvent(ViewportEvent&) {} void Sdl2Application::keyPressEvent(KeyEvent&) {} void Sdl2Application::keyReleaseEvent(KeyEvent&) {} + +#ifdef MAGNUM_BUILD_DEPRECATED +namespace { + +CORRADE_IGNORE_DEPRECATED_PUSH +Sdl2Application::MouseEvent::Button pointerToButton(const Sdl2Application::Pointer pointer) { + switch(pointer) { + case Sdl2Application::Pointer::MouseLeft: + return Sdl2Application::MouseEvent::Button::Left; + case Sdl2Application::Pointer::MouseMiddle: + return Sdl2Application::MouseEvent::Button::Middle; + case Sdl2Application::Pointer::MouseRight: + return Sdl2Application::MouseEvent::Button::Right; + case Sdl2Application::Pointer::MouseButton4: + return Sdl2Application::MouseEvent::Button::X1; + case Sdl2Application::Pointer::MouseButton5: + return Sdl2Application::MouseEvent::Button::X2; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); +} +CORRADE_IGNORE_DEPRECATED_POP + +} +#endif + +void Sdl2Application::pointerPressEvent(PointerEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())} + #ifndef CORRADE_TARGET_EMSCRIPTEN + , event.clickCount() + #endif + }; + mousePressEvent(mouseEvent); + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void Sdl2Application::mousePressEvent(MouseEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +void Sdl2Application::pointerReleaseEvent(PointerEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + MouseEvent mouseEvent{event.event(), pointerToButton(event.pointer()), Vector2i{Math::round(event.position())} + #ifndef CORRADE_TARGET_EMSCRIPTEN + , event.clickCount() + #endif + }; + mouseReleaseEvent(mouseEvent); + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void Sdl2Application::mouseReleaseEvent(MouseEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +void Sdl2Application::pointerMoveEvent(PointerMoveEvent& event) { + #ifdef MAGNUM_BUILD_DEPRECATED + const Vector2i roundedPosition{Math::round(event.position())}; + + CORRADE_IGNORE_DEPRECATED_PUSH + /* 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()) { + /* SDL2 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{event.event(), pointerToButton(*event.pointer()), + roundedPosition + #ifndef CORRADE_TARGET_EMSCRIPTEN + , 1 + #endif + }; + event.pointers() >= *event.pointer() ? + mousePressEvent(mouseEvent) : mouseReleaseEvent(mouseEvent); + } else { + MouseMoveEvent::Buttons buttons; + if(event.pointers() & Pointer::MouseLeft) + buttons |= MouseMoveEvent::Button::Left; + if(event.pointers() & Pointer::MouseMiddle) + buttons |= MouseMoveEvent::Button::Middle; + if(event.pointers() & Pointer::MouseRight) + buttons |= MouseMoveEvent::Button::Right; + if(event.pointers() & Pointer::MouseButton4) + buttons |= MouseMoveEvent::Button::X1; + if(event.pointers() & Pointer::MouseButton5) + buttons |= MouseMoveEvent::Button::X2; + + /* 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{event.event(), roundedPosition, roundedPosition - previousRoundedPosition, buttons}; + mouseMoveEvent(mouseEvent); + } + } + CORRADE_IGNORE_DEPRECATED_POP + #else + static_cast(event); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void Sdl2Application::mouseMoveEvent(MouseMoveEvent&) {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + void Sdl2Application::mouseScrollEvent(MouseScrollEvent&) {} void Sdl2Application::multiGestureEvent(MultiGestureEvent&) {} void Sdl2Application::textInputEvent(TextInputEvent&) {} @@ -1269,15 +1443,35 @@ Containers::StringView Sdl2Application::KeyEvent::keyName() const { return keyName(_key); } +Sdl2Application::InputEvent::Modifiers Sdl2Application::PointerEvent::modifiers() { + if(!_modifiers) + _modifiers = fixedModifiers(Uint16(SDL_GetModState())); + return *_modifiers; +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { if(_modifiers) return *_modifiers; return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); } +CORRADE_IGNORE_DEPRECATED_POP +#endif +Sdl2Application::InputEvent::Modifiers Sdl2Application::PointerMoveEvent::modifiers() { + if(!_modifiers) + _modifiers = fixedModifiers(Uint16(SDL_GetModState())); + return *_modifiers; +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() { if(_modifiers) return *_modifiers; return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); } +CORRADE_IGNORE_DEPRECATED_POP +#endif Vector2i Sdl2Application::MouseScrollEvent::position() { if(_position) return *_position; diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index 6cc2f1325..0f0ac9a41 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -500,13 +500,30 @@ class Sdl2Application { class ViewportEvent; class InputEvent; class KeyEvent; + class PointerEvent; + class PointerMoveEvent; + #ifdef MAGNUM_BUILD_DEPRECATED class MouseEvent; class MouseMoveEvent; + #endif class MouseScrollEvent; class MultiGestureEvent; class TextInputEvent; class TextEditingEvent; + /* 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 @@ -1001,7 +1018,7 @@ class Sdl2Application { * @} */ - /** @{ @name Mouse handling */ + /** @{ @name Pointer handling */ public: /** @@ -1081,28 +1098,90 @@ class Sdl2Application { #endif private: + /** + * @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. * - * Called when mouse button is pressed. Default implementation does + * 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 mousePressEvent(MouseEvent& event); + virtual void pointerReleaseEvent(PointerEvent& 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. * - * Called when mouse button is released. Default implementation does + * 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 mouseReleaseEvent(MouseEvent& event); + 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 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 /** * @brief Mouse scroll event @@ -1112,14 +1191,6 @@ class Sdl2Application { */ virtual void mouseScrollEvent(MouseScrollEvent& event); - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - /** @{ @name Touch gesture handling */ - /** * @brief Multi gesture event * @@ -1294,6 +1365,47 @@ class Sdl2Application { int _exitCode = 0; }; +/** +@brief Pointer type +@m_since_latest + +@see @ref Pointers, @ref PointerEvent::pointer(), + @ref PointerMoveEvent::pointer(), @ref PointerMoveEvent::pointers() +*/ +enum class Sdl2Application::Pointer: UnsignedByte { + /** + * Left mouse button. Corresponds to `SDL_BUTTON_LEFT` / + * `SDL_BUTTON_LMASK`. + */ + MouseLeft = 1 << 0, + + /** + * Middle mouse button. Corresponds to `SDL_BUTTON_MIDDLE` / + * `SDL_BUTTON_MMASK`. + */ + MouseMiddle = 1 << 1, + + /** + * Right mouse button. Corresponds to `SDL_BUTTON_RIGHT` / + * `SDL_BUTTON_RMASK`. + */ + MouseRight = 1 << 2, + + /** + * Fourth mouse button, such as wheel left. Corresponds to `SDL_BUTTON_X1` + * / `SDL_BUTTON_X1MASK`. + */ + MouseButton4 = 1 << 3, + + /** + * Fifth mouse button, such as wheel right. Corresponds to `SDL_BUTTON_X2` + * / `SDL_BUTTON_X2MASK`. + */ + MouseButton5 = 1 << 4, +}; + +CORRADE_ENUMSET_OPERATORS(Sdl2Application::Pointers) + #ifdef MAGNUM_TARGET_GL /** @brief OpenGL context configuration @@ -2141,9 +2253,10 @@ class Sdl2Application::ViewportEvent { /** @brief Base for input events -@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), - @ref keyReleaseEvent(), @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 Sdl2Application::InputEvent { public: @@ -2151,7 +2264,9 @@ class Sdl2Application::InputEvent { * @brief Modifier * * @see @ref Modifiers, @ref KeyEvent::modifiers(), - * @ref MouseEvent::modifiers(), @ref MouseMoveEvent::modifiers() + * @ref PointerEvent::modifiers(), + * @ref PointerMoveEvent::modifiers(), + * @ref MouseScrollEvent::modifiers() */ enum class Modifier: Uint16 { /** @@ -2208,8 +2323,9 @@ class Sdl2Application::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; @@ -2242,9 +2358,9 @@ class Sdl2Application::InputEvent { * @brief Underlying SDL event * * Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent, - * `SDL_MOUSEBUTTONUP` / `SDL_MOUSEBUTTONDOWN` for @ref MouseEvent, - * `SDL_MOUSEWHEEL` for @ref MouseScrollEvent and `SDL_MOUSEMOTION` for - * @ref MouseMoveEvent. + * `SDL_MOUSEBUTTONDOWN` / `SDL_MOUSEBUTTONUP` for @ref PointerEvent, + * `SDL_MOUSEMOTION` for @ref PointerMoveEvent and `SDL_MOUSEWHEEL` for + * @ref MouseScrollEvent. * @see @ref Sdl2Application::anyEvent() */ const SDL_Event& event() const { return _event; } @@ -2585,13 +2701,85 @@ class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { const bool _repeated; }; +/** +@brief Pointer event +@m_since_latest + +@see @ref PointerMoveEvent, @ref pointerPressEvent(), + @ref pointerReleaseEvent() +*/ +class Sdl2Application::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 + * + * For mouse input the position is always reported in whole pixels. + */ + Vector2 position() const { return _position; } + + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Click count + * + * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + Int clickCount() const { return _clickCount; } + #endif + + /** + * @brief Modifiers + * + * Lazily populated on first request. + */ + Modifiers modifiers(); + + private: + friend Sdl2Application; + + explicit PointerEvent(const SDL_Event& event, Pointer pointer, const Vector2& position + #ifndef CORRADE_TARGET_EMSCRIPTEN + , Int clickCount + #endif + ): InputEvent{event}, _pointer(pointer), _position{position} + #ifndef CORRADE_TARGET_EMSCRIPTEN + , _clickCount{clickCount} + #endif + {} + + const Pointer _pointer; + Containers::Optional _modifiers; + const Vector2 _position; + #ifndef CORRADE_TARGET_EMSCRIPTEN + const Int _clickCount; + #endif +}; + +#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 / pen input. @see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() */ -class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { +class CORRADE_DEPRECATED("use PointerEvent, pointerPressEvent() and pointerReleaseEvent() instead") Sdl2Application::MouseEvent: public InputEvent { public: /** * @brief Mouse button @@ -2652,13 +2840,90 @@ class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { #endif Containers::Optional _modifiers; }; +#endif +/** +@brief Pointer move event +@m_since_latest + +@see @ref PointerEvent, @ref pointerMoveEvent() +*/ +class Sdl2Application::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. + * @see @ref pointer() + */ + Pointers pointers() const { return _pointers; } + + /** + * @brief Position + * + * For mouse input the position is always reported in whole pixels. + */ + Vector2 position() const { return _position; } + + /** + * @brief Position relative to the previous touch event + * + * For mouse input the position is always reported in whole pixels. + */ + Vector2 relativePosition() const { return _relativePosition; } + + /** + * @brief Modifiers + * + * Lazily populated on first request. + */ + Modifiers modifiers(); + + private: + friend Sdl2Application; + + explicit PointerMoveEvent(const SDL_Event& event, Containers::Optional pointer, Pointers pointers, const Vector2& position, const Vector2& relativePosition): InputEvent{event}, _pointer{pointer}, _pointers{pointers}, _position{position}, _relativePosition{relativePosition} {} + + const Containers::Optional _pointer; + const Pointers _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 Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { +class CORRADE_DEPRECATED("use PointerMoveEvent and pointerMoveEvent() instead") Sdl2Application::MouseMoveEvent: public InputEvent { public: /** * @brief Mouse button @@ -2714,10 +2979,15 @@ class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { Containers::Optional _modifiers; }; +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_ENUMSET_OPERATORS(Sdl2Application::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 Sdl2Application::MouseScrollEvent: public Sdl2Application::InputEvent { public: @@ -3007,7 +3277,6 @@ typedef BasicScreenedApplication ScreenedApplication; CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags) CORRADE_ENUMSET_OPERATORS(Sdl2Application::InputEvent::Modifiers) -CORRADE_ENUMSET_OPERATORS(Sdl2Application::MouseMoveEvent::Buttons) }} diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index a579d5483..ee5627b28 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -77,7 +77,25 @@ 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) + #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) { @@ -92,6 +110,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 { @@ -107,7 +127,19 @@ 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, + }); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { debug << "Button" << Debug::nospace; switch(value) { @@ -123,7 +155,7 @@ 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, @@ -132,6 +164,8 @@ Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) { Application::MouseMoveEvent::Button::X2, }); } +CORRADE_IGNORE_DEPRECATED_POP +#endif Debug& operator<<(Debug& debug, Application::KeyEvent::Key value) { debug << "Key" << Debug::nospace; @@ -285,22 +319,38 @@ struct Sdl2ApplicationTest: Platform::Application { redraw(); } - /* For testing event coordinates */ + /* 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(); + _gestureDistance = {}; + _gestureRotation = {}; + } + void pointerReleaseEvent(PointerEvent& event) override { + Debug{} << "pointer release:" << event.pointer() << event.modifiers() << Debug::packed << event.position(); + _gestureDistance = {}; + _gestureRotation = {}; + } + 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() << Debug::packed << event.position() << event.modifiers(); _gestureDistance = {}; _gestureRotation = {}; } - void mouseReleaseEvent(MouseEvent& event) override { Debug{} << "mouse release:" << event.button() << Debug::packed << event.position() << event.modifiers(); _gestureDistance = {}; _gestureRotation = {}; } - void mouseMoveEvent(MouseMoveEvent& event) override { Debug{} << "mouse move:" << event.buttons() << Debug::packed << event.position() << Debug::packed << event.relativePosition() << event.modifiers(); } + CORRADE_IGNORE_DEPRECATED_POP + #endif void mouseScrollEvent(MouseScrollEvent& event) override { Debug{} << "mouse scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position();