From 2828548d7d1205741b3c6060498fdc7fd2d57d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 1 Aug 2019 19:17:46 +0200 Subject: [PATCH] Platform: expose mouseScrollEvent() and text*Event() in Screen APIs. Because not all applications implement these, it's done via a "mixin". I never did such a thing before, just got an idea that it could work and I'm equally amazed and horrified that it actually DOES WORK. The Screen will now expose the MouseScrollEvent, TextInputEvent and TextEditingEvent typedefs only if the underlying application has them too and provides the overrideable mouseScrollEvent(), textInputEvent() and textEditingEvent() also only if the underlying application has them -- that also means you can't `override` those if the app doesn't provide such APIs, which acts as a nice check against accidental overgenericity. --- doc/changelog.dox | 5 + src/Magnum/Platform/Screen.h | 112 +++++++++++++++++++- src/Magnum/Platform/ScreenedApplication.h | 59 ++++++++++- src/Magnum/Platform/ScreenedApplication.hpp | 57 ++++++++++ 4 files changed, 229 insertions(+), 4 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 5141ab8d0..3a12bc665 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -352,6 +352,11 @@ See also: @ref Platform::GlfwApplication::exit() now have an optional parameter to specify the actual application return code (see [mosra/magnum#332](https://github.com/mosra/magnum/pull/332)) +- Extended @ref Platform::BasicScreen with + @ref Platform::BasicScreen::mouseScrollEvent() "mouseScrollEvent()", + @ref Platform::BasicScreen::textInputEvent() "textInputEvent()" and + @ref Platform::BasicScreen::textEditingEvent() "textEditingEvent()" on + application implementations that provide such events @subsubsection changelog-latest-changes-text Text library diff --git a/src/Magnum/Platform/Screen.h b/src/Magnum/Platform/Screen.h index 075199715..d5b90ce39 100644 --- a/src/Magnum/Platform/Screen.h +++ b/src/Magnum/Platform/Screen.h @@ -45,6 +45,43 @@ enum class PropagatedScreenEvent: UnsignedByte { typedef Containers::EnumSet PropagatedScreenEvents; CORRADE_ENUMSET_OPERATORS(PropagatedScreenEvents) +/* These provide overrideable event handlers on the Screen side for events that + are not implemented by all apps. The virtual *Event() function is defined + only if the base Application has it. Calling into those is done through + a corresponding Application*EventMixin defined in ScreenedApplication.h. */ +template class ScreenMouseScrollEventMixin {}; +template class ScreenMouseScrollEventMixin { + public: + typedef typename BasicScreenedApplication::MouseScrollEvent MouseScrollEvent; + + private: + friend ApplicationMouseScrollEventMixin; + + virtual void mouseScrollEvent(MouseScrollEvent& event); +}; + +template class ScreenTextInputEventMixin {}; +template class ScreenTextInputEventMixin { + public: + typedef typename BasicScreenedApplication::TextInputEvent TextInputEvent; + + private: + friend ApplicationTextInputEventMixin; + + virtual void textInputEvent(TextInputEvent& event); +}; + +template class ScreenTextEditingEventMixin {}; +template class ScreenTextEditingEventMixin { + public: + typedef typename BasicScreenedApplication::TextEditingEvent TextEditingEvent; + + private: + friend ApplicationTextEditingEventMixin; + + virtual void textEditingEvent(TextEditingEvent& event); +}; + } /** @@ -69,7 +106,12 @@ The following specialization are explicitly compiled into each particular - @ref Sdl2Application "BasicScreen" - @ref XEglApplication "BasicScreen" */ -template class BasicScreen: private Containers::LinkedListItem, BasicScreenedApplication> { +template class BasicScreen: + private Containers::LinkedListItem, BasicScreenedApplication>, + public Implementation::ScreenMouseScrollEventMixin::value>, + public Implementation::ScreenTextInputEventMixin::value>, + public Implementation::ScreenTextEditingEventMixin::value> +{ public: #ifdef DOXYGEN_GENERATING_OUTPUT /** @@ -89,8 +131,10 @@ template class BasicScreen: private Containers::LinkedListIte * Input events. * * When enabled, @ref keyPressEvent(), @ref keyReleaseEvent(), - * @ref mousePressEvent(), @ref mouseReleaseEvent() and - * @ref mouseMoveEvent() are propagated to this screen. + * @ref mousePressEvent(), @ref mouseReleaseEvent(), + * @ref mouseMoveEvent(), @ref mouseScrollEvent(), + * @ref textInputEvent() and @ref textEditingEvent() are propagated + * to this screen. */ Input = 1 << 1 }; @@ -121,6 +165,32 @@ template class BasicScreen: private Containers::LinkedListIte /** @brief Mouse move event */ typedef typename BasicScreenedApplication::MouseMoveEvent MouseMoveEvent; + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Mouse scroll event + * + * Defined only if the application has a + * @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". + */ + typedef typename BasicScreenedApplication::MouseScrollEvent MouseScrollEvent; + + /** + * @brief Text input event + * + * Defined only if the application has a + * @ref Sdl2Application::TextInputEvent "TextInputEvent". + */ + typedef typename BasicScreenedApplication::TextInputEvent TextInputEvent; + + /** + * @brief Text editing event + * + * Defined only if the application has a + * @ref Sdl2Application::TextEditingEvent "TextEditingEvent". + */ + typedef typename BasicScreenedApplication::TextEditingEvent TextEditingEvent; + #endif + explicit BasicScreen(); ~BasicScreen(); @@ -284,6 +354,42 @@ template class BasicScreen: private Containers::LinkedListIte */ virtual void mouseMoveEvent(MouseMoveEvent& event); + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Mouse scroll event + * + * Called when @ref PropagatedEvent::Input is enabled and mouse wheel + * is rotated. See @ref Sdl2Application::mouseScrollEvent() "*Application::mouseScrollEvent()" + * for more information. Defined only if the application has a + * @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". + */ + virtual void mouseScrollEvent(MouseScrollEvent& event); + #endif + + /*@}*/ + + /** @{ @name Text input handling */ + + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Text input event + * + * Called when @ref PropagatedEvent::Input is enabled and text is being + * input. Defined only if the application has a + * @ref Sdl2Application::TextInputEvent "TextInputEvent". + */ + virtual void textInputEvent(TextInputEvent& event); + + /** + * @brief Text editing event + * + * Called when @ref PropagatedEvent::Input and the text is being + * edited. Defined only if the application has a + * @ref Sdl2Application::TextEditingEvent "TextEditingEvent". + */ + virtual void textEditingEvent(TextEditingEvent& event); + #endif + /*@}*/ private: diff --git a/src/Magnum/Platform/ScreenedApplication.h b/src/Magnum/Platform/ScreenedApplication.h index 887e5e447..122001204 100644 --- a/src/Magnum/Platform/ScreenedApplication.h +++ b/src/Magnum/Platform/ScreenedApplication.h @@ -37,6 +37,53 @@ namespace Magnum { namespace Platform { +namespace Implementation { + +CORRADE_HAS_TYPE(HasMouseScrollEvent, typename T::MouseScrollEvent); +CORRADE_HAS_TYPE(HasTextInputEvent, typename T::TextInputEvent); +CORRADE_HAS_TYPE(HasTextEditingEvent, typename T::TextEditingEvent); + +/* Calls into the screen in case the application has a mouseScrollEvent(), + otherwise provides a dummy virtual so the application can unconditionally + override */ +template struct ApplicationMouseScrollEventMixin { + typedef int MouseScrollEvent; + virtual void mouseScrollEvent(MouseScrollEvent&) = 0; + + void callMouseScrollEvent(MouseScrollEvent&, Containers::LinkedList>&); +}; +template struct ApplicationMouseScrollEventMixin { + void callMouseScrollEvent(typename Application::MouseScrollEvent& event, Containers::LinkedList>& screens); +}; + +/* Calls into the screen in case the application has a textInputEvent(), + otherwise provides a dummy virtual so the application can unconditionally + override */ +template struct ApplicationTextInputEventMixin { + typedef int TextInputEvent; + virtual void textInputEvent(TextInputEvent&) = 0; + + void callTextInputEvent(TextInputEvent&, Containers::LinkedList>&); +}; +template struct ApplicationTextInputEventMixin { + void callTextInputEvent(typename Application::TextInputEvent& event, Containers::LinkedList>& screens); +}; + +/* Calls into the screen in case the application has a textEditingEvent(), + otherwise provides a dummy virtual so the application can unconditionally + override */ +template struct ApplicationTextEditingEventMixin { + typedef int TextEditingEvent; + virtual void textEditingEvent(TextEditingEvent&) = 0; + + void callTextEditingEvent(TextEditingEvent&, Containers::LinkedList>&); +}; +template struct ApplicationTextEditingEventMixin { + void callTextEditingEvent(typename Application::TextEditingEvent& event, Containers::LinkedList>& screens); +}; + +} + /** @brief Base for applications with screen management @@ -98,7 +145,13 @@ The following specialization are explicitly compiled into each particular - @ref Sdl2Application "BasicScreenedApplication" - @ref XEglApplication "BasicScreenedApplication" */ -template class BasicScreenedApplication: public Application, private Containers::LinkedList> { +template class BasicScreenedApplication: + public Application, + private Containers::LinkedList>, + private Implementation::ApplicationMouseScrollEventMixin::value>, + private Implementation::ApplicationTextInputEventMixin::value>, + private Implementation::ApplicationTextEditingEventMixin::value> +{ public: #ifdef MAGNUM_TARGET_GL /** @@ -221,6 +274,10 @@ template class BasicScreenedApplication: public Application, void mousePressEvent(typename Application::MouseEvent& event) override final; void mouseReleaseEvent(typename Application::MouseEvent& event) override final; void mouseMoveEvent(typename Application::MouseMoveEvent& event) override final; + + void mouseScrollEvent(typename BasicScreenedApplication::MouseScrollEvent& event) override final; + void textInputEvent(typename BasicScreenedApplication::TextInputEvent& event) override final; + void textEditingEvent(typename BasicScreenedApplication::TextEditingEvent& event) override final; }; }} diff --git a/src/Magnum/Platform/ScreenedApplication.hpp b/src/Magnum/Platform/ScreenedApplication.hpp index ac5a581c6..06ff14144 100644 --- a/src/Magnum/Platform/ScreenedApplication.hpp +++ b/src/Magnum/Platform/ScreenedApplication.hpp @@ -34,6 +34,51 @@ namespace Magnum { namespace Platform { +namespace Implementation { + +template void ApplicationMouseScrollEventMixin::callMouseScrollEvent(MouseScrollEvent&, Containers::LinkedList>&) {} +template void ApplicationMouseScrollEventMixin::callMouseScrollEvent(typename Application::MouseScrollEvent& event, Containers::LinkedList>& screens) { + /* Front-to-back event propagation, stop when the event gets accepted */ + for(BasicScreen* s = screens.first(); s; s = s->nextFartherScreen()) { + if(s->propagatedEvents() & Implementation::PropagatedScreenEvent::Input) { + s->mouseScrollEvent(event); + if(event.isAccepted()) break; + } + } +} + +template void ApplicationTextInputEventMixin::callTextInputEvent(TextInputEvent&, Containers::LinkedList>&) {} +template void ApplicationTextInputEventMixin::callTextInputEvent(typename Application::TextInputEvent& event, Containers::LinkedList>& screens) { + /* Front-to-back event propagation, stop when the event gets accepted */ + for(BasicScreen* s = screens.first(); s; s = s->nextFartherScreen()) { + if(s->propagatedEvents() & Implementation::PropagatedScreenEvent::Input) { + s->textInputEvent(event); + if(event.isAccepted()) break; + } + } +} + +template void ApplicationTextEditingEventMixin::callTextEditingEvent(TextEditingEvent&, Containers::LinkedList>&) {} +template void ApplicationTextEditingEventMixin::callTextEditingEvent(typename Application::TextEditingEvent& event, Containers::LinkedList>& screens) { + /* Front-to-back event propagation, stop when the event gets accepted */ + for(BasicScreen* s = screens.first(); s; s = s->nextFartherScreen()) { + if(s->propagatedEvents() & Implementation::PropagatedScreenEvent::Input) { + s->textEditingEvent(event); + if(event.isAccepted()) break; + } + } +} + +template void ScreenMouseScrollEventMixin::mouseScrollEvent(MouseScrollEvent&) {} +template void ScreenTextInputEventMixin::textInputEvent(TextInputEvent&) {} +template void ScreenTextEditingEventMixin::textEditingEvent(TextEditingEvent&) {} + +} + template BasicScreen::BasicScreen() = default; template BasicScreen::~BasicScreen() = default; @@ -160,6 +205,18 @@ template void BasicScreenedApplication::mouseMov } } +template void BasicScreenedApplication::mouseScrollEvent(typename BasicScreenedApplication::MouseScrollEvent& event) { + this->callMouseScrollEvent(event, screens()); +} + +template void BasicScreenedApplication::textInputEvent(typename BasicScreenedApplication::TextInputEvent& event) { + this->callTextInputEvent(event, screens()); +} + +template void BasicScreenedApplication::textEditingEvent(typename BasicScreenedApplication::TextEditingEvent& event) { + this->callTextEditingEvent(event, screens()); +} + }} #endif