diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index f3765bff0..8ee116619 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -28,6 +28,7 @@ #include #include +#include #include "Magnum/Version.h" #include "Magnum/Platform/Context.h" @@ -138,6 +139,7 @@ bool GlfwApplication::tryCreateContext(const Configuration& configuration) { glfwSetCursorPosCallback(_window, staticMouseMoveEvent); glfwSetMouseButtonCallback(_window, staticMouseEvent); glfwSetScrollCallback(_window, staticMouseScrollEvent); + glfwSetCharCallback(_window, staticTextInputEvent); glfwMakeContextCurrent(_window); @@ -219,6 +221,15 @@ void GlfwApplication::staticMouseScrollEvent(GLFWwindow* window, double xoffset, #endif } +void GlfwApplication::staticTextInputEvent(GLFWwindow*, unsigned int codepoint) { + if(!(_instance->_flags & Flag::TextInputActive)) return; + + char utf8[4]; + const std::size_t size = Utility::Unicode::utf8(codepoint, utf8); + TextInputEvent e{{utf8, size}}; + _instance->textInputEvent(e); +} + void GlfwApplication::staticErrorCallback(int, const char* description) { Error() << description; } @@ -247,6 +258,7 @@ void GlfwApplication::mousePressEvent(MouseEvent&) {} void GlfwApplication::mouseReleaseEvent(MouseEvent&) {} void GlfwApplication::mouseMoveEvent(MouseMoveEvent&) {} void GlfwApplication::mouseScrollEvent(MouseScrollEvent&) {} +void GlfwApplication::textInputEvent(TextInputEvent&) {} GlfwApplication::Configuration::Configuration(): _title{"Magnum GLFW Application"}, diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index f1dc0e005..279e942f9 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -32,6 +32,7 @@ #include #include +#include #include "Magnum/Magnum.h" #include "Magnum/Math/Vector2.h" @@ -110,6 +111,7 @@ class GlfwApplication { class MouseEvent; class MouseMoveEvent; class MouseScrollEvent; + class TextInputEvent; /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ #ifdef DOXYGEN_GENERATING_OUTPUT @@ -251,9 +253,53 @@ class GlfwApplication { /*@}*/ + /** @{ @name Text input handling */ + public: + /** + * @brief Whether text input is active + * + * If text input is active, text input events go to + * @ref textInputEvent(). + * @see @ref startTextInput(), @ref stopTextInput() + */ + bool isTextInputActive() const { return !!(_flags & Flag::TextInputActive); } + + /** + * @brief Start text input + * + * Starts text input that will go to @ref textInputEvent(). + * @see @ref stopTextInput(), @ref isTextInputActive() + */ + void startTextInput() { _flags |= Flag::TextInputActive; } + + /** + * @brief Stop text input + * + * Stops text input that went to @ref textInputEvent(). + * @see @ref startTextInput(), @ref isTextInputActive(), + * @ref textInputEvent() + */ + void stopTextInput() { _flags &= ~Flag::TextInputActive; } + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** + * @brief Text input event + * + * Called when text input is active and the text is being input. + * @see @ref isTextInputActive() + */ + virtual void textInputEvent(TextInputEvent& event); + + /*@}*/ + private: enum class Flag: UnsignedByte { - Redraw = 1 << 0 + Redraw = 1 << 0, + TextInputActive = 1 << 1 }; typedef Containers::EnumSet Flags; @@ -273,6 +319,8 @@ class GlfwApplication { static void staticErrorCallback(int error, const char* description); + static void staticTextInputEvent(GLFWwindow* window, unsigned int codepoint); + static GlfwApplication* _instance; GLFWwindow* _window; @@ -922,6 +970,50 @@ class GlfwApplication::MouseScrollEvent: public GlfwApplication::InputEvent { const Modifiers _modifiers; }; +/** +@brief Text input event + +@see @ref textInputEvent() +*/ +class GlfwApplication::TextInputEvent { + friend GlfwApplication; + + public: + /** @brief Copying is not allowed */ + TextInputEvent(const TextInputEvent&) = delete; + + /** @brief Moving is not allowed */ + TextInputEvent(TextInputEvent&&) = delete; + + /** @brief Copying is not allowed */ + TextInputEvent& operator=(const TextInputEvent&) = delete; + + /** @brief Moving is not allowed */ + TextInputEvent& operator=(TextInputEvent&&) = delete; + + /** @brief Whether the event is accepted */ + constexpr bool isAccepted() const { return _accepted; } + + /** + * @brief Set event as accepted + * + * If the event is ignored (i.e., not set as accepted), it might be + * propagated elsewhere, for example to another screen when using + * @ref BasicScreenedApplication "ScreenedApplication". By default is + * each event ignored and thus propagated. + */ + void setAccepted(bool accepted = true) { _accepted = accepted; } + + /** @brief Input text in UTF-8 */ + constexpr Containers::ArrayView text() const { return _text; } + + private: + constexpr TextInputEvent(Containers::ArrayView text): _text{text}, _accepted{false} {} + + Containers::ArrayView _text; + bool _accepted; +}; + /** @hideinitializer @brief Entry point for GLFW-based applications @param className Class name