playbit / docs

UI3

Playbit's new UI library is an immediate-mode styled API. The UI is composed of a tree of rectangles.


#include <playbit/ui3.h>

U3Tree

typedef struct U3Tree U3Tree;

U3Point

typedef union U3Point {
    struct {
        i64 x, y;
    };
    i64 e[2];
} U3Point;

U3Rectangle

typedef union U3Rectangle {
    struct {
        i64 x0, y0, x1, y1;
    };
    struct {
        U3Point p0, p1;
    };
    i64 e[4];
} U3Rectangle;

U3CornerRadius

typedef struct U3CornerRadius {
    f32 topLeft, topRight, bottomRight, bottomLeft;
} U3CornerRadius;

U3Padding

typedef struct U3Padding {
    f32 left, top, right, bottom;
} U3Padding;

U3BoxStyle

typedef struct U3BoxStyle {
    PBColor        fillColor;
    PBColor        strokeColor;
    f32            strokeWidth;
    U3CornerRadius cornerRadius;
} U3BoxStyle;

U3BoxState

typedef struct U3BoxState {
    bool pressed : 1;
    bool released : 1;
    bool hovered : 1;
    bool disabled : 1;
    bool clicked : 1;
} U3BoxState;

U3EventKind

typedef enum U3EventKind {
    // Called when a box is about to be removed from the tree
    U3Event_UNMOUNT,

    // Called when the box should respond to an input event (mouse, keyboard, etc.)
    U3Event_INPUT,

    // Called when a box should layout its children
    U3Event_LAYOUT,

    // Called to ask a box for its preferred sizes
    U3Event_ASK_WIDTH,
    U3Event_ASK_HEIGHT,

    // Called to ask a box for its cursor style
    // Set the event's cursor field if your box sets a cursor.
    U3Event_ASK_CURSOR,

    // Called to try to tab traverse to the box. Set U3Event.consumed if handled.
    U3Event_TAB_TRAVERSE,

    // Called when the box loses keyboard focus
    U3Event_LOSE_FOCUS,

    // Called when a box will be drawn
    U3Event_DRAW_BACKGROUND, // Before descendent boxes.
    U3Event_DRAW_FOREGROUND, // After descendent boxes.
} U3EventKind;

U3Event

typedef struct U3Event {
    // What type of event this is.
    U3EventKind kind;

    // Used by U3Event_INPUT.
    // All input events bubble up the tree.
    // Setting this field to true does *not* stop the bubbling.
    // You are expected to check and update this field as appropriate for your box.
    // e.g. a button uses the macros U3EventConsumeLMBPressed and U3EventWasLMBReleased.
    // The former checks consumed is false and sets it to tree;
    // the latter ignores the consumed field.
    // This way pressing the left mouse button only activates the button if it is the innermost box interested in such events,
    // but releasing the left mouse button is always able to deactivate the button.
    // Also used by U3Event_DRAW_BACKGROUND: set to true to skip drawing descendents.
    // Also used by U3Event_TAB_TRAVERSE: set to true to finish step.
    bool consumed;

    // Additional data specific to each type of event.
    union {
        // For U3Event_INPUT: the structure from the system containing more information about the input event.
        const PBEventAny* _Nullable data;

        // For U3Event_ASK_WIDTH and U3Event_ASK_HEIGHT.
        struct {
            // Output.
            i64 sizePx;

            // Input. If non-null: for U3Event_ASK_WIDTH, this is the available height;
            // for U3Event_ASK_HEIGHT, this is the available width.
            const i64* _Nullable otherSizePxIfKnown;
        } askSize;

        PBSysCursorStyle cursor; // Output.
    };
} U3Event;

An event that is sent to boxes' event handlers. See U3EventHandler.

U3AttributedString

typedef struct U3AttributedString U3AttributedString;

For U3Event_ASK_WIDTH and U3Event_ASK_HEIGHT.

U3AttributedStringArray

typedef PBArrayType(U3AttributedString* _Nonnull) U3AttributedStringArray;

For U3Event_ASK_WIDTH and U3Event_ASK_HEIGHT.

U3Key

typedef struct U3Key {
    i64 value;
} U3Key;

A key that uniquely identifies a box among its siblings.

