diff options
author | David Robillard <d@drobilla.net> | 2020-11-01 13:17:59 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-11-01 13:43:16 +0100 |
commit | 71a9fbf296928d3599743e88f72390ff827c5ab3 (patch) | |
tree | 473f96ae254843f4bbfd3d5b9b6f1acb4fce2ce8 | |
parent | 92edbddd304c5df0b1c89b8248053b0c1bfd9158 (diff) | |
download | pugl-71a9fbf296928d3599743e88f72390ff827c5ab3.tar.gz pugl-71a9fbf296928d3599743e88f72390ff827c5ab3.tar.bz2 pugl-71a9fbf296928d3599743e88f72390ff827c5ab3.zip |
Make event handling in C++ more flexible
-rw-r--r-- | bindings/cxx/include/pugl/pugl.hpp | 340 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/vulkan.hpp | 2 | ||||
-rw-r--r-- | examples/pugl_cxx_demo.cpp | 10 | ||||
-rw-r--r-- | examples/pugl_vulkan_cxx_demo.cpp | 14 |
4 files changed, 225 insertions, 141 deletions
diff --git a/bindings/cxx/include/pugl/pugl.hpp b/bindings/cxx/include/pugl/pugl.hpp index b7d3d14..4f5f3cf 100644 --- a/bindings/cxx/include/pugl/pugl.hpp +++ b/bindings/cxx/include/pugl/pugl.hpp @@ -353,14 +353,8 @@ enum class Cursor { static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, ""); -/** - A drawable region that receives events. - - This is the generic base class for all views. It can be used directly by - manually setting an event handling function, but typical applications should - use pugl::View which handles dispatching events. -*/ -class ViewBase : protected detail::Wrapper<PuglView, puglFreeView> +/// @copydoc PuglView +class View : protected detail::Wrapper<PuglView, puglFreeView> { public: /** @@ -369,7 +363,7 @@ public: @{ */ - explicit ViewBase(World& world) + explicit View(World& world) : Wrapper{puglNewView(world.cobj())} , _world(world) { @@ -379,6 +373,46 @@ public: const World& world() const noexcept { return _world; } World& world() noexcept { return _world; } + /** + Set the object that will be called to handle events. + + This is a type-safe wrapper for the C functions puglSetHandle() and + puglSetEventFunc() that will automatically dispatch events to the + `onEvent` method of `handler` that takes the appropriate event type. + The handler must have such a method defined for every event type, but if + the handler is the view itself, a `using` declaration can be used to + "inherit" the default implementation to avoid having to define every + method. For example: + + @code + class MyView : public pugl::View + { + public: + explicit MyView(pugl::World& world) + : pugl::View{world} + { + setEventHandler(*this); + } + + using pugl::View::onEvent; + + pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; + pugl::Status onEvent(const pugl::ExposeEvent& event) noexcept; + }; + @endcode + + This facility is just a convenience, applications may use the C API + directly to set a handle and event function to set up a different + approach for event handling. + */ + template<class Handler> + Status setEventHandler(Handler& handler) + { + puglSetHandle(cobj(), &handler); + return static_cast<Status>( + puglSetEventFunc(cobj(), eventFunc<Handler>)); + } + /// @copydoc puglSetBackend Status setBackend(const PuglBackend* backend) noexcept { @@ -552,176 +586,222 @@ public: return static_cast<Status>(puglStopTimer(cobj(), id)); } - PuglView* cobj() noexcept { return Wrapper::cobj(); } - const PuglView* cobj() const noexcept { return Wrapper::cobj(); } + /** + @} + @name Event Handlers + Methods called when events are dispatched to the view. -private: - World& _world; -}; + For convenience, the methods defined here are all trivial stubs that + return success. + @{ + */ -/** - A view with event handlers. + static Status onEvent(const CreateEvent&) noexcept + { + return Status::success; + } - To implement a view, applications can inherit from this class, which handles - type-safe dispatching of events to methods. This class uses the CRTP - pattern, so the name of the derived class needs to be passed as a template - paramter, for example: + static Status onEvent(const DestroyEvent&) noexcept + { + return Status::success; + } - @code - class MyView : public pugl::View<MyView> - @endcode -*/ -template<class Derived> -class View : public ViewBase -{ -public: - explicit View(World& world) - : ViewBase{world} + static Status onEvent(const ConfigureEvent&) noexcept { - if (cobj()) { - puglSetHandle(cobj(), this); - puglSetEventFunc(cobj(), eventFunc); - } + return Status::success; } - /** - @} - @name Event Handlers - Methods called when events are dispatched to the view. - @{ - */ + static Status onEvent(const MapEvent&) noexcept { return Status::success; } - Status onEvent(const CreateEvent&) noexcept { return Status::success; } - Status onEvent(const DestroyEvent&) noexcept { return Status::success; } - Status onEvent(const ConfigureEvent&) noexcept { return Status::success; } - Status onEvent(const MapEvent&) noexcept { return Status::success; } - Status onEvent(const UnmapEvent&) noexcept { return Status::success; } - Status onEvent(const UpdateEvent&) noexcept { return Status::success; } - Status onEvent(const ExposeEvent&) noexcept { return Status::success; } - Status onEvent(const CloseEvent&) noexcept { return Status::success; } - Status onEvent(const FocusInEvent&) noexcept { return Status::success; } - Status onEvent(const FocusOutEvent&) noexcept { return Status::success; } - Status onEvent(const KeyPressEvent&) noexcept { return Status::success; } - Status onEvent(const KeyReleaseEvent&) noexcept { return Status::success; } - Status onEvent(const TextEvent&) noexcept { return Status::success; } - Status onEvent(const PointerInEvent&) noexcept { return Status::success; } - Status onEvent(const PointerOutEvent&) noexcept { return Status::success; } - Status onEvent(const ButtonPressEvent&) noexcept { return Status::success; } - Status onEvent(const ButtonReleaseEvent&) noexcept + static Status onEvent(const UnmapEvent&) noexcept { return Status::success; } - Status onEvent(const MotionEvent&) noexcept { return Status::success; } - Status onEvent(const ScrollEvent&) noexcept { return Status::success; } - Status onEvent(const ClientEvent&) noexcept { return Status::success; } - Status onEvent(const TimerEvent&) noexcept { return Status::success; } - Status onEvent(const LoopEnterEvent&) noexcept { return Status::success; } - Status onEvent(const LoopLeaveEvent&) noexcept { return Status::success; } - /** - @} - */ + static Status onEvent(const UpdateEvent&) noexcept + { + return Status::success; + } -private: - Derived& self() noexcept { return *static_cast<Derived*>(this); } - const Derived& self() const noexcept + static Status onEvent(const ExposeEvent&) noexcept { - return *static_cast<Derived*>(this); + return Status::success; } - static PuglStatus eventFunc(PuglView* view, const PuglEvent* event) noexcept + static Status onEvent(const CloseEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const FocusInEvent&) noexcept { - View* self = static_cast<View*>(puglGetHandle(view)); + return Status::success; + } -#ifdef __cpp_exceptions - try { - return self->dispatch(event); - } catch (...) { - return PUGL_UNKNOWN_ERROR; - } -#else - return self->dispatch(event); -#endif + static Status onEvent(const FocusOutEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const KeyPressEvent&) noexcept + { + return Status::success; } - PuglStatus dispatch(const PuglEvent* event) noexcept + static Status onEvent(const KeyReleaseEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const TextEvent&) noexcept { return Status::success; } + + static Status onEvent(const PointerInEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const PointerOutEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const ButtonPressEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const ButtonReleaseEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const MotionEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const ScrollEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const ClientEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const TimerEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const LoopEnterEvent&) noexcept + { + return Status::success; + } + + static Status onEvent(const LoopLeaveEvent&) noexcept + { + return Status::success; + } + + /** + @} + */ + + PuglView* cobj() noexcept { return Wrapper::cobj(); } + const PuglView* cobj() const noexcept { return Wrapper::cobj(); } + +private: + template<class Target> + static Status dispatch(Target& target, const PuglEvent* event) { switch (event->type) { case PUGL_NOTHING: - return PUGL_SUCCESS; + return Status::success; case PUGL_CREATE: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const CreateEvent&>(event->any))); + return target.onEvent(static_cast<const CreateEvent&>(event->any)); case PUGL_DESTROY: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const DestroyEvent&>(event->any))); + return target.onEvent(static_cast<const DestroyEvent&>(event->any)); case PUGL_CONFIGURE: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const ConfigureEvent&>(event->configure))); + return target.onEvent( + static_cast<const ConfigureEvent&>(event->configure)); case PUGL_MAP: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const MapEvent&>(event->any))); + return target.onEvent(static_cast<const MapEvent&>(event->any)); case PUGL_UNMAP: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const UnmapEvent&>(event->any))); + return target.onEvent(static_cast<const UnmapEvent&>(event->any)); case PUGL_UPDATE: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const UpdateEvent&>(event->any))); + return target.onEvent(static_cast<const UpdateEvent&>(event->any)); case PUGL_EXPOSE: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const ExposeEvent&>(event->expose))); + return target.onEvent( + static_cast<const ExposeEvent&>(event->expose)); case PUGL_CLOSE: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const CloseEvent&>(event->any))); + return target.onEvent(static_cast<const CloseEvent&>(event->any)); case PUGL_FOCUS_IN: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const FocusInEvent&>(event->focus))); + return target.onEvent( + static_cast<const FocusInEvent&>(event->focus)); case PUGL_FOCUS_OUT: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const FocusOutEvent&>(event->focus))); + return target.onEvent( + static_cast<const FocusOutEvent&>(event->focus)); case PUGL_KEY_PRESS: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const KeyPressEvent&>(event->key))); + return target.onEvent( + static_cast<const KeyPressEvent&>(event->key)); case PUGL_KEY_RELEASE: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const KeyReleaseEvent&>(event->key))); + return target.onEvent( + static_cast<const KeyReleaseEvent&>(event->key)); case PUGL_TEXT: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const TextEvent&>(event->text))); + return target.onEvent(static_cast<const TextEvent&>(event->text)); case PUGL_POINTER_IN: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const PointerInEvent&>(event->crossing))); + return target.onEvent( + static_cast<const PointerInEvent&>(event->crossing)); case PUGL_POINTER_OUT: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const PointerOutEvent&>(event->crossing))); + return target.onEvent( + static_cast<const PointerOutEvent&>(event->crossing)); case PUGL_BUTTON_PRESS: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const ButtonPressEvent&>(event->button))); + return target.onEvent( + static_cast<const ButtonPressEvent&>(event->button)); case PUGL_BUTTON_RELEASE: - return static_cast<PuglStatus>(self().onEvent( - static_cast<const ButtonReleaseEvent&>(event->button))); + return target.onEvent( + static_cast<const ButtonReleaseEvent&>(event->button)); case PUGL_MOTION: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const MotionEvent&>(event->motion))); + return target.onEvent( + static_cast<const MotionEvent&>(event->motion)); case PUGL_SCROLL: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const ScrollEvent&>(event->scroll))); + return target.onEvent( + static_cast<const ScrollEvent&>(event->scroll)); case PUGL_CLIENT: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const ClientEvent&>(event->client))); + return target.onEvent( + static_cast<const ClientEvent&>(event->client)); case PUGL_TIMER: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const TimerEvent&>(event->timer))); + return target.onEvent(static_cast<const TimerEvent&>(event->timer)); case PUGL_LOOP_ENTER: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const LoopEnterEvent&>(event->any))); + return target.onEvent( + static_cast<const LoopEnterEvent&>(event->any)); case PUGL_LOOP_LEAVE: - return static_cast<PuglStatus>( - self().onEvent(static_cast<const LoopLeaveEvent&>(event->any))); + return target.onEvent( + static_cast<const LoopLeaveEvent&>(event->any)); } - return PUGL_FAILURE; + return Status::failure; + } + + template<class Target> + static PuglStatus eventFunc(PuglView* view, const PuglEvent* event) noexcept + { + auto* target = static_cast<Target*>(puglGetHandle(view)); + +#ifdef __cpp_exceptions + try { + return static_cast<PuglStatus>(dispatch(*target, event)); + } catch (...) { + return PUGL_UNKNOWN_ERROR; + } +#else + return static_cast<PuglStatus>(pugl::dispatch(*target, event)); +#endif } + + World& _world; }; /** diff --git a/bindings/cxx/include/pugl/vulkan.hpp b/bindings/cxx/include/pugl/vulkan.hpp index f9737e8..a7b16cb 100644 --- a/bindings/cxx/include/pugl/vulkan.hpp +++ b/bindings/cxx/include/pugl/vulkan.hpp @@ -143,7 +143,7 @@ getInstanceExtensions() noexcept /// @copydoc puglCreateSurface inline VkResult createSurface(const VulkanLoader& loader, - ViewBase& view, + View& view, VkInstance instance, const VkAllocationCallbacks* const allocator, VkSurfaceKHR* const surface) noexcept diff --git a/examples/pugl_cxx_demo.cpp b/examples/pugl_cxx_demo.cpp index 4dab35c..9767345 100644 --- a/examples/pugl_cxx_demo.cpp +++ b/examples/pugl_cxx_demo.cpp @@ -29,14 +29,16 @@ #include <cmath> -class CubeView : public pugl::View<CubeView> +class CubeView : public pugl::View { public: explicit CubeView(pugl::World& world) - : pugl::View<CubeView>{world} - {} + : pugl::View{world} + { + setEventHandler(*this); + } - using pugl::View<CubeView>::onEvent; + using pugl::View::onEvent; static pugl::Status onEvent(const pugl::ConfigureEvent& event) noexcept; pugl::Status onEvent(const pugl::UpdateEvent& event) noexcept; diff --git a/examples/pugl_vulkan_cxx_demo.cpp b/examples/pugl_vulkan_cxx_demo.cpp index a4634ee..e25f243 100644 --- a/examples/pugl_vulkan_cxx_demo.cpp +++ b/examples/pugl_vulkan_cxx_demo.cpp @@ -78,7 +78,7 @@ struct VulkanContext { struct GraphicsDevice { VkResult init(const pugl::VulkanLoader& loader, const VulkanContext& context, - pugl::ViewBase& view, + pugl::View& view, const PuglTestOptions& opts); sk::SurfaceKHR surface; @@ -409,7 +409,7 @@ selectPhysicalDevice(const sk::VulkanApi& vk, VkResult GraphicsDevice::init(const pugl::VulkanLoader& loader, const VulkanContext& context, - pugl::ViewBase& view, + pugl::View& view, const PuglTestOptions& opts) { const auto& vk = context.vk; @@ -1372,15 +1372,17 @@ recordCommandBuffers(const sk::VulkanApi& vk, class PuglVulkanDemo; -class View : public pugl::View<View> +class View : public pugl::View { public: View(pugl::World& world, PuglVulkanDemo& app) - : pugl::View<View>{world} + : pugl::View{world} , _app{app} - {} + { + setEventHandler(*this); + } - using pugl::View<View>::onEvent; + using pugl::View::onEvent; pugl::Status onEvent(const pugl::ConfigureEvent& event); pugl::Status onEvent(const pugl::UpdateEvent& event); |