637 lines
15 KiB
C
637 lines
15 KiB
C
////////////////////////////////////////////////////////////
|
|
//~ Key types
|
|
|
|
#define UI_NilKey ((UI_Key) { 0 })
|
|
#define UI_RootKey ((UI_Key) { 0xaaaaaaaaaaaaaaaaUll })
|
|
|
|
Struct(UI_Key)
|
|
{
|
|
u64 v;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Size types
|
|
|
|
Enum(UI_SizeKind)
|
|
{
|
|
UI_SizeKind_Pixel, // Exact size
|
|
UI_SizeKind_Grow, // Size as percent of parent size
|
|
UI_SizeKind_Shrink, // Size of children + padding in pixels
|
|
};
|
|
|
|
Struct(UI_Size)
|
|
{
|
|
UI_SizeKind kind;
|
|
f32 v;
|
|
f32 strictness;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Rounding types
|
|
|
|
Enum(UI_RoundKind)
|
|
{
|
|
UI_RoundKind_Pixel, // Exact radius
|
|
UI_RoundKind_Grow, // Radius as percent of size
|
|
};
|
|
|
|
Struct(UI_Round)
|
|
{
|
|
UI_RoundKind kind;
|
|
f32 v;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Region types
|
|
|
|
Enum(UI_Region)
|
|
{
|
|
UI_Region_TopLeft,
|
|
UI_Region_Top,
|
|
UI_Region_TopRight,
|
|
|
|
UI_Region_Left,
|
|
UI_Region_Center,
|
|
UI_Region_Right,
|
|
|
|
UI_Region_BottomLeft,
|
|
UI_Region_Bottom,
|
|
UI_Region_BottomRight,
|
|
};
|
|
|
|
Enum(UI_AxisRegion)
|
|
{
|
|
UI_AxisRegion_Start,
|
|
UI_AxisRegion_Center,
|
|
UI_AxisRegion_End,
|
|
};
|
|
|
|
Union(UI_RegionPair)
|
|
{
|
|
struct { UI_AxisRegion x, y; };
|
|
UI_AxisRegion v[2];
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Checkpoint types
|
|
|
|
Struct(UI_Checkpoint)
|
|
{
|
|
u64 v;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Box flags
|
|
|
|
Enum(UI_BoxFlag)
|
|
{
|
|
UI_BoxFlag_None = 0,
|
|
UI_BoxFlag_DrawText = (1 << 0),
|
|
UI_BoxFlag_CaptureMouse = (1 << 1),
|
|
UI_BoxFlag_CaptureThroughChildren = (1 << 2),
|
|
UI_BoxFlag_DontTruncateText = (1 << 3),
|
|
UI_BoxFlag_Floating = (1 << 4),
|
|
UI_BoxFlag_DontClampFloatingX = (1 << 5),
|
|
UI_BoxFlag_DontClampFloatingY = (1 << 6),
|
|
UI_BoxFlag_Scissor = (1 << 7),
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Debug breakpoint flags
|
|
|
|
Enum(UI_DebugBreakFlag)
|
|
{
|
|
UI_DebugBreakFlag_None = 0,
|
|
UI_DebugBreakFlag_CheckCursorHover = (1 << 0),
|
|
UI_DebugBreakFlag_BuildFeedback = (1 << 1),
|
|
UI_DebugBreakFlag_PrepLayout = (1 << 2),
|
|
UI_DebugBreakFlag_IndependentSolve = (1 << 3),
|
|
UI_DebugBreakFlag_UpwardsDependentSolveLayoutAxis = (1 << 4),
|
|
UI_DebugBreakFlag_DownwardsDependentSolve = (1 << 5),
|
|
UI_DebugBreakFlag_UpwardsDependentSolveNonLayoutAxis = (1 << 6),
|
|
UI_DebugBreakFlag_SolveViolations = (1 << 7),
|
|
UI_DebugBreakFlag_FinalSolve = (1 << 8),
|
|
UI_DebugBreakFlag_BuildGpuData = (1 << 9),
|
|
|
|
UI_DebugBreakFlag_All = 0xFFFFFFFF
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Style types
|
|
|
|
#define UI_StyleKindsXList(X) \
|
|
X(OmitFlags, UI_BoxFlag) \
|
|
X(OrFlags, UI_BoxFlag) \
|
|
X(Flags, UI_BoxFlag) \
|
|
X(DebugBreakFlags, UI_DebugBreakFlag) \
|
|
X(Parent, UI_Key) \
|
|
X(Tag, u64) \
|
|
X(ChildLayoutAxis, Axis) \
|
|
X(ChildAlignment, UI_Region) \
|
|
X(Width, UI_Size) \
|
|
X(Height, UI_Size) \
|
|
X(Scale, Vec2) \
|
|
X(BackgroundColor, Vec4) \
|
|
X(BorderColor, Vec4) \
|
|
X(DebugColor, Vec4) \
|
|
X(InvisibleDebugColor, Vec4) \
|
|
X(Tint, Vec4) \
|
|
X(Opacity, f32) \
|
|
X(BorderSize, f32) \
|
|
X(Anchor, UI_Region) \
|
|
X(FloatingPos, Vec2) \
|
|
X(Rounding, UI_Round) \
|
|
X(Font, GC_FontKey) \
|
|
X(FontSize, f32) \
|
|
X(TextColor, Vec4) \
|
|
X(Text, String) \
|
|
X(Icon, UI_Icon) \
|
|
X(SpriteSheet, SPR_SheetKey) \
|
|
X(SpriteSpan, SPR_SpanKey) \
|
|
X(SpriteSeq, i64) \
|
|
X(BackgroundTexture, G_TextureRef) \
|
|
X(BackgroundTextureSliceUv, Rng2) \
|
|
X(Misc, f64) \
|
|
X(ZagColor, Vec4) \
|
|
X(ZagPeriod, f32) \
|
|
X(ZagAmplitude, f32) \
|
|
X(ZagThickness, f32) \
|
|
X(ZagRoundness, f32) \
|
|
/* --------------------------------------------- */ \
|
|
/* --------------- Virtual styles -------------- */ \
|
|
/* --------------------------------------------- */ \
|
|
X(BeginVirtualStyles_, i8) \
|
|
/* --------------------------------------------- */ \
|
|
X(AxisSize, UI_Size) \
|
|
/* ------------------------------------------------- */
|
|
|
|
Enum(UI_StyleKind)
|
|
{
|
|
#define X(name, type) CAT(UI_StyleKind_,name),
|
|
UI_StyleKind_None,
|
|
UI_StyleKindsXList(X)
|
|
UI_StyleKind_COUNT,
|
|
#undef X
|
|
};
|
|
|
|
Struct(UI_Style)
|
|
{
|
|
UI_StyleKind kind;
|
|
// Union of all style fields
|
|
union
|
|
{
|
|
#define X(name, type) type name;
|
|
UI_StyleKindsXList(X)
|
|
#undef X
|
|
};
|
|
};
|
|
|
|
Struct(UI_StyleDesc)
|
|
{
|
|
Axis axis;
|
|
UI_Style style;
|
|
|
|
// Push
|
|
b32 pop_when_used;
|
|
b32 override;
|
|
|
|
// Pop
|
|
b32 force_pop;
|
|
b32 use;
|
|
};
|
|
|
|
Struct(UI_StyleNode)
|
|
{
|
|
UI_StyleNode *next;
|
|
UI_Style style;
|
|
UI_Checkpoint checkpoint;
|
|
b32 pop_when_used;
|
|
b32 override;
|
|
};
|
|
|
|
Struct(UI_Stack)
|
|
{
|
|
UI_StyleNode *style_tops[UI_StyleKind_COUNT];
|
|
UI_Checkpoint top_checkpoint;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Input types
|
|
|
|
Struct(UI_ButtonState)
|
|
{
|
|
b32 held; // Persistent
|
|
i32 downs; // Mouse button down events over box causing activation
|
|
i32 ups; // Mouse button up events while box is active
|
|
i32 presses; // Mouse button events while box is active and hovered
|
|
};
|
|
|
|
Struct(UI_InputState)
|
|
{
|
|
UI_ButtonState buttons[Button_COUNT];
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Feedback types
|
|
|
|
Struct(UI_Feedback)
|
|
{
|
|
Rng2 screen_rect;
|
|
Vec2 screen_anchor;
|
|
|
|
b32 active_absolute;
|
|
b32 hot_absolute;
|
|
b32 hovered_absolute;
|
|
b32 exists_absolute;
|
|
|
|
f32 active_smooth;
|
|
f32 hot_smooth;
|
|
f32 hovered_smooth;
|
|
f32 exists_smooth;
|
|
|
|
f64 misc_smooth;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Command types
|
|
|
|
Enum(UI_CmdKind)
|
|
{
|
|
UI_CmdKind_None,
|
|
UI_CmdKind_BuildBox,
|
|
UI_CmdKind_SetRawTexture,
|
|
};
|
|
|
|
Struct(UI_BoxDesc)
|
|
{
|
|
UI_BoxFlag flags;
|
|
UI_DebugBreakFlag debug_break_flags;
|
|
|
|
UI_Key key;
|
|
UI_Key parent;
|
|
|
|
b32 is_transient;
|
|
|
|
UI_Size pref_semantic_dims[Axis_COUNTXY];
|
|
UI_Round rounding;
|
|
Vec4 background_color;
|
|
Vec4 border_color;
|
|
Vec4 debug_color;
|
|
Vec4 invisible_debug_color;
|
|
Vec4 tint;
|
|
f32 opacity;
|
|
f32 border_size;
|
|
Vec2 scale;
|
|
Vec4 text_color;
|
|
String text;
|
|
UI_Icon icon;
|
|
GC_FontKey font;
|
|
f32 font_size;
|
|
Axis child_layout_axis;
|
|
UI_Region child_alignment;
|
|
UI_Region anchor;
|
|
Vec2 floating_pos;
|
|
SPR_SheetKey sprite_sheet;
|
|
SPR_SpanKey sprite_span;
|
|
i64 sprite_seq;
|
|
f64 misc;
|
|
Vec4 zag_color;
|
|
f32 zag_period;
|
|
f32 zag_amplitude;
|
|
f32 zag_thickness;
|
|
f32 zag_roundness;
|
|
};
|
|
|
|
Struct(UI_Cmd)
|
|
{
|
|
UI_CmdKind kind;
|
|
union
|
|
{
|
|
UI_BoxDesc box;
|
|
struct
|
|
{
|
|
UI_Key key;
|
|
G_TextureRef tex;
|
|
Rng2 slice_uv;
|
|
} set_raw_texture;
|
|
};
|
|
};
|
|
|
|
Struct(UI_CmdNode)
|
|
{
|
|
UI_CmdNode *next;
|
|
UI_Cmd cmd;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Box types
|
|
|
|
Struct(UI_Box)
|
|
{
|
|
UI_Key key;
|
|
|
|
//- Persistent data
|
|
UI_Box *next_in_bin;
|
|
UI_Box *prev_in_bin;
|
|
i64 last_build_tick;
|
|
|
|
i64 old_gen;
|
|
i64 gen;
|
|
|
|
//- Tree links
|
|
UI_Box *parent;
|
|
UI_Box *first;
|
|
UI_Box *last;
|
|
UI_Box *next;
|
|
UI_Box *prev;
|
|
u64 count;
|
|
|
|
//- Cmd data
|
|
UI_BoxDesc desc;
|
|
G_TextureRef raw_texture;
|
|
Rng2 raw_texture_slice_uv;
|
|
GC_Run glyph_run;
|
|
SPR_Sprite sprite;
|
|
|
|
//- Pre-layout data
|
|
u64 pre_index;
|
|
u64 post_index;
|
|
|
|
//- Layout data
|
|
f32 solved_opacity;
|
|
Vec2 solved_scale;
|
|
Rng2 solved_scissor;
|
|
Vec2 final_children_size_accum;
|
|
Vec2 solved_dims;
|
|
f32 cursor;
|
|
|
|
//- Layout results
|
|
Rng2 screen_rect;
|
|
Vec2 screen_anchor;
|
|
f32 rounding_tl;
|
|
f32 rounding_tr;
|
|
f32 rounding_br;
|
|
f32 rounding_bl;
|
|
|
|
//- Feedback
|
|
b32 mouse_hovered;
|
|
b32 mouse_captured;
|
|
UI_Feedback feedback;
|
|
UI_Feedback drag_feedback;
|
|
};
|
|
|
|
Struct(UI_BoxBin)
|
|
{
|
|
UI_Box *first;
|
|
UI_Box *last;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Iterator types
|
|
|
|
Struct(UI_BoxIterDfsNode)
|
|
{
|
|
UI_BoxIterDfsNode *next;
|
|
UI_Box *box;
|
|
b32 visited;
|
|
};
|
|
|
|
Struct(UI_BoxIter)
|
|
{
|
|
UI_BoxIterDfsNode *first;
|
|
UI_BoxIterDfsNode *first_free;
|
|
UI_BoxIterDfsNode *last_free;
|
|
};
|
|
|
|
Struct(UI_BoxIterResult)
|
|
{
|
|
UI_Box *box;
|
|
b32 pre;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ State types
|
|
|
|
Enum(UI_FrameFlag)
|
|
{
|
|
UI_FrameFlag_None = 0,
|
|
UI_FrameFlag_Debug = (1 << 0),
|
|
};
|
|
|
|
Struct(UI_Frame)
|
|
{
|
|
Arena *arena;
|
|
Arena *rects_arena;
|
|
|
|
WND_Frame window_frame;
|
|
|
|
// Time
|
|
i64 tick;
|
|
i64 time_ns;
|
|
i64 dt_ns;
|
|
f32 dt;
|
|
|
|
// Input
|
|
Vec2 cursor_pos;
|
|
Vec2 drag_cursor_pos;
|
|
UI_InputState input;
|
|
UI_InputState drag_input;
|
|
|
|
UI_Key top_hovered_box;
|
|
UI_Key top_hot_box;
|
|
UI_Key top_active_box;
|
|
|
|
// Cmds
|
|
UI_FrameFlag frame_flags;
|
|
UI_CmdNode *first_cmd_node;
|
|
UI_CmdNode *last_cmd_node;
|
|
u64 cmds_count;
|
|
|
|
// Style stack
|
|
UI_Stack *top_stack;
|
|
UI_StyleNode *first_free_style_node;
|
|
|
|
// Layout
|
|
UI_Box **boxes_pre;
|
|
UI_Box **boxes_post;
|
|
};
|
|
|
|
Struct(UI_Ctx)
|
|
{
|
|
u64 boxes_count;
|
|
Arena *box_arena;
|
|
UI_Box *root_box;
|
|
UI_BoxBin box_bins[Kibi(256)];
|
|
UI_Box *first_free_box;
|
|
|
|
RandState rand;
|
|
|
|
G_ArenaHandle gpu_frame_arena;
|
|
G_CommandListHandle cl;
|
|
|
|
i64 cur_frame_tick;
|
|
UI_Frame frames[2];
|
|
};
|
|
|
|
extern UI_Ctx UI;
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Key helpers
|
|
|
|
b32 UI_MatchKey(UI_Key a, UI_Key b);
|
|
b32 UI_IsKeyNil(UI_Key key);
|
|
UI_Key UI_KeyFromString(String str);
|
|
UI_Key UI_KeyF_(String fmt, ...);
|
|
#define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
|
|
UI_Key UI_RandKey(void);
|
|
UI_Box *UI_BoxFromKey(UI_Key key);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Region helpers
|
|
|
|
UI_Region UI_RegionFromPair(UI_RegionPair pair);
|
|
UI_RegionPair UI_PairFromRegion(UI_Region region);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Iteration helpers
|
|
|
|
UI_BoxIterResult UI_FirstBox(Arena *arena, UI_BoxIter *iter, UI_Key start_key);
|
|
UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ String helpers
|
|
|
|
String UI_StringF_(String fmt, ...);
|
|
#define UI_StringF(fmt_cstr, ...) UI_StringF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Style stack helpers
|
|
|
|
void UI_PushDefaults(void);
|
|
|
|
void UI_PushStyle(UI_StyleDesc desc);
|
|
UI_Style UI_PopStyle(UI_StyleDesc desc);
|
|
|
|
#define UI_STYLEDESC(name, ...) (UI_StyleDesc) { .style.kind = CAT(UI_StyleKind_,name), __VA_ARGS__ }
|
|
|
|
#define UI_SetNext(name, ...) UI_PushStyle(UI_STYLEDESC(name, .pop_when_used = 1, .style.name = __VA_ARGS__))
|
|
#define UI_Push(name, ...) UI_PushStyle(UI_STYLEDESC(name, .style.name = __VA_ARGS__))
|
|
#define UI_ForceNext(name, ...) UI_PushStyle(UI_STYLEDESC(name, .pop_when_used = 1, .override = 1, .style.name = __VA_ARGS__))
|
|
#define UI_ForcePush(name, ...) UI_PushStyle(UI_STYLEDESC(name, .override = 1, .style.name = __VA_ARGS__))
|
|
#define UI_Pop(name, ...) UI_PopStyle(UI_STYLEDESC(name, .force_pop = 1, __VA_ARGS__)).name
|
|
#define UI_PeekTop(name, ...) UI_PopStyle(UI_STYLEDESC(name, __VA_ARGS__)).name
|
|
#define UI_Top(name, ...) UI_PopStyle(UI_STYLEDESC(name, .use = 1, __VA_ARGS__)).name
|
|
|
|
#define UI_SetDF(name, ...) DeferFor(UI_Push(name, __VA_ARGS__), UI_Pop(name))
|
|
|
|
#define UI_PushCopy(name, src, ...) do { \
|
|
UI_StyleDesc _new = src; \
|
|
_new.style.kind = CAT(UI_StyleKind_,name); \
|
|
_new.style.name = __VA_ARGS__; \
|
|
UI_PushStyle(_new); \
|
|
} while (0)
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Checkpoint helpers
|
|
|
|
UI_Checkpoint UI_PushCp(UI_Key parent);
|
|
void UI_PopCp(UI_Checkpoint pop_to);
|
|
|
|
UI_Checkpoint UI_TopCp(void);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Size helpers
|
|
|
|
#define UI_SIZE(_kind, _v, _s) (UI_Size) { .kind = (_kind), .v = (_v), .strictness = (_s) }
|
|
|
|
#define UI_Px(_v, _s) UI_SIZE(UI_SizeKind_Pixel, (_v), (_s))
|
|
#define UI_Shrink(_v, _s) UI_SIZE(UI_SizeKind_Shrink, (_v), (_s))
|
|
#define UI_Grow(_v, _s) UI_SIZE(UI_SizeKind_Grow, (_v), (_s))
|
|
#define UI_Fnt(_v, _s) UI_SIZE(UI_SizeKind_Pixel, (f32)UI_PeekTop(FontSize) * (_v), (_s))
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Rounding helpers
|
|
|
|
#define UI_ROUND(_kind, _v) (UI_Round) { .kind = (_kind), .v = (_v)}
|
|
|
|
#define UI_Rpx(_v) UI_ROUND(UI_RoundKind_Pixel, (_v))
|
|
#define UI_Rgrow(_v) UI_ROUND(UI_RoundKind_Grow, (_v))
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Command helpers
|
|
|
|
UI_Key UI_BuildBoxEx(UI_Key semantic_key);
|
|
#define UI_BuildBox() UI_BuildBoxEx(UI_NilKey)
|
|
|
|
void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv);
|
|
|
|
#if IsRtcEnabled
|
|
#define UI_DebugBreak(box, target_flags) do { if (box->desc.debug_break_flags & target_flags) { DEBUGBREAK; } } while (0)
|
|
#else
|
|
#define UI_DebugBreak(...)
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Begin frame
|
|
|
|
UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Frame helpers
|
|
|
|
UI_Frame *UI_CurrentFrame(void);
|
|
UI_Frame *UI_PrevFrame(void);
|
|
Arena *UI_FrameArena(void);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Feedback
|
|
|
|
//- Passive
|
|
|
|
b32 UI_IsButtonPassive(Button button);
|
|
|
|
//- Input
|
|
|
|
b32 UI_Held(UI_Key key, Button button);
|
|
i32 UI_Downs(UI_Key key, Button button);
|
|
i32 UI_Ups(UI_Key key, Button button);
|
|
i32 UI_Presses(UI_Key key, Button button);
|
|
|
|
//- Interaction
|
|
|
|
f32 UI_Active(UI_Key key);
|
|
b32 UI_ActiveAbsolute(UI_Key key);
|
|
|
|
f32 UI_Hot(UI_Key key);
|
|
b32 UI_HotAbsolute(UI_Key key);
|
|
|
|
f32 UI_Hovered(UI_Key key);
|
|
f32 UI_HoveredAbsolute(UI_Key key);
|
|
|
|
f32 UI_Exists(UI_Key key);
|
|
b32 UI_ExistsAbsolute(UI_Key);
|
|
|
|
f64 UI_Misc(UI_Key key);
|
|
f64 UI_DragMisc(UI_Key key);
|
|
|
|
//- Layout
|
|
|
|
Vec2 UI_Anchor(UI_Key key);
|
|
Vec2 UI_DragAnchor(UI_Key key);
|
|
|
|
Rng2 UI_Rect(UI_Key key);
|
|
Rng2 UI_DragRect(UI_Key key);
|
|
|
|
Vec2 UI_CursorPos(void);
|
|
Vec2 UI_DragCursorPos(void);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Layout helpers
|
|
|
|
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale);
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ End frame
|
|
|
|
void UI_EndFrame(UI_Frame *frame, i32 vsync);
|