U3PrimaryAxisAlignment

typedef enum U3PrimaryAxisAlignment {
    U3PrimaryAxisAlignment_START,
    U3PrimaryAxisAlignment_END,
    U3PrimaryAxisAlignment_CENTER,
    U3PrimaryAxisAlignment_SPACE_EVENLY,
    U3PrimaryAxisAlignment_SPACE_BETWEEN,
    U3PrimaryAxisAlignment_SPACE_AROUND,
} U3PrimaryAxisAlignment;

U3SecondaryAxisAlignment

typedef enum U3SecondaryAxisAlignment {
    U3SecondaryAxisAlignment_DEFAULT,
    U3SecondaryAxisAlignment_START,
    U3SecondaryAxisAlignment_END,
    U3SecondaryAxisAlignment_CENTER,
    U3SecondaryAxisAlignment_STRETCH,
} U3SecondaryAxisAlignment;

For U3FlexDirection_HORIZONTAL, the secondary axis is the vertical axis. For U3FlexDirection_VERTICAL, the secondary axis is the horizontal axis.

U3FlexDirection

typedef enum U3FlexDirection {
    U3FlexDirection_VERTICAL,
    U3FlexDirection_HORIZONTAL,
} U3FlexDirection;

U3BoxLayout

typedef struct U3BoxLayout {
    // Overrides the values returned from U3Event_ASK_WIDTH/HEIGHT.
    // Set a field to zero if you don't want a fixed size on that axis.
    PBSize fixedSizeDp;

    union {
        // For child boxes of a flex box parent:
        struct {
            U3SecondaryAxisAlignment secondaryAxisAlignment;

            // If true, then the box is treated as having no size on the primary axis.
            // Then, after all other layout is done, any remaining space in the parent is distributed equally to all boxes with this flag set.
            bool primaryAxisGrow;
        } flex;
    };
} U3BoxLayout;

Describes the information the parent box needs to layout this box.

U3EventHandler

typedef void (U3Box * _Nonnull, U3Event * _Nonnull) U3EventHandler;

For child boxes of a flex box parent:

U3BoxArray

typedef PBArrayType(U3Box* _Nonnull) U3BoxArray;

For child boxes of a flex box parent:

U3TreeAlloc

U3Tree* U3TreeAlloc(PBMem    mem,
                    PBWindow window);

Allocate a new UI tree.

U3TreeGetRoot

U3Box* U3TreeGetRoot(U3Tree* tree);

Get the root box from the tree.

U3TreeSetAdditionalScaleFactor

void U3TreeSetAdditionalScaleFactor(U3Tree* tree,
                                    f32     additionalScale);

Set the additional scale factor of the tree.

U3TreeUpdate

void U3TreeUpdate(U3Tree*           tree,
                  U3Box*            fromBox,
                  const PBEventAny* optionalEvent);

Start a tree update. If an PBEvent is passed, it will be forwarded to the relevant boxes via U3Event_INPUT. This can be called multiple times before U3TreeRender if needed.

U3TreeRender

void U3TreeRender(U3Tree* tree);

End a tree update. This removes any boxes from the tree that are no longer needed, performs layout, draws the window, and finally deallocates temporary allocations such as U3Fragments.

U3FindBoxAtPoint

U3Box* U3FindBoxAtPoint(U3Box*  box,
                        U3Point point);

Find the front-most box intersecting the given point within the specified box.

U3Contains

bool U3Contains(U3Box* maybeAncestor,
                U3Box* maybeDescendent,
                bool   allowEquality);

Check if one box is an ancestor of another box. If maybeAncestor=maybeDescendent, then allowEquality is returned.

U3ScaleI64

i64 U3ScaleI64(i64 x);

Convert a dp value to px, with correct rounding.

U3ScaleF32

i64 U3ScaleF32(f32 x);

U3GetTree

U3Tree* U3GetTree();

U3GetFocus

U3Box* U3GetFocus();

Get the currently focused box in the tree being updated.

U3GetPointerOver

U3Box* U3GetPointerOver();

Get the box that the main pointer is hovering over in the tree being updated.

U3RectangleContainsPoint

bool U3RectangleContainsPoint(U3Rectangle rect,
                              U3Point     p);

