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
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,

6
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] */

206
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<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
, 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<MouseMoveEvent::Button>(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<void>(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<void>(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<void>(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;

325
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<Pointer> 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<Modifier> 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> _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> _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
@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> _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<Sdl2Application> ScreenedApplication;
CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags)
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 << ")";
}
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();

Loading…
Cancel
Save