Skip to content

Events

Event Subscription Model

libwui implements a "flat subscription" model. Unlike traditional frameworks where events are passed down an inheritance chain, here the Window acts as a central dispatcher. Any object β€” be it a control, dialog, or business logic β€” can subscribe to the window's event stream.

Advantages

Decoupling from Hierarchy You don't need to inherit from wui::button to override on_click. You simply subscribe any method of any class to events of the desired type. This allows separating UI and logic (real MVVM/MVC without pain).

Targeted or Global Events - If the control parameter is passed β€” you receive events only for that control (clicks, focus). - If control == nullptr β€” you subscribe to system or internal window events (resize, theme change, hotkeys). This is the ideal place for adaptive layout logic.

Lifetime Management (Subscriber ID) The method returns a unique subscriber_id (string). This allows dynamic unsubscribing at any time without destroying the object. This is critical for "detachable" windows and dynamic interfaces.

Bitmask Event Types The event_type parameter is a bitmask. You can subscribe to mouse | keyboard | internal at once, reducing the number of callback registrations.

// Subscribe to keyboard events and system changes
std::string sub_id = window->subscribe([this](const wui::event& ev) {
    if (ev.type == wui::event_type::keyboard) {
        // Hotkey logic
    }
}, wui::event_type::keyboard | wui::event_type::system);

// When component is destroyed - just unsubscribe by ID
window->unsubscribe(sub_id);

Usage Example

// MainFrame subscribes to window resize
window->subscribe([this](const wui::event& ev) {
    if (ev.type == wui::event_type::internal && 
        ev.internal_event_.type == wui::internal_event_type::size_changed) {
        // Adaptive layout
        updateLayout(ev.internal_event_.x, ev.internal_event_.y);
    }
}, wui::event_type::internal);

// DialingDialog subscribes to cancel button
cancelButton = std::make_shared<wui::button>("Cancel", [this]() {
    Cancel();
});

Note: We abandoned the concept of virtual on_something handlers in favor of a unified window event bus. Now any component is just a subscriber. Want the sidebar to know about a key press in the text field? Just subscribe it. This turns a monolithic UI into a flexible constructor where components communicate via std::function without knowing anything about each other.

Basic API

The subscription model is used to handle events. The control or user code subscribes in the window to the events it needs and receives them. Events can be generated by the system or user code.

All events pass through the window. The following methods are used to work with them:

class window
{
public:
    ...
    std::string subscribe(std::function<void(const event&)> receive_callback, event_type event_types, std::shared_ptr<i_control> control = nullptr);
    void unsubscribe(std::string_view subscriber_id);

    void emit_event(int32_t x, int32_t y);
    ...
};

std::string subscribe(...)

Used to receive events from the window. The method returns a unique identifier of the subscriber which should be passed to unsubscribe() for unsubscribing.

  • receive_callback - function that receives events
  • event_types - bit mask indicating which events should be received
  • control - if this field is set, only mouse events occurring on this control will be received. and keyboard events if it has input focus. Otherwise, all events from the window will be received.

unsubscribe(std::string_view subscriber_id)

Used to unsubscribe from window events

  • subscriber_id is a unique identifier of the subscriber, given by the `subscribe method.

emit_event(int32_t x, int32_t y)

A method that generates an internal event. Used by custom code to generate its own events.

Event types

enum class event_type : uint32_t
{
    system = (1 << 0),
    mouse = (1 << 1),
    keyboard = (1 << 2),
    internal = (1 << 3),

    all = system | mouse | keyboard | internal
};

struct event
{
    event_type type;

    union
    {
        mouse_event mouse_event_;
        keyboard_event keyboard_event_;
        internal_event internal_event_;
        system_event system_event_;
    };
};

Depending on the type of event, the value from the event association is taken.

System events

enum class system_event_type
{
    device_change
};

struct system_event
{
    system_event_type type;
    int32_t x, y;
};

Mouse events

enum class mouse_event_type
{
    move,
    enter,
    leave,
    right_down,
    right_up,
    center_down,
    center_up,
    left_down,
    left_up,
    left_double,
    wheel
};

struct mouse_event
{
    mouse_event_type type;

    int32_t x, y;

    int32_t wheel_delta;
};

Keyboard events

enum class keyboard_event_type
{
    down, 
    up,
    key
};

struct keyboard_event
{
    keyboard_event_type type;

    uint8_t modifier; /// vk_capital or vk_shift or vk_alt or vk_insert
    char key[4];
    uint8_t key_size;
};

Internal events

enum class internal_event_type
{
    set_focus,
    remove_focus,
    execute_focused,

    size_changed,
    position_changed,

    user_emitted
};

struct internal_event
{
    internal_event_type type;
    int32_t x, y;
};