aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-11-01 13:17:59 +0100
committerDavid Robillard <d@drobilla.net>2020-11-01 13:43:16 +0100
commit71a9fbf296928d3599743e88f72390ff827c5ab3 (patch)
tree473f96ae254843f4bbfd3d5b9b6f1acb4fce2ce8
parent92edbddd304c5df0b1c89b8248053b0c1bfd9158 (diff)
downloadpugl-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.hpp340
-rw-r--r--bindings/cxx/include/pugl/vulkan.hpp2
-rw-r--r--examples/pugl_cxx_demo.cpp10
-rw-r--r--examples/pugl_vulkan_cxx_demo.cpp14
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);