app_codebase/src/ui/ui_core.h
Anton Ljungdahl caf9a7f24b hueh
2024-08-08 21:19:46 +02:00

403 lines
11 KiB
C

/* date = March 25th 2024 10:14 pm */
#ifndef UI_CORE_H
#define UI_CORE_H
////////////////////////////////
//~ Keys
typedef enum UI_Color
{
UI_Color_Null,
UI_Color_PlainBackground,
UI_Color_PlainText,
UI_Color_PlainBorder,
UI_Color_PlainOverlay,
UI_Color_COUNT
} UI_Color;
////////////////////////////////
//~ Keys
// The UI Key is used to hash a "widget" so we can get events from it.
typedef struct UI_Key UI_Key;
struct UI_Key
{
U64 u64[1];
};
////////////////////////////////
//~ Mouse Button Kinds
typedef enum UI_MouseButtonKind
{
UI_MouseButtonKind_Left,
UI_MouseButtonKind_Middle,
UI_MouseButtonKind_Right,
UI_MouseButtonKind_COUNT
}
UI_MouseButtonKind;
////////////////////////////////
//~ Focus Types (for hot, active widget states)
typedef enum UI_FocusKind
{
UI_FocusKind_Null,
UI_FocusKind_Off,
UI_FocusKind_On,
UI_FocusKind_Root,
UI_FocusKind_COUNT
}
UI_FocusKind;
////////////////////////////////
//~ Semantic sizes
// The size kind specifies how the size of a box should be computed
typedef enum UI_SizeKind
{
UI_SizeKind_Pixels,
UI_SizeKind_TextDim,
UI_SizeKind_Percent,
UI_SizeKind_SizeByChildren,
UI_SizeKind_COUNT
}
UI_SizeKind;
typedef struct UI_Size UI_Size;
struct UI_Size
{
UI_SizeKind kind;
F32 value;
F32 strictness;
};
////////////////////////////////
//~ Main UI hierarchy
typedef enum UI_TextAlignment
{
UI_TextAlignment_Left,
UI_TextAlignment_COUNT,
}
UI_TextAlignment;
typedef U32 UI_BoxFlags;
enum
{
// Interaction
UI_BoxFlag_Disabled = (1<<0),
UI_BoxFlag_MouseClickable = (1<<1),
UI_BoxFlag_FocusHot = (1<<3),
UI_BoxFlag_FocusActive = (1<<4),
// Layout
UI_BoxFlag_FloatingX = (1<<5),
UI_BoxFlag_FloatingY = (1<<6),
UI_BoxFlag_FixedWidth = (1<<7),
UI_BoxFlag_FixedHeight = (1<<8),
UI_BoxFlag_OverflowX = (1<<9),
UI_BoxFlag_OverflowY = (1<<10),
// Appearance
UI_BoxFlag_Clip = (1<<11),
UI_BoxFlag_DrawText = (1<<12),
UI_BoxFlag_DrawBackground = (1<<13),
UI_BoxFlag_DrawBorder = (1<<14),
UI_BoxFlag_DrawHotEffects = (1<<15),
UI_BoxFlag_DrawActiveEffects = (1<<16),
UI_BoxFlag_DisableTextTruncate = (1<<17),
UI_BoxFlag_DrawDropShadow = (1<<18),
// Helpers, when either X or Y is active etc.
UI_BoxFlag_Floating = UI_BoxFlag_FloatingX | UI_BoxFlag_FloatingY,
UI_BoxFlag_Clickable = UI_BoxFlag_MouseClickable, //| UI_BoxFlag_KeyboardClickable,
};
// UI Box is the big struct that handles all of the information of a "node" in the ui hierarchy.
// The box is a part of the composition that can be described as a "widget".
// It has both the tree information adn the state information in it.
typedef struct UI_Box UI_Box;
struct UI_Box
{
// Hash links, persistent across frames
UI_Box *hash_next;
UI_Box *hash_prev;
// Tree link data, updates every frame.
// This enables us to encode an n-ary tree to describe the "box" hierarchy that defines the UI.
UI_Box *first;
UI_Box *last;
UI_Box *next;
UI_Box *prev;
UI_Box *parent;
U64 child_count;
// Key and generation info
UI_Key key;
U64 last_frame_touched_index;
// Per-build parameters
UI_BoxFlags flags;
String8 string;
Vec2_F32 fixed_position;
Vec2_F32 fixed_size;
UI_Size pref_size[Axis2_COUNT];
Axis2 child_layout_axis;
Vec4_F32 background_color;
Vec4_F32 text_color;
Vec4_F32 border_color;
Vec4_F32 overlay_color;
F32 corner_radii[Corner_COUNT];
// Post size determination
Vec2_F32 calc_size;
Vec2_F32 calc_rel_pos;
// Post-layout data
Rng2_F32 rel_rect;
Rng2_F32 rect;
// State that is persistent across frames
F32 hot_t;
F32 active_t;
F32 disabled_t;
F32 focus_hot_t;
F32 focus_active_t;
U64 first_gen_touched;
U64 last_gen_touched;
Vec2_F32 view_offset;
Vec2_F32 target_view_offset;
};
typedef struct UI_BoxRec UI_BoxRec;
struct UI_BoxRec
{
UI_Box *next;
S32 push_count;
S32 pop_count;
};
//~ Signal
typedef U32 UI_SignalFlags;
enum
{
// mouse press -> box was pressed while hovering
UI_SignalFlag_LeftPressed = (1<<0),
UI_SignalFlag_MiddlePressed = (1<<1),
UI_SignalFlag_RightPressed = (1<<2),
// released -> box was previously pressed & user released, in or out of bounds
UI_SignalFlag_LeftReleased = (1<<12),
UI_SignalFlag_MiddleReleased = (1<<13),
UI_SignalFlag_RightReleased = (1<<14),
// clicked -> box was previously pressed & user released, in bounds
UI_SignalFlag_LeftClicked = (1<<15),
UI_SignalFlag_MiddleClicked = (1<<16),
UI_SignalFlag_RightClicked = (1<<17),
UI_SignalFlag_Hovering = (1<<25), // hovering specifically this box
UI_SignalFlag_MouseOver = (1<<26), // mouse is over, but may be occluded
UI_SignalFlag_Pressed = UI_SignalFlag_LeftPressed | UI_SignalFlag_MiddlePressed | UI_SignalFlag_RightPressed,
UI_SignalFlag_Clicked = UI_SignalFlag_LeftClicked | UI_SignalFlag_MiddleClicked | UI_SignalFlag_RightClicked
};
// The UI Signal is the struct which carries the information about user interaction with a box
typedef struct UI_Signal UI_Signal;
struct UI_Signal
{
UI_Box *box;
UI_SignalFlags flag;
};
#define UI_hovering(s) !!((s).flag & UI_SignalFlag_Hovering)
#define UI_pressed(s) !!((s).flag & UI_SignalFlag_Pressed)
/////////////////////////////////
//~ Generated/meta
// This is "metaprogramming" code that eventually will be generated externally.
// Right now I am doing some thing by hand, but it basically contains macros that define
// different stack members for the UI state.
#include "ui_meta.h"
////////////////////////////////
//~ UI State
// This is a slot in the hash table that helps caching of UI boxes between frames.
typedef struct UI_BoxSlot UI_BoxSlot;
struct UI_BoxSlot
{
UI_Box *first;
UI_Box *last;
};
typedef struct UI_State UI_State;
struct UI_State
{
// Permanent state
U64 build_gen;
Arena *arena;
// Frame arenas. We have two since we want to use information from the previous frame to
// compute things in the current frame.
U32 last_frame_arena_index;
U32 current_frame_arena_index;
Arena *frame_arenas[2];
//- Persistent box state
UI_Box *first_free_box;
U64 free_box_list_count;
UI_BoxSlot *box_table;
U64 box_table_size;
//- Per UI build parameters
OS_Handle window;
OS_EventList *events;
UI_Box *root;
UI_Box *ctx_menu_root;
B32 ctx_menu_touched_this_frame;
Vec2_F32 mouse;
//- User interaction state
UI_Key hot_box_key;
UI_Key active_box_key[UI_MouseButtonKind_COUNT];
//- Color, rendering properties
Vec4_F32 colors[UI_Color_COUNT];
//- Context menu state
UI_Key ctx_menu_anchor_key;
UI_Key next_ctx_menu_anchor_key;
Vec2_F32 ctx_menu_anchor_box_last_pos;
Vec2_F32 ctx_menu_anchor_offset;
B32 ctx_menu_open;
B32 next_ctx_menu_open;
UI_Key ctx_menu_key;
B32 ctx_menu_changed;
// TODO(anton): tooltip root
// Stack state. Here Ryan uses generated code from metadesk. I will do it by hand to start with.
UI_declare_stack_nils;
UI_declare_stacks;
};
////////////////////////////////
//~ Globals
// Nil structs
global UI_Box ui_g_nil_box;
////////////////////////////////
//~ Basic type functions
//- Boxes
root_function B32 UI_box_is_nil(UI_Box *box);
#define UI_box_set_nil(b) ((b) = &ui_g_nil_box)
root_function UI_BoxRec UI_box_recurse_depth_first(UI_Box *box, UI_Box *stopper, MemberOffset sib, MemberOffset child);
#define UI_box_recurse_depth_first_post(box, stopper) UI_box_recurse_depth_first((box), (stopper), MemberOff(UI_Box, prev), MemberOff(UI_Box, last));
//- Sizes
root_function UI_Size UI_size_make(UI_SizeKind kind, F32 value, F32 strictness);
#define UI_pixels(v, strictness) UI_size_make(UI_SizeKind_Pixels, (v), (strictness))
#define UI_size_by_children(v, strictness) UI_size_make(UI_SizeKind_SizeByChildren, (v), (strictness))
#define UI_pct(v, strictness) UI_size_make(UI_SizeKind_Percent, (v), (strictness))
//- ID strings
root_function String8 UI_hash_part_from_box_string(String8 string);
//- Keys
root_function UI_Key UI_key_zero(void);
root_function UI_Key UI_key_from_string(UI_Key seed, String8 string);
root_function B32 UI_key_match(UI_Key a, UI_Key b);
//- Signal
root_function UI_Signal UI_signal_from_box(UI_Box *box);
////////////////////////////////
//~ UI State functions
root_function UI_State *UI_state_alloc(void);
root_function void UI_state_set(UI_State *ui);
////////////////////////////////
//~ Build phase
root_function void UI_build_begin(OS_Handle window, OS_EventList *events);
root_function void UI_build_end(void);
////////////////////////////////
//~ Context menu
root_function void UI_ctx_menu_open(UI_Key key, UI_Key anchor_key, Vec2_F32 anchor_offset);
root_function void UI_ctx_menu_close(void);
root_function B32 UI_begin_ctx_menu(UI_Key key);
root_function void UI_end_ctx_menu(void);
////////////////////////////////
//~ UI Frame
root_function Arena *UI_frame_arena(void);
root_function void UI_frame_begin(F32 delta_time);
root_function void UI_frame_end(void);
////////////////////////////////
//~ Box hierarchy construction
root_function UI_Box *UI_box_from_key(UI_Key key);
root_function UI_Box *UI_box_make_from_key(UI_BoxFlags flags, UI_Key key);
root_function UI_Box *UI_box_make(UI_BoxFlags flags, String8 string);
////////////////////////////////
//~ layout
root_function void UI_solve_independent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_upward_dependent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_downward_dependent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_size_violations(UI_Box *root, Axis2 axis);
root_function void UI_layout_root(UI_Box *root, Axis2 axis);
root_function void UI_layout(void);
////////////////////////////////
//~ Compositions
root_function void UI_push_pref_size(Axis2 axis, UI_Size v);
root_function void UI_pop_pref_size(Axis2 axis);
root_function void UI_set_next_pref_size(Axis2 axis, UI_Size v);
root_function void UI_push_fixed_pos(Vec2_F32 v);
root_function void UI_pop_fixed_pos();
root_function void UI_set_next_fixed_pos(Vec2_F32 v);
root_function void UI_push_fixed_rect(Rng2_F32 rect);
root_function void UI_pop_fixed_rect();
root_function void UI_set_next_fixed_rect(Rng2_F32 rect);
////////////////////////////////
//~ Drawing and text
root_function Vec2_F32 UI_text_pos_from_box(UI_Box *box);
root_function String8 UI_display_string_from_box(UI_Box *box);
////////////////////////////////
//~ Defer helpers
//- base
#define UI_parent(v) DeferLoop(UI_push_parent(v), UI_pop_parent())
#define UI_pref_width(v) DeferLoop(UI_push_pref_width(v), UI_pop_pref_width())
#define UI_pref_height(v) DeferLoop(UI_push_pref_height(v), UI_pop_pref_height())
//- pane
#define UI_pane(r, s) DeferLoop(UI_pane_begin((r), (s)), UI_pane_end())
//- compositions
#define UI_width_fill UI_pref_width(UI_pct(1, 0))
#define UI_height_fill UI_pref_height(UI_pct(1, 0))
#define UI_fixed_x(v) DeferLoop(UI_push_fixed_x((v)), UI_pop_fixed_x())
#define UI_fixed_y(v) DeferLoop(UI_push_fixed_y((v)), UI_pop_fixed_y())
#define UI_fixed_pos(v) DeferLoop(UI_push_fixed_pos(v), UI_pop_fixed_pos())
#define UI_ctx_menu(key) DeferLoopChecked(UI_begin_ctx_menu(key), UI_end_ctx_menu())
#endif //UI_CORE_H