Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
First shot at #436: Wrap away the SFML Event API
Browse files Browse the repository at this point in the history
It's already much nicer; the event loops in the test/example apps are
almost back to SFML2 simplicity and flexibility. ;)

Now event polling can (should) also go through the GUI manager (not just
directly using the SFML API to poll the host window), and, as a bonus,
it can now filter out the massive redundant flood of raw mouse-move events
transparently. (Which the SFML guys will obviously disable at some point
in the future anyway ;) but it's nice to have it dealt with already.)
  • Loading branch information
xparq committed Jul 19, 2024
1 parent c9b84a7 commit cd26755
Show file tree
Hide file tree
Showing 24 changed files with 449 additions and 166 deletions.
15 changes: 15 additions & 0 deletions include/SAL/Event.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef _DMOIUWR4EH56OU6HYU83DMR0Y98U7NH87MVI56BBR56N_
#define _DMOIUWR4EH56OU6HYU83DMR0Y98U7NH87MVI56BBR56N_


#include "SAL/event/Input.hpp"
#include "SAL/event/Source.hpp"
#include "SAL/event/Handler.hpp"


namespace SAL::event
{
} // namespace SAL::event


#endif // _DMOIUWR4EH56OU6HYU83DMR0Y98U7NH87MVI56BBR56N_
61 changes: 61 additions & 0 deletions include/SAL/event/Handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef _WCOVIREHJ5Y9UHW4C9UYH89U7VHME87RCMTYVE5B8TUYTYBJB_
#define _WCOVIREHJ5Y9UHW4C9UYH89U7VHME87RCMTYVE5B8TUYTYBJB_


#include "SAL/event/Input.hpp"

#include <functional>
#include <optional>
#include <variant>
#include <unordered_map>


namespace SAL::event
{
class Handler;
namespace internal
{
using Callback_void = std::optional<std::function<void()>>;
using Callback_w = std::optional<std::function<void(Handler*)>>;

//!!?? Does this make any measurable difference? (Likely even just using plain ordered map wouldn't.)
//!! Note also that this would put every single handler in its own distinct hash bucket! Sounds like
//!! a pretty blatant memory overhead, at the very least.)
template <typename T> struct IDHash
{
std::size_t operator()(const T& id) const { return static_cast<std::size_t>(id); }
};
}

using Callback = std::variant<internal::Callback_w, internal::Callback_void>;
using CallbackMap = std::unordered_map<ID, Callback, internal::IDHash<ID>>;
//!!?? using CallbackMap = std::unordered_map<ID, Callback>; // (see comment above at IDHash)


//--------------------------------------------------------------------
// Base class for entites that want to handle sfw::Events
//--------------------------------------------------------------------
class Handler // mixin, not for polymorphism via Handler* pointers!
{
protected:
CallbackMap m_callbackMap; // See Widget::on(event::ID, event::Callback)!

void on(event::ID e, event::Callback f) { m_callbackMap[e] = f; }

// General, "raw" events: user inputs, system events etc.
virtual void onMouseMoved(float, float) {}
virtual void onMousePressed(float, float) {}
virtual void onMouseReleased(float, float) {}
virtual void onMouseWheelMoved(int) {}
virtual void onMouseEnter() {}
virtual void onMouseLeave() {}
virtual void onKeyPressed(const sf::Event::KeyChanged&) {}
virtual void onKeyReleased(const sf::Event::KeyChanged&) {}
virtual void onTextEntered(char32_t) {}
virtual void onTick() {}
};

} // namespace SAL::event


#endif // _WCOVIREHJ5Y9UHW4C9UYH89U7VHME87RCMTYVE5B8TUYTYBJB_
29 changes: 29 additions & 0 deletions include/SAL/event/Input.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef _ASD8745YUYNGM8304C8M5NYPI8UYH398V5BMNYHU_
#define _ASD8745YUYNGM8304C8M5NYPI8UYH398V5BMNYHU_


#include "SAL.hpp"
#include SAL_ADAPTER(event/Input)


namespace SAL::event
{
//!! LEGACY LEFTOVER EXPERIM. codes for widget updates (or whatnot):
enum ID
{
Noop,
Update,
Change,
Commit,

_EVENTS_
};


//!!...
using Input = adapter::Input_Impl;

} // namespace SAL::event


#endif // _ASD8745YUYNGM8304C8M5NYPI8UYH398V5BMNYHU_
76 changes: 76 additions & 0 deletions include/SAL/event/Input/SFML/Impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef _SPPDXCW345YN879Y6X24UCN7NYH895V6RMUBUBR7IKNB_
#define _SPPDXCW345YN879Y6X24UCN7NYH895V6RMUBUBR7IKNB_


#include <SFML/Window/Event.hpp>

#include <optional> // forward
#include <utility> // forward

