Repository navigation
Events
Each Component has an EventListener, which manages incoming Events and the forwarding of those events to sub-components. An event listener also contains a StateHandler, which manages the state of the component using the incoming events. Event callbacks can be added to the event listener by using the void operator+=(). And state callbacks can be added to the state handler by calling the StateHandler& State(const StateSettings&) method, which also comes with some StateSettings.
struct StateSettings
{
State state; // State to link.
int limit = -1; // Limit on amount of sub-components with this state.
bool visible = true; // Must the sub-component be visible?
}An event listener manages incoming events, by calling the registered event callbacks, and by forwarding it to sub-components.
EventListener::operator+=(auto lambda)An event callback can be registered using the += operator, a callback must be a lambda with no return and a single argument, which should be a const reference to a class derived from Event. Whenever an event is properly handled, you can call the void Event::Handle() method to signal to any subsequent callbacks that the event has been handled. This will not stop further callbacks from being called, however. You can check if an event has been handled by calling bool Event::Handled().
listener += [](const MousePress& e) {
// handle event here
};NOTE: An event listener does not copy the callbacks when it is copy constructed or copy assigned! This is because the callbacks might capture references or this-pointers, which may become invalid. This means it is important to register event callbacks inside of the (copy/move) constructor of a component.
StateHandler& State(const StateSettings&);Registering state handlers is a bit more involved, since there are a bit more settings involved. When calling the State() function it will return a reference to a StateHandler that will be linked to the state given in the StateSettings. You can then call the operator+=() on that StateHandler to register callbacks. Those callbacks must return an integer and take in a const reference to a class derived from Event, a reference to a class derived from or equal to Component, and an int.
The final int parameter will be set to the available amount of slots for this state, which is linked to the given limit in the StateSettings. So for example, if a single component has returned 1 for the state Pressed, all subsequent calls to the callback will have that int parameter set to 0, because the limit has been reached. You can still choose to return any number to set the state, the limit is only used as an indication that can be used inside these callbacks.
An example of how to register a state handler for pressed:
auto& _stateHandler = listener.State({.state = Pressed, .limit = 1 });
_stateHandler += [](const MousePress& e, Component& c, int l) -> int { return l > 0 && c.Hitbox(e.pos); };
_stateHandler += [](const MouseRelease& e, Component& c, int) -> int { return 0; };The event base class can be extended to make custom events. The constructor expects a bool, which determines whether the event will be forwarded to the sub-components before or after the state handler. It also contains a pure virtual Forward method that must be overridden, which determines whether this event should be forwarded to the given component.
Event(bool beforeStateHandler);
virtual bool Forward(const Component&) const;Because of the way the event listener works, it means it is very easy to create custom events. But some standard events are given:
MouseMove event is fired when the cursor is moved and no mouse button was held down.
struct MouseMove
{
Vec2<float> pos; // Position of the cursor
};MouseDrag event is fired when the cursor is moved and any mouse button was held down.
struct MouseDrag
{
Vec2<float> source; // Press position of the cursor
Vec2<float> pos; // Position of the cursor
int buttons; // All buttons that are held down (bits)
int mod; // Mods (shift/ctrl etc.)
};MousePress event is fired when any mouse button is pressed.
struct MousePress
{
Vec2<float> pos; // Position of the cursor
int button; // Button that was clicked
int mod; // Mods (shift/ctrl etc.)
};MouseClick event is fired when the cursor was clicked and released in exactly the same location.
struct MouseClick
{
Vec2<float> pos; // Position of the cursor
int button; // Button that was clicked
int mod; // Mods (shift/ctrl etc.)
};MouseWheel event is fired whenever the mouse wheel is used.
struct MouseWheel
{
Vec2<float> pos; // Position of the cursor
int amount; // Scrolled amount
int mod; // Mods (shift/ctrl etc.)
};MouseRelease event is fired whenever any mouse button is released.
struct MouseRelease
{
Vec2<float> pos; // Position of the cursor
int button; // Button that was released
int mod; // Mods (shift/ctrl etc.)
};KeyPress event is fired whenever any key is pressed down.
struct KeyPress
{
int keycode; // Key code
int mod; // Mods (shift/ctrl etc.)
bool repeat; // true when event was fired because key was held down.
};KeyType event is fired whenever a character was typed.
struct KeyType
{
char key; // Character typed
int mod; // Mods (shift/ctrl etc.)
bool coded; // Is it a coded character?
};KeyRelease event is fired whenever a key was released.
struct KeyRelease
{
int keycode; // Key code
int mod; // Mods (shift/ctrl etc.)
}MouseExit event is fired whenever the cursor leaves the component's, or parent component's Hitbox, or forced on unfocus.
struct MouseExit {};MouseEvent event is fired whenever the cursor enters the component's Hitbox.
struct MouseEnter {};Focus event is fired whenever a component gains focus, usually when it has been pressed on.
struct Focus {};Unfocus event is fired whenever a component loses focus.
struct Unfocus {};There is a special Key class that helps with key combinations and contains an enum with some key code like Backspace, Tab etc. You can construct a key combo like this Key combo = Mods::Control | 'A';. The key class also contains an efficient const std::string& ToString() method, which will return a reference to a string that is only constructed on the first call, and cached after. There are also some equality operators between various events related to keys and the key class, so you can easily check if a KeyPress is a certain key combination.