GGUI/Public/UIWidget.h file

Widgets: node kinds with their own state and behaviour.

A widget is a struct. Its members are the per-node state; its methods are the hooks it wants. Hooks are detected at compile time: a missing one costs nothing, and one with the right name but the wrong signature fails to compile.

struct LogView
{
    static constexpr std::string_view Name = "LogView";
    uint64_t Top = 0;                                              // state
    void Woken(WidgetHost<LogView>& host);                         // a watched revision changed
    void Sized(WidgetHost<LogView>& host);                         // layout gave it a new size
    void Valued(WidgetHost<LogView>& host);                        // its value changed (value widgets)
    void Language(WidgetHost<LogView>& host);                      // the language or the text measurer changed
    void Laid(WidgetHost<LogView>& host);                          // layout finished (after AfterLayout)
    void Focus(WidgetHost<LogView>& host, bool bFocused, bool bFromKeys);
    bool Text(WidgetHost<LogView>& host, const TextInputEvent& text);   // typed, composed, or pasted
    bool Key(WidgetHost<LogView>& host, const InputEvent& input);  // true = handled
    bool Wheel(WidgetHost<LogView>& host, float notches);          // true = scrolled
    void Publish(const WidgetHost<LogView>& host, GGSnapshot& out) const;
    bool ScrollBar(const WidgetHost<LogView>& host, float& scrollY, float& contentHeight) const;
    void ScrollBarTo(WidgetHost<LogView>& host, float scrollY);    // its scroll bar was dragged
};

A widget also opts into services with constants: static constexpr bool EditsValue = true; (the value service, UIValues.h), TakesText (text input), ShowsFocus (shows focus however it was gained), and KeysFirst (its Key hook runs before commands and navigation).

Hooks get a WidgetHost<T>: the widget's node plus the part of the document a widget may use. It is deliberately not the UIContext. A widget only runs when an event reaches it, a revision it watches moves, or it asked to be woken, so an idle document stays idle.

A node becomes a widget where it is built (UI::Widget<T>):

if (auto box = ui.Widget<LogView>(where))
{
    box.Layout({ ... });
    box.OnPress([](LogView& view, WidgetHost<LogView>& host, const PointerEvent& event) { ... });
    box->Configure(box.Host(), lines, props);
}

The core knows no widget by name. It holds one WidgetDef per kind, generated from the struct, and calls through it.

Namespaces

namespace GGUI

Classes

struct GGUI::WidgetDef
The core's view of a widget kind: state size, construct/destruct, and one function pointer per hook the struct has. Generated by WidgetDefOf<T>; never written by hand.
class GGUI::WidgetHostBase
The document API available to every widget. WidgetHost<T> adds the typed state.
template<typename T>
class GGUI::WidgetHost
What a widget's hooks are given: its node, its typed state, and the document services it may use.