U3PointMake

U3Point U3PointMake(i64 x,
                    i64 y);

U3Hash

u64 U3Hash(const void* data,
           usize       dataBytes,
           u64         hash);

U3DrawStyle

void U3DrawStyle(const U3BoxStyle* style,
                 U3Rectangle       rect);

May be removed in a future version.

U3BoxIsHidden

bool U3BoxIsHidden(U3Box* box,
                   bool   testAncestors);

U3BoxGetBounds

U3Rectangle U3BoxGetBounds(U3Box* box);

Get the bounds of the box in U3Rectangle, which contains 64-bit integers. In pixels.

U3BoxGetBoundsAsPBRectangle

PBRectangle U3BoxGetBoundsAsPBRectangle(U3Box* box);

Get the bounds of the box in PBRectangle, which contains floating-point numbers. Suitable for passing to the PBDraw* family of functions.

U3BoxMove

void U3BoxMove(U3Box*             box,
               U3Rectangle        bounds,
               const U3Rectangle* clipRect,
               const U3Rectangle* reserved);

Only call if you are the parent of the box. Move a box and, optionally, set its clip rectangle (which is intersected with its ancestors' clip rectangles).

U3BoxGetData

void* U3BoxGetData(U3Box* box);

Only call if you implement the box. Get the implementor's data for the box. This is N bytes of memory for custom use, where N is passed to U3BoxMake when the box is first created. This cannot be resized. It is freed automatically when the box is removed.

U3BoxGetOnEvent

U3EventHandler* U3BoxGetOnEvent(U3Box* box);

Get the "onEvent" function the box uses, that was passed to U3BoxMake.

U3BoxSetOnEvent

void U3BoxSetOnEvent(U3Box*          box,
                     U3EventHandler* handler);

U3BoxGetParent

U3Box* U3BoxGetParent(U3Box* box);

Get the parent box, or NULL if the box is the root.

U3BoxGetChild

U3Box* U3BoxGetChild(U3Box* box,
                     u32    index);

Get the Nth child of the box, where 0<=N<U3BoxGetChildrenLen.

U3BoxGetChildrenLen

u32 U3BoxGetChildrenLen(U3Box* box);

Get the number of children of the box.

U3BoxAskSize

i64 U3BoxAskSize(U3Box*     box,
                 const i64* otherSizePxIfKnown,
                 i8         s);

Ask the box for its size. Returns px. This either sends U3Event_ASK_WIDTH or U3Event_ASK_HEIGHT to the box's event handler, or the box has a fixed sized in its U3BoxLayout, then that will be scaled and returned instead. The otherSizePxIfKnown parameter is used for boxes where their size on one axis depending on the space available on the other axis. For example, with wrapped LTR text, its height increases as the available width decreases.

U3BoxAskCursor

PBSysCursorStyle U3BoxAskCursor(U3Box* box);

Ask the box for its cursor style. Returns PBSysCursorStyle_DEFAULT if the box doesn't set a cursor style. This sends U3Event_ASK_CURSOR to the box's event handler,

U3BoxMake

U3Box* U3BoxMake(U3Box*             parentBox,
                 U3Key              keyInParent,
                 const U3BoxLayout* positionInParent,
                 U3EventHandler*    onEvent,
                 usize              dataBytes);

This either creates a new box or finds an existing child box of the specified parent. The latter occurs when the key matches an existing child, the former occurs when it does not. If a new box is created, its event handler is set to the provided value and its data structure is allocated with the given size. The layout parameters are always updated.

U3BoxDeleteChild

void U3BoxDeleteChild(U3Box* parent,
                      usize  index);

Immediately remove a box from the tree.

U3BoxSetHidden

void U3BoxSetHidden(U3Box* box,
                    bool   hidden);

Hide or show a box. A box is only shown if it and all its ancestors are shown.

U3BoxSetStyle

void U3BoxSetStyle(U3Box*            box,
                   const U3BoxStyle* style);

May be removed in a future version.

U3BoxGetStyle

const U3BoxStyle* U3BoxGetStyle(U3Box* box);

U3BoxGetLayout

U3BoxLayout U3BoxGetLayout(U3Box* box);

Get the layout parameters on the box.

U3BoxSetFocus

void U3BoxSetFocus(U3Box* box);

Give keyboard focus to the box. Only call if you implement the box. The box that previously had keyboard focus is sent U3Event_LOSE_FOCUS.

U3BoxGetClippedBounds

U3Rectangle U3BoxGetClippedBounds(U3Box* box);

Get the intersection of the box``s bounds with its and its ancestors clips, where they exist.

U3BoxProcessEvent

void U3BoxProcessEvent(U3Box*   box,
                       U3Event* event);

Updates the box's state from the event (passively)

U3BoxWasClicked

bool U3BoxWasClicked(U3Box* box);

Returns true if the box was clicked (and consumes the signal)

U3BoxSetDisabled

void U3BoxSetDisabled(U3Box* box,
                      bool   disabled);

Prevents the click signal from firing on the box (including passive signals)

U3BoxIsDisabled

bool U3BoxIsDisabled(U3Box* box);

Sets the box disabled signal

U3BoxGetState

U3BoxState U3BoxGetState(U3Box* box);

Returns the box state (updated by U3BoxProcessEvent)

U3BoxSetLayout

void U3BoxSetLayout(U3Box*       box,
                    U3BoxLayout* layout);

U3ButtonFn

typedef void (void * _Nullable) U3ButtonFn;

U3ButtonMake

U3Box* U3ButtonMake(U3Box*              parentBox,
                    U3Key               keyInParent,
                    const U3BoxLayout*  positionInParent,
                    U3AttributedString* label,
                    U3ButtonFn*         fn,
                    void*               context);

U3ButtonWasClicked

bool U3ButtonWasClicked(U3Box* button);

U3ButtonSetDisabled

void U3ButtonSetDisabled(U3Box* button,
                         bool   disabled);

U3CheckMake

U3Box* U3CheckMake(U3Box*              parentBox,
                   U3Key               keyInParent,
                   const U3BoxLayout*  positionInParent,
                   U3AttributedString* label,
                   bool                isChecked);

U3CheckWasClicked

bool U3CheckWasClicked(U3Box* check);

U3ColorPickerMake

U3Box* U3ColorPickerMake(U3Box*             parentBox,
                         U3Key              keyInParent,
                         const U3BoxLayout* positionInParent);

U3ColorPickerSetColor

void U3ColorPickerSetColor(U3Box*  picker,
                           PBColor color);

U3ColorPickerGetColor

PBColor U3ColorPickerGetColor(U3Box* picker);

U3ColorPickerWasChangedByUser

bool U3ColorPickerWasChangedByUser(U3Box* picker);

U3TextMake

U3Box* U3TextMake(U3Box*              parentBox,
                  U3Key               keyInParent,
                  const U3BoxLayout*  positionInParent,
                  U3AttributedString* content,
                  u32                 flags);

U3TextSetContent

void U3TextSetContent(U3Box*              box,
                      U3AttributedString* content);

U3TextSetOverrideColor

void U3TextSetOverrideColor(U3Box*   box,
                            PBColor* color);

U3TextHitTest

u64 U3TextHitTest(U3Box*  textBox,
                  U3Point point);

The point is in window coordinates. The result is the byte offset from the start of the text string.

U3BodyMake

U3Box* U3BodyMake(U3Box*             parentBox,
                  U3Key              keyInParent,
                  const U3BoxLayout* positionInParent,
                  bool               hasCaret);

Create a new body box, or match an existing box based on parentBox/keyInParent.

U3BodyMakeTextSelectable

void U3BodyMakeTextSelectable(U3Box* bodyBox,
                              U3Box* textBox,
                              i64    logicalIndex);

Register a descendent text box within the body box as containing selectable text. The logical indices determine the order of text selection.

U3BodySetScrollBox

void U3BodySetScrollBox(U3Box* bodyBox,
                        U3Box* scrollBox);

Set the scroll box the body box should use when it needs to scroll the caret into view.

U3BodyGetSelection

void U3BodyGetSelection(U3Box* box,
                        i64*   caretPartLogicalIndex,
                        i64*   anchorPartLogicalIndex,
                        u32*   caretOffsetInPart,
                        u32*   anchorOffsetInPart);

Get the current range of selected text.

U3BodySetSelection

void U3BodySetSelection(U3Box* box,
                        i64    caretPartLogicalIndex,
                        i64    anchorPartLogicalIndex,
                        u32    caretOffsetInPart,
                        u32    anchorOffsetInPart,
                        bool   scrollCaretIntoView);

Set the range of selected text, and optionally scroll the caret into view (see U3BodySetScrollBox).

U3BodyKeyDown

bool U3BodyKeyDown(U3Box*                 box,
                   const PBKeyboardEvent* ev);

Simulate a key down event. Returns true if handled.

U3BodyUserSelectingText

bool U3BodyUserSelectingText(U3Box* box);

Test if the user is currently dragging a text selection.

U3BodyExpect

U3Box* U3BodyExpect(U3Box* box);

Panic if the box is not a Body.

U3InputFieldMake

U3Box* U3InputFieldMake(U3Box*             parentBox,
                        U3Key              keyInParent,
                        const U3BoxLayout* positionInParent);

U3InputFieldMakeMultiline

U3Box* U3InputFieldMakeMultiline(U3Box*             parentBox,
                                 U3Key              keyInParent,
                                 const U3BoxLayout* positionInParent);

U3InputFieldMakeMultiline adds a multi-line input field to parentBox. Text wraps, the field scrolls vertically, and Enter inserts a newline (WasEnterPressed never fires). Default height is three lines; override with U3BoxLayout.fixedSizeDp.height.

U3InputFieldMakeWithMPObject

U3Box* U3InputFieldMakeWithMPObject(U3Box*             parentBox,
                                    U3Key              keyInParent,
                                    const U3BoxLayout* positionInParent,
                                    PBMPObject*        object);

U3InputFieldWasEdited

bool U3InputFieldWasEdited(U3Box* box);

U3InputFieldBufferDelete

void U3InputFieldBufferDelete(U3Box* box,
                              u32    at,
                              u32    len);

U3InputFieldBufferInsert

void U3InputFieldBufferInsert(U3Box*     box,
                              u32        at,
                              PBStrSlice text);

U3InputFieldBufferSetColor

void U3InputFieldBufferSetColor(U3Box*  box,
                                u32     at,
                                u32     len,
                                PBColor color);

U3InputFieldBufferSetFontFamily

void U3InputFieldBufferSetFontFamily(U3Box* box,
                                     u32    at,
                                     u32    len,
                                     u32    fontFamily);

U3InputFieldBufferSetWeight

void U3InputFieldBufferSetWeight(U3Box* box,
                                 u32    at,
                                 u32    len,
                                 f32    weight);

U3InputFieldBufferGetColor

bool U3InputFieldBufferGetColor(U3Box*   box,
                                u32      at,
                                u32      len,
                                PBColor* color);

U3InputFieldBufferGetFontFamily

bool U3InputFieldBufferGetFontFamily(U3Box* box,
                                     u32    at,
                                     u32    len,
                                     u32*   fontFamily);

U3InputFieldBufferGetWeight

bool U3InputFieldBufferGetWeight(U3Box* box,
                                 u32    at,
                                 u32    len,
                                 f32*   weight);

U3InputFieldOnChangeFn

typedef bool (U3Box * _Nonnull, PBStrSlice, void * _Nullable) U3InputFieldOnChangeFn;

U3InputFieldOnChange

void U3InputFieldOnChange(U3Box*                  box,
                          U3InputFieldOnChangeFn* fn,
                          void*                   context);

U3InputFieldGetSelection

void U3InputFieldGetSelection(U3Box* box,
                              usize* caret,
                              usize* anchor);

U3InputFieldSetSelection

void U3InputFieldSetSelection(U3Box* box,
                              usize  caret,
                              usize  anchor,
                              bool   scrollCaretIntoView);

U3InputFieldSetFocus

void U3InputFieldSetFocus(U3Box* box);

U3InputFieldSetText

void U3InputFieldSetText(U3Box*     box,
                         PBStrSlice appText);

U3InputFieldGetTextCopy

PBStr U3InputFieldGetTextCopy(U3Box* box,
                              PBMem  allocator);

U3InputFieldGetTextUnsafe

PBStrSlice U3InputFieldGetTextUnsafe(U3Box* box);

U3InputFieldSetPlaceholderText

void U3InputFieldSetPlaceholderText(U3Box*     box,
                                    PBStrSlice placeholderText);

U3InputFieldWasEnterPressed

bool U3InputFieldWasEnterPressed(U3Box* box);

U3InputFieldWasTabPressed

bool U3InputFieldWasTabPressed(U3Box* box);

U3InputFieldWasEscapePressed

bool U3InputFieldWasEscapePressed(U3Box* box);

U3UtilMake

U3Box* U3UtilMake(U3Box*             parentBox,
                  U3Key              keyInParent,
                  const U3BoxLayout* positionInParent,
                  PBColor            fillColor,
                  f32                cornerRadiusDp,
                  PBColor            strokeColor,
                  f32                strokeWidthDp,
                  bool               clip);

U3UtilExpect

U3Box* U3UtilExpect(U3Box* box);

Panic if the box is not a Util.

U3ScrollMake

U3Box* U3ScrollMake(U3Box*             parentBox,
                    U3Key              keyInParent,
                    const U3BoxLayout* positionInParent,
                    bool               scrollHorizontally,
                    bool               scrollVertically,
                    bool               alwaysHideBars);

U3ScrollGetState

void U3ScrollGetState(U3Box* scroll,
                      f64*   outPosX,
                      f64*   outPosY,
                      f64*   outMaxPosX,
                      f64*   outMaxPosY);

U3ScrollSetState

void U3ScrollSetState(U3Box* scroll,
                      f64    inPosX,
                      f64    inPosY);

U3ScrollLayoutNow

void U3ScrollLayoutNow(U3Box* scroll);

U3ScrollExpect

U3Box* U3ScrollExpect(U3Box* box);

Panic if the box is not a Scroll.

U3FlexParams

typedef struct U3FlexParams {
    U3FlexDirection direction;

    // The spacing between each pair of child boxes.
    f32 childSpacingDp;

    // The spacing between the edges of the flex box and its children.
    U3Padding paddingDp;

    U3PrimaryAxisAlignment primaryAxisAlignment;

    // default for the children, unless otherwise specified in U3BoxLayout.secondaryAxisAlignment
    U3SecondaryAxisAlignment secondaryAxisAlignment;
} U3FlexParams;

Parameters determining how a flex box lays out its children.

U3FlexMake

U3Box* U3FlexMake(U3Box*             parentBox,
                  U3Key              keyInParent,
                  const U3BoxLayout* positionInParent,
                  U3FlexParams       params);

U3FlexExpect

U3Box* U3FlexExpect(U3Box* box);

Panic if the box is not a Flex.

U3AttributedStringMake

U3AttributedString* U3AttributedStringMake(PBStrSlice str,
                                           u32        fontFamily,
                                           f32        size,
                                           u32        weight,
                                           PBColor    color);

Create an immutable string with styling attribuets. This is wrapper for U3AttributedStringMake2, which allows passing more attribute types. The returned pointer is valid until the next call to U3TreeRender.

U3AttributedStringMake2

U3AttributedString* U3AttributedStringMake2(PBStrSlice          str,
                                            PBSysTextAttribute* attributes,
                                            usize               attributesLen);

Create an immutable string with styling attributes. This is the fully-featured version of U3AttributedStringMake, which only allows the most common attributes to be passed. The returned pointer is valid until the next call to U3TreeRender.

U3AttributedStringConcatenate

U3AttributedString* U3AttributedStringConcatenate(U3AttributedString* first, ...);

Concatenate together attributed strings. Terminate the varargs with NULL.

U3AttributedStringConcatenateArray

U3AttributedString* U3AttributedStringConcatenateArray(U3AttributedString* array,
                                                       usize               arrayLen);

Concatenate together attributed strings with an array.

U3AttributedStringGetHash

u64 U3AttributedStringGetHash(U3AttributedString* string);

Get a hash of the contents of the attributed string, included both the string and its attributes.

PBMPShareAndJoinMake

U3Box* PBMPShareAndJoinMake(U3Box*             parentBox,
                            U3Key              keyInParent,
                            const U3BoxLayout* positionInParent,
                            PBMPDoc*           _doc,
                            bool*              docChanged);