#pragma once ENUM(UI_SizeKind, u8, UI_SIZE_PIXEL_COUNT, UI_SIZE_PERCENTAGE, UI_SIZE_BY_CHILDREN, UI_SIZE_COUNT, ); STRUCT(UI_Size) { UI_SizeKind kind; f32 value; f32 strictness; }; static_assert(sizeof(UI_Size) == 12); decl_vb(UI_Size); STRUCT(UI_Key) { u64 value; }; STRUCT(UI_MousePosition) { f64 x; f64 y; }; ENUM(UI_WidgetFlagEnum, u64, UI_WIDGET_FLAG_DISABLED = 1 << 0, UI_WIDGET_FLAG_MOUSE_CLICKABLE = 1 << 1, UI_WIDGET_FLAG_KEYBOARD_PRESSABLE = 1 << 2, UI_WIDGET_FLAG_DRAW_TEXT = 1 << 3, UI_WIDGET_FLAG_DRAW_BACKGROUND = 1 << 4, UI_WIDGET_FLAG_OVERFLOW_X = 1 << 5, UI_WIDGET_FLAG_OVERFLOW_Y = 1 << 6, UI_WIDGET_FLAG_FLOATING_X = 1 << 7, UI_WIDGET_FLAG_FLOATING_Y = 1 << 8, ); UNION(UI_WidgetFlags) { struct { u64 disabled:1; u64 mouse_clickable:1; u64 keyboard_pressable:1; u64 draw_text:1; u64 draw_background:1; u64 overflow_x:1; u64 overflow_y:1; u64 floating_x:1; u64 floating_y:1; }; u64 v; }; static_assert(sizeof(UI_WidgetFlags) == sizeof(u64)); STRUCT(UI_Widget) { // Random category I temporarily introduce String text; UI_Widget* hash_previous; UI_Widget* hash_next; UI_Widget* first; UI_Widget* last; UI_Widget* next; UI_Widget* previous; UI_Widget* parent; u64 child_count; UI_Key key; // Input parameters UI_Size pref_size[AXIS2_COUNT]; Axis2 child_layout_axis; UI_WidgetFlags flags; // Data known after size determination happens float2 computed_size; float2 computed_relative_position; // Data known after layout computation happens F32Interval2 relative_rect; F32Interval2 rect; float2 relative_corner_delta[CORNER_COUNT]; // Persistent data across frames u64 last_build_touched; float2 view_offset; float4 background_colors[4]; float4 text_color; }; decl_vbp(UI_Widget); STRUCT(UI_WidgetSlot) { UI_Widget* first; UI_Widget* last; }; declare_slice(UI_WidgetSlot); decl_vb(Axis2); decl_vb(float4); STRUCT(UI_StateStackAutoPops) { u64 parent:1; u64 pref_width:1; u64 pref_height:1; u64 child_layout_axis:1; u64 text_color:1; u64 background_color:1; u64 font_size:1; }; static_assert(sizeof(UI_StateStackAutoPops) % sizeof(u64) == 0); STRUCT(UI_StateStackNulls) { UI_Widget* parent; UI_Size pref_width; UI_Size pref_height; Axis2 child_layout_axis; float4 text_color; float4 background_color; f32 font_size; }; STRUCT(UI_StateStacks) { VirtualBufferP(UI_Widget) parent; VirtualBuffer(UI_Size) pref_width; VirtualBuffer(UI_Size) pref_height; VirtualBuffer(Axis2) child_layout_axis; VirtualBuffer(float4) text_color; VirtualBuffer(float4) background_color; VirtualBuffer(f32) font_size; }; STRUCT(UI_State) { Arena* arena; Arena* build_arenas[2]; Renderer* renderer; RenderWindow* render_window; WindowingInstance* window; u64 build_count; f64 frame_time; UI_Widget* root; UI_MousePosition mouse_position; Slice(UI_WidgetSlot) widget_table; UI_Widget* free_widget_list; u64 free_widget_count; WindowingEventMouseButtonEvent mouse_button_events[WINDOWING_EVENT_MOUSE_BUTTON_COUNT]; u8 focused:1; UI_StateStacks stacks; UI_StateStackNulls stack_nulls; UI_StateStackAutoPops stack_autopops; }; enum { UI_SignalFlag_ClickedLeft = (1 << 0), }; typedef u32 UI_SignalFlags; STRUCT(UI_Signal) { UI_Widget* widget; union { UI_SignalFlags flags; struct { u32 clicked_left:1; u32 reserved:31; }; }; }; #define ui_stack_autopop_set(field_name, value) ui_state->stack_autopops.field_name = (value) #define ui_stack_push_impl(field_name, value, auto_pop_value) do \ {\ *vb_add(&ui_state->stacks.field_name, 1) = (value);\ ui_stack_autopop_set(field_name, auto_pop_value);\ } while (0) #define ui_push(field_name, value) ui_stack_push_impl(field_name, value, 0) #define ui_push_next_only(field_name, value) ui_stack_push_impl(field_name, value, 1) #define ui_pop(field_name) (typeof(ui_state->stacks.field_name.pointer)) ui_pop_generic((VirtualBuffer(u8)*)&ui_state->stacks.field_name, sizeof(*ui_state->stacks.field_name.pointer)) #define ui_top(field_name) (ui_state->stacks.field_name.length ? ui_state->stacks.field_name.pointer[ui_state->stacks.field_name.length - 1] : ui_state->stack_nulls.field_name) fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size) { let(length, stack->length); assert(length > 0); let(next_length, length - 1); let(index, next_length); let(result, &stack->pointer[index * element_size]); stack->length = next_length; return result; } fn UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window); fn void ui_state_select(UI_State* state); fn u8 ui_build_begin(WindowingInstance* window, f64 frame_time, WindowingEventQueue* event_queue); fn void ui_build_end(); fn void ui_draw(); fn UI_Signal ui_signal_from_widget(UI_Widget* widget); fn UI_State* ui_state_get(); fn UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string); fn UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...); fn UI_Size ui_pixels(u32 width, f32 strictness); fn UI_Size ui_percentage(f32 percentage, f32 strictness); fn UI_Size ui_em(f32 value, f32 strictness);