UI3
Playbit's new UI library is an immediate-mode styled API. The UI is composed of a tree of rectangles.
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);