#include <cassert>

namespace SAL::event::adapter
{

struct Input_Impl
{
public:
using native_type = sf::Event;

private:
std::optional<native_type> _d;

public:
constexpr auto& native() { assert(_d); return _d.value(); }
constexpr const auto& native() const { assert(_d); return _d.value(); }

// Null instance:
//!!constexpr // not with assert() :-/
Input_Impl() { assert((bool)(*this) == false); }

// Copy (should also accept native_type):
Input_Impl(const std::optional<native_type>& other) : _d(other) {}

// Reuse the native ctors:
//!!?? using native_type::native_type;

// Reuse the native ctors:
template <typename... T>
constexpr Input_Impl(T&&... args) : _d(std::forward<T>(args)...) {}

/*!!
// Backend event type adapter base
struct InputType //!!: public sf::Event //!! The SFML category types don't have a common base, actually!
{
using native_type = sf::Event;
//!!...
};
!!*/
// Proxy accessors for sf::Event's std::optional API:
auto& operator -> () { assert(_d); return _d; }
const auto& operator -> () const { assert(_d); return _d; }

auto& value() { assert(_d); return std::forward< sf::Event&>(_d.value()); } //!!?? forward is superfluous?
const auto& value() const { assert(_d); return std::forward<const sf::Event&>(_d.value()); } //!!?? forward is superfluous?

operator bool() const { return _d.has_value(); }

template <typename InputT> bool is() const { return _d.has_value() ? _d->is<InputT>() : false; }

//!! Proxied sf:Event -> std::variant, which simply returns null on error! :)
template <typename InputT> const InputT* getIf() const { return _d->getIf<InputT>(); }

template <typename InputT> const InputT& get() const { assert(_d); return *(_d->getIf<InputT>()); }

// Type-specific accessors:
//!!ONLY FOR KeyChanged:
//!! auto code() { return _d.code(); }


}; // class Input_Impl


} // namespace SAL::event::adapter


#endif // _SPPDXCW345YN879Y6X24UCN7NYH895V6RMUBUBR7IKNB_
33 changes: 33 additions & 0 deletions include/SAL/event/Source.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef _SDFM80D4S6Y974DYFRMG978G4M7G9ERF80F3456GU3R5U_
#define _SDFM80D4S6Y974DYFRMG978G4M7G9ERF80F3456GU3R5U_


#include "SAL.hpp"
#include SAL_ADAPTER(event/Input)

#include <SFML/Window/WindowBase.hpp> // pollEvent, waitEvent etc.


namespace SAL::event
{
//!! LEGACY LEFTOVER EXPERIM. codes for widget updates (or whatnot):

class Source
{
public:
using nativ_type = sf::WindowBase;

nativ_type& _d;

public:
Source(nativ_type& backend_source) : _d(backend_source) {}


Input poll() { return _d.pollEvent(); }

}; // class Source

} // namespace SAL::event


#endif // _SDFM80D4S6Y974DYFRMG978G4M7G9ERF80F3456GU3R5U_
73 changes: 11 additions & 62 deletions include/sfw/Event.hpp
Original file line number Diff line number Diff line change
@@ -1,68 +1,17 @@
#ifndef SFW_EVENT_HPP
#define SFW_EVENT_HPP
#ifndef _SDC8E75YTT8UD4NFY978Y8F5795GH87OG5656UEFJYI_
#define _SDC8E75YTT8UD4NFY978Y8F5795GH87OG5656UEFJYI_

#include <SFML/Window/Event.hpp>

#include <functional>
#include <optional>
#include <variant>
#include <unordered_map>
#include "SAL/Event.hpp"

namespace sfw::Event
{
enum ID
{
Noop,
Update,
Change,
Commit,

_EVENTS_
};

class Handler;
namespace internal
{
using Callback_void = std::optional<std::function<void()>>;
using Callback_w = std::optional<std::function<void(Handler*)>>;

//!!?? Does this make any measurable difference? (Likely even just using plain ordered map wouldn't.)
//!! Note also that this would put every single handler in its own distinct hash bucket! Sounds like
//!! a pretty blatant memory overhead, at the very least.)
template <typename T> struct IDHash
{
std::size_t operator()(const T& id) const { return static_cast<std::size_t>(id); }
};
}

using Callback = std::variant<internal::Callback_w, internal::Callback_void>;
using CallbackMap = std::unordered_map<ID, Callback, internal::IDHash<ID>>;
//!!?? using CallbackMap = std::unordered_map<ID, Callback>; // (see comment above at IDHash)

//----------------------------------------------------------------------------
//!! Temporary kludge for the current unfinished C++ namespacing...
//----------------------------------------------------------------------------
namespace sfw::event
{
using namespace SAL::event;
}

//--------------------------------------------------------------------
// Base class for entites that want to handle sfw::Events
//--------------------------------------------------------------------
class Handler // mixin, not for polymorphism via Handler* pointers!
{
protected:
CallbackMap m_callbackMap; // See Widget::on(Event::ID, Event::Callback)!

void on(Event::ID e, Event::Callback f) { m_callbackMap[e] = f; }

// General, "raw" events: user inputs, system events etc.
virtual void onMouseMoved(float, float) {}
virtual void onMousePressed(float, float) {}
virtual void onMouseReleased(float, float) {}
virtual void onMouseWheelMoved(int) {}
virtual void onMouseEnter() {}
virtual void onMouseLeave() {}
virtual void onKeyPressed(const sf::Event::KeyChanged&) {}
virtual void onKeyReleased(const sf::Event::KeyChanged&) {}
virtual void onTextEntered(char32_t) {}
virtual void onTick() {}
};

} // namespace sfw::Event

