Browse Source

Platform: replace mouse events with pointer events in Sdl2Application.

Pointer events are an unified abstraction over mouse, touch, pen and
potential other yet-to-be-invented pointer-like input methods. Their goal
is to expose all such input methods under a single interface so the
application side doesn't need to explicitly make sure that it's
touch-aware or pen-aware. This abstraction is already present in HTML5,
in Qt6 and in WINAPI as well, and is also what I adopted for the new UI
library because it *just makes sense*.

Unfortunately not even SDL3 took the opportunity to introduce that and
instead added a *third* separate event type for pen input in SDL3.  At
first I thought that I wouldn't introduce any extra abstractions in the
Application classes (because that's what they are designed to be, as
lightweight as possible), but midway through introducing TouchEvent
classes and fighting SDL's touch->mouse and mouse->touch compatibility
translation (yes, it's both ways, depending on the platform) I realized
that a much simpler solution that doesn't require any event translation
or the users duplicating their event handling logic for several possible
input types is to introduce a single new event type that covers all.

Which is what this commit does -- it doesn't introduce anything
touch-related so far, just creates a new PointerEvent and
PointerMoveEvent class and corresponding virtual functions. Additionally,
I took this as an opportunity to make the position floating-point, since
that's what SDL3 does now as well, and GLFW did so since ever.

Plus, the Pointer and Pointers enums are directly on the Sdl2Application
class, to allow me to *finally* introduce pointer state queries. Which
weren't possible until now, because there were mutually incompatible
MouseEvent::Button and MouseMoveEvent::Button enums and putting them on
the base class would mean one would have to be translated and the other
not. With Pointer it's translated always, because there isn't any similar
enumeration in SDL that would cover mouse, touch and pen at the same
time.
pull/651/head
Vladimír Vondruš 2 years ago
parent
commit
fc6c76726d
  1. 9
      doc/changelog.dox
  2. 6
      doc/snippets/SceneGraph-gl.cpp
  3. 206
      src/Magnum/Platform/Sdl2Application.cpp
  4. 325
      src/Magnum/Platform/Sdl2Application.h
  5. 62
      src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp

9
doc/changelog.dox

@ -1386,6 +1386,15 @@ See also:
- @cpp Platform::Sdl2Application::setMinimalLoopPeriod(UnsignedInt) @ce - @cpp Platform::Sdl2Application::setMinimalLoopPeriod(UnsignedInt) @ce
taking an untyped millisecond value is deprecated in favor of taking an untyped millisecond value is deprecated in favor of
@relativeref{Platform::Sdl2Application,setMinimalLoopPeriod(Nanoseconds)} @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::DistanceFieldVector @ce, @cpp Shaders::Flat @ce,
@cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce, @cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce,
@cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce, @cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce,

6
doc/snippets/SceneGraph-gl.cpp

@ -51,7 +51,7 @@ struct MyApplication: Platform::Application {
explicit MyApplication(const Arguments& arguments); explicit MyApplication(const Arguments& arguments);
void drawEvent() override; void drawEvent() override;
void mousePressEvent(MouseEvent& event) override; void pointerPressEvent(PointerEvent& event) override;
SceneGraph::AnimableGroup3D animables; SceneGraph::AnimableGroup3D animables;
Timeline timeline; Timeline timeline;
@ -75,9 +75,9 @@ void MyApplication::drawEvent() {
} }
/* [Animable-usage-timeline] */ /* [Animable-usage-timeline] */
void MyApplication::mousePressEvent(MouseEvent& event) { void MyApplication::pointerPressEvent(PointerEvent& event) {
/* [Camera-projectionSize] */ /* [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(); - Vector2{0.5f})*Vector2::yScale(-1.0f)*camera.projectionSize();
/* [Camera-projectionSize] */ /* [Camera-projectionSize] */

206
src/Magnum/Platform/Sdl2Application.cpp

@ -893,6 +893,42 @@ void Sdl2Application::exit(const int exitCode) {
_exitCode = 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() { bool Sdl2Application::mainLoopIteration() {
/* If exit was requested directly in the constructor, exit immediately /* If exit was requested directly in the constructor, exit immediately
without calling anything else */ without calling anything else */
@ -982,12 +1018,27 @@ bool Sdl2Application::mainLoopIteration() {
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: { case SDL_MOUSEBUTTONUP: {
MouseEvent e{event, static_cast<MouseEvent::Button>(event.button.button), {event.button.x, event.button.y} 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 #ifndef CORRADE_TARGET_EMSCRIPTEN
, event.button.clicks , event.button.clicks
#endif #endif
}; };
event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e); event.type == SDL_MOUSEBUTTONDOWN ?
pointerPressEvent(e) : pointerReleaseEvent(e);
}
} break; } break;
case SDL_MOUSEWHEEL: { case SDL_MOUSEWHEEL: {
@ -996,10 +1047,11 @@ bool Sdl2Application::mainLoopIteration() {
} break; } break;
case SDL_MOUSEMOTION: { case SDL_MOUSEMOTION: {
MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast<MouseMoveEvent::Button>(event.motion.state)}; PointerMoveEvent e{event, {}, buttonsToPointers(event.motion.state),
mouseMoveEvent(e); {Float(event.motion.x), Float(event.motion.y)},
break; {Float(event.motion.xrel), Float(event.motion.yrel)}};
} pointerMoveEvent(e);
} break;
case SDL_MULTIGESTURE: { case SDL_MULTIGESTURE: {
MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; 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::viewportEvent(ViewportEvent&) {}
void Sdl2Application::keyPressEvent(KeyEvent&) {} void Sdl2Application::keyPressEvent(KeyEvent&) {}
void Sdl2Application::keyReleaseEvent(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<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void Sdl2Application::mousePressEvent(MouseEvent&) {} 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<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void Sdl2Application::mouseReleaseEvent(MouseEvent&) {} 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<void>(event);
#endif
}
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
void Sdl2Application::mouseMoveEvent(MouseMoveEvent&) {} void Sdl2Application::mouseMoveEvent(MouseMoveEvent&) {}
CORRADE_IGNORE_DEPRECATED_POP
#endif
void Sdl2Application::mouseScrollEvent(MouseScrollEvent&) {} void Sdl2Application::mouseScrollEvent(MouseScrollEvent&) {}
void Sdl2Application::multiGestureEvent(MultiGestureEvent&) {} void Sdl2Application::multiGestureEvent(MultiGestureEvent&) {}
void Sdl2Application::textInputEvent(TextInputEvent&) {} void Sdl2Application::textInputEvent(TextInputEvent&) {}
@ -1269,15 +1443,35 @@ Containers::StringView Sdl2Application::KeyEvent::keyName() const {
return keyName(_key); 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() { Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() {
if(_modifiers) return *_modifiers; if(_modifiers) return *_modifiers;
return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); 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() { Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() {
if(_modifiers) return *_modifiers; if(_modifiers) return *_modifiers;
return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState())));
} }
CORRADE_IGNORE_DEPRECATED_POP
#endif
Vector2i Sdl2Application::MouseScrollEvent::position() { Vector2i Sdl2Application::MouseScrollEvent::position() {
if(_position) return *_position; if(_position) return *_position;

325
src/Magnum/Platform/Sdl2Application.h

@ -500,13 +500,30 @@ class Sdl2Application {
class ViewportEvent; class ViewportEvent;
class InputEvent; class InputEvent;
class KeyEvent; class KeyEvent;
class PointerEvent;
class PointerMoveEvent;
#ifdef MAGNUM_BUILD_DEPRECATED
class MouseEvent; class MouseEvent;
class MouseMoveEvent; class MouseMoveEvent;
#endif
class MouseScrollEvent; class MouseScrollEvent;
class MultiGestureEvent; class MultiGestureEvent;
class TextInputEvent; class TextInputEvent;
class TextEditingEvent; 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<Pointer> Pointers;
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
/** /**
* @brief Construct with an OpenGL context * @brief Construct with an OpenGL context
@ -1001,7 +1018,7 @@ class Sdl2Application {
* @} * @}
*/ */
/** @{ @name Mouse handling */ /** @{ @name Pointer handling */
public: public:
/** /**
@ -1081,28 +1098,90 @@ class Sdl2Application {
#endif #endif
private: 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 * @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. * nothing.
*/ */
virtual void mousePressEvent(MouseEvent& event); virtual void pointerReleaseEvent(PointerEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
* @brief Mouse release event * @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. * nothing.
*/ */
virtual void mouseReleaseEvent(MouseEvent& event); virtual void pointerMoveEvent(PointerMoveEvent& event);
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
* @brief Mouse move event * @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 * @brief Mouse scroll event
@ -1112,14 +1191,6 @@ class Sdl2Application {
*/ */
virtual void mouseScrollEvent(MouseScrollEvent& event); 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 * @brief Multi gesture event
* *
@ -1294,6 +1365,47 @@ class Sdl2Application {
int _exitCode = 0; 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 #ifdef MAGNUM_TARGET_GL
/** /**
@brief OpenGL context configuration @brief OpenGL context configuration
@ -2141,9 +2253,10 @@ class Sdl2Application::ViewportEvent {
/** /**
@brief Base for input events @brief Base for input events
@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), @see @ref KeyEvent, @ref PointerEvent, @ref PointerMoveEvent,
@ref keyReleaseEvent(), @ref mousePressEvent(), @ref mouseReleaseEvent(), @ref MouseScrollEvent, @ref keyPressEvent(), @ref keyReleaseEvent(),
@ref mouseMoveEvent() @ref pointerPressEvent(), @ref pointerReleaseEvent(),
@ref pointerMoveEvent(), @ref mouseScrollEvent()
*/ */
class Sdl2Application::InputEvent { class Sdl2Application::InputEvent {
public: public:
@ -2151,7 +2264,9 @@ class Sdl2Application::InputEvent {
* @brief Modifier * @brief Modifier
* *
* @see @ref Modifiers, @ref KeyEvent::modifiers(), * @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 { enum class Modifier: Uint16 {
/** /**
@ -2208,8 +2323,9 @@ class Sdl2Application::InputEvent {
/** /**
* @brief Set of modifiers * @brief Set of modifiers
* *
* @see @ref KeyEvent::modifiers(), @ref MouseEvent::modifiers(), * @see @ref KeyEvent::modifiers(), @ref PointerEvent::modifiers(),
* @ref MouseMoveEvent::modifiers() * @ref PointerMoveEvent::modifiers(),
* @ref MouseScrollEvent::modifiers()
*/ */
typedef Containers::EnumSet<Modifier> Modifiers; typedef Containers::EnumSet<Modifier> Modifiers;
@ -2242,9 +2358,9 @@ class Sdl2Application::InputEvent {
* @brief Underlying SDL event * @brief Underlying SDL event
* *
* Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent, * Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent,
* `SDL_MOUSEBUTTONUP` / `SDL_MOUSEBUTTONDOWN` for @ref MouseEvent, * `SDL_MOUSEBUTTONDOWN` / `SDL_MOUSEBUTTONUP` for @ref PointerEvent,
* `SDL_MOUSEWHEEL` for @ref MouseScrollEvent and `SDL_MOUSEMOTION` for * `SDL_MOUSEMOTION` for @ref PointerMoveEvent and `SDL_MOUSEWHEEL` for
* @ref MouseMoveEvent. * @ref MouseScrollEvent.
* @see @ref Sdl2Application::anyEvent() * @see @ref Sdl2Application::anyEvent()
*/ */
const SDL_Event& event() const { return _event; } const SDL_Event& event() const { return _event; }
@ -2585,13 +2701,85 @@ class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent {
const bool _repeated; 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> _modifiers;
const Vector2 _position;
#ifndef CORRADE_TARGET_EMSCRIPTEN
const Int _clickCount;
#endif
};
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@brief Mouse event @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(), @see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(),
@ref mouseReleaseEvent() @ref mouseReleaseEvent()
*/ */
class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { class CORRADE_DEPRECATED("use PointerEvent, pointerPressEvent() and pointerReleaseEvent() instead") Sdl2Application::MouseEvent: public InputEvent {
public: public:
/** /**
* @brief Mouse button * @brief Mouse button
@ -2652,13 +2840,90 @@ class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent {
#endif #endif
Containers::Optional<Modifiers> _modifiers; Containers::Optional<Modifiers> _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> 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> pointer, Pointers pointers, const Vector2& position, const Vector2& relativePosition): InputEvent{event}, _pointer{pointer}, _pointers{pointers}, _position{position}, _relativePosition{relativePosition} {}
const Containers::Optional<Pointer> _pointer;
const Pointers _pointers;
const Vector2 _position, _relativePosition;
Containers::Optional<Modifiers> _modifiers;
};
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@brief Mouse move event @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() @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: public:
/** /**
* @brief Mouse button * @brief Mouse button
@ -2714,10 +2979,15 @@ class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent {
Containers::Optional<Modifiers> _modifiers; Containers::Optional<Modifiers> _modifiers;
}; };
CORRADE_IGNORE_DEPRECATED_PUSH
CORRADE_ENUMSET_OPERATORS(Sdl2Application::MouseMoveEvent::Buttons)
CORRADE_IGNORE_DEPRECATED_POP
#endif
/** /**
@brief Mouse scroll event @brief Mouse scroll event
@see @ref MouseEvent, @ref MouseMoveEvent, @ref mouseScrollEvent() @see @ref PointerEvent, @ref PointerMoveEvent, @ref mouseScrollEvent()
*/ */
class Sdl2Application::MouseScrollEvent: public Sdl2Application::InputEvent { class Sdl2Application::MouseScrollEvent: public Sdl2Application::InputEvent {
public: public:
@ -3007,7 +3277,6 @@ typedef BasicScreenedApplication<Sdl2Application> ScreenedApplication;
CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags) CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags)
CORRADE_ENUMSET_OPERATORS(Sdl2Application::InputEvent::Modifiers) CORRADE_ENUMSET_OPERATORS(Sdl2Application::InputEvent::Modifiers)
CORRADE_ENUMSET_OPERATORS(Sdl2Application::MouseMoveEvent::Buttons)
}} }}

62
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 << ")"; 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; debug << "Button" << Debug::nospace;
switch(value) { switch(value) {
@ -92,6 +110,8 @@ static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")";
} }
CORRADE_IGNORE_DEPRECATED_POP
#endif
namespace Test { namespace { 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; debug << "Button" << Debug::nospace;
switch(value) { switch(value) {
@ -123,7 +155,7 @@ Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) {
return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; 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{}", { return Containers::enumSetDebugOutput(debug, value, "Buttons{}", {
Application::MouseMoveEvent::Button::Left, Application::MouseMoveEvent::Button::Left,
Application::MouseMoveEvent::Button::Middle, Application::MouseMoveEvent::Button::Middle,
@ -132,6 +164,8 @@ Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) {
Application::MouseMoveEvent::Button::X2, Application::MouseMoveEvent::Button::X2,
}); });
} }
CORRADE_IGNORE_DEPRECATED_POP
#endif
Debug& operator<<(Debug& debug, Application::KeyEvent::Key value) { Debug& operator<<(Debug& debug, Application::KeyEvent::Key value) {
debug << "Key" << Debug::nospace; debug << "Key" << Debug::nospace;
@ -285,22 +319,38 @@ struct Sdl2ApplicationTest: Platform::Application {
redraw(); 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 { void mousePressEvent(MouseEvent& event) override {
Debug{} << "mouse press:" << event.button() << Debug::packed << event.position() << event.modifiers(); Debug{} << "mouse press:" << event.button() << Debug::packed << event.position() << event.modifiers();
_gestureDistance = {}; _gestureDistance = {};
_gestureRotation = {}; _gestureRotation = {};
} }
void mouseReleaseEvent(MouseEvent& event) override { void mouseReleaseEvent(MouseEvent& event) override {
Debug{} << "mouse release:" << event.button() << Debug::packed << event.position() << event.modifiers(); Debug{} << "mouse release:" << event.button() << Debug::packed << event.position() << event.modifiers();
_gestureDistance = {}; _gestureDistance = {};
_gestureRotation = {}; _gestureRotation = {};
} }
void mouseMoveEvent(MouseMoveEvent& event) override { void mouseMoveEvent(MouseMoveEvent& event) override {
Debug{} << "mouse move:" << event.buttons() << Debug::packed << event.position() << Debug::packed << event.relativePosition() << event.modifiers(); 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 { void mouseScrollEvent(MouseScrollEvent& event) override {
Debug{} << "mouse scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position(); Debug{} << "mouse scroll:" << event.modifiers() << Debug::packed << event.offset() << Debug::packed << event.position();

Loading…
Cancel
Save