#endif // SFW_EVENT_HPP
#endif // _SDC8E75YTT8UD4NFY978Y8F5795GH87OG5656UEFJYI_
21 changes: 16 additions & 5 deletions include/sfw/GUI-main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "sfw/gfx/Color.hpp"
#include "sfw/gfx/Render.hpp"
#include "sfw/math/Vector.hpp"
#include "sfw/Event.hpp"

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
Expand Down Expand Up @@ -69,13 +70,22 @@ class GUI: public VBox
bool active();
operator bool() { return active(); }


/**
* Get the last input event from the active input event source
* (The input source is always the event queue of the associated window
* currently.)
*/
event::Input poll();

/**
* Process events from the backend (i.e. SFML)
* Process input event
* Returns false if the GUI is inactive, or when the underlying backend's
* CLOSE event has been received. In that case close() will also be called
* (which will deactivate the GUI).
* CLOSE event has been received. In that case close() will also be called,
* which will deactivate the GUI (but not close the host window itself,
* unless the GUI was set up to manage ("own") the window).
*/
bool process(const sf::Event& event);
bool process(const event::Input& event);

/**
* Draw the entire GUI to the backend (i.e. SFML)
Expand Down Expand Up @@ -175,7 +185,8 @@ class GUI: public VBox

// ---- Data -----------------------------------------------------------------
std::error_code m_error;
sf::RenderWindow& m_window;
sf::RenderWindow& m_window; //!! Refine to focus on being a RenderTarget (the host windowing chores could have a separate API)
sfw::event::Source m_inputQueue; //!! ...especially as the event handling has now been abstracted away from the window!
bool m_own_window;
sfw::Theme::Cfg m_themeCfg;
sfw::Wallpaper m_wallpaper;
Expand Down
8 changes: 4 additions & 4 deletions include/sfw/InputWidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ template <class W> class InputWidget : public Widget
// applying (committing) the changes as the new content of the widget.
W* setCallback(std::function<void(W*)> callback)
{
on(Event::Update, [callback] (Event::Handler* w) { callback((W*)w); } );
on(event::Update, [callback] (event::Handler* w) { callback((W*)w); } );
return (W*)this;
}

W* setCallback(std::function<void()> callback)
{
on(Event::Update, callback);
on(event::Update, callback);
return (W*)this;
}

Expand All @@ -143,9 +143,9 @@ template <class W> class InputWidget : public Widget

virtual void onUpdated() //!! -> "EventHandler::invoke_callback(Updated)", and "updated" -> "notify(Updated)" or sg...
{
using namespace Event::internal;
using namespace event::internal;

auto callback = m_callbackMap[Event::Update];
auto callback = m_callbackMap[event::Update];
assert( std::holds_alternative<Callback_w>(callback) || std::holds_alternative<Callback_void>(callback) );
//!!?? This may no longer be true after getting something from the map that may not have
//!!?? been there! Seems to still work, so probably true, but make it explicit whether the
Expand Down
2 changes: 1 addition & 1 deletion include/sfw/Widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class Widget; template <std::derived_from<Widget> W = Widget> class WidgetPtr;
//!!C++: No, can't just make this a template like this (for *optional* CRTP support):
//!!struct WidgetProxy{};
//!!template <class W = WidgetProxy>
class Widget : public gfx::Drawable, public Event::Handler
class Widget : public gfx::Drawable, public event::Handler
{
public:
// Widget geometry
Expand Down
1 change: 1 addition & 0 deletions include/sfw/gfx/Color.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "SAL/gfx/Color.hpp"


//----------------------------------------------------------------------------
//!! Temporary kludge for the current unfinished C++ namespacing...
//----------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions include/sfw/gfx/Render.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "SAL/gfx/Render.hpp"


//----------------------------------------------------------------------------
//!! Temporary kludge for the current unfinished C++ namespacing...
//----------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit cd26755

Please sign in to comment.