Improve ui library
This commit is contained in:
		
							parent
							
								
									83b1d2eed5
								
							
						
					
					
						commit
						3f9be0ffae
					
				@ -65,6 +65,26 @@ STRUCT(BBGUIState)
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
global_variable BBGUIState state;
 | 
					global_variable BBGUIState state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn void ui_top_bar()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ui_push(pref_height, ui_em(1, 1));
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ui_push(child_layout_axis, AXIS2_X);
 | 
				
			||||||
 | 
					        auto* top_bar = ui_widget_make((UI_WidgetFlags) {
 | 
				
			||||||
 | 
					                .draw_background = 1,
 | 
				
			||||||
 | 
					                }, strlit("top_bar"));
 | 
				
			||||||
 | 
					        ui_push(parent, top_bar);
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ui_button(strlit("Button 1"));
 | 
				
			||||||
 | 
					            ui_button(strlit("Button 2"));
 | 
				
			||||||
 | 
					            ui_button(strlit("Button 3"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ui_pop(parent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ui_pop(pref_height);
 | 
				
			||||||
 | 
					    ui_button(strlit("Hello!"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn void app_update()
 | 
					fn void app_update()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto frame_end = os_timestamp();
 | 
					    auto frame_end = os_timestamp();
 | 
				
			||||||
@ -87,24 +107,16 @@ fn void app_update()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (likely(ui_build_begin(window->os, frame_ms, &state.event_queue)))
 | 
					        if (likely(ui_build_begin(window->os, frame_ms, &state.event_queue)))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ui_font_size(default_font_height);
 | 
					            ui_push(font_size, default_font_height);
 | 
				
			||||||
            ui_pref_width(ui_em(10, 1));
 | 
					 | 
				
			||||||
            ui_pref_height(ui_em(2, 1));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (unlikely(ui_button(strlit("Hello world\n")).clicked_left))
 | 
					            ui_top_bar();
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                print("Clicked on hello world\n");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (unlikely(ui_button(strlit("Bye world\n")).clicked_left))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                print("Clicked on bye world\n");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ui_build_end();
 | 
					            ui_build_end();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ui_draw();
 | 
					            ui_draw();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ui_pop(font_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            renderer_window_frame_end(renderer, render_window);
 | 
					            renderer_window_frame_end(renderer, render_window);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
 | 
				
			|||||||
@ -338,6 +338,8 @@ fn u64 safe_flag(u64 value, u64 flag)
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define member_from_offset(pointer, type, memory_offset) (*(type*)((u8*)pointer + memory_offset))
 | 
				
			||||||
 | 
					#define offset_of(T, member) __builtin_offsetof(T, member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define my_panic(...) do \
 | 
					#define my_panic(...) do \
 | 
				
			||||||
{\
 | 
					{\
 | 
				
			||||||
 | 
				
			|||||||
@ -77,6 +77,7 @@ STRUCT(UI_Widget)
 | 
				
			|||||||
    UI_Widget* next;
 | 
					    UI_Widget* next;
 | 
				
			||||||
    UI_Widget* previous;
 | 
					    UI_Widget* previous;
 | 
				
			||||||
    UI_Widget* parent;
 | 
					    UI_Widget* parent;
 | 
				
			||||||
 | 
					    u64 child_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    UI_Key key;
 | 
					    UI_Key key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -189,6 +190,33 @@ STRUCT(UI_Signal)
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern UI_State* ui_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto length = stack->length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(length > 0);
 | 
				
			||||||
 | 
					    auto next_length = length - 1;
 | 
				
			||||||
 | 
					    auto index = next_length;
 | 
				
			||||||
 | 
					    auto* result = &stack->pointer[index * element_size];
 | 
				
			||||||
 | 
					    stack->length = next_length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPORT UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window);
 | 
					EXPORT UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window);
 | 
				
			||||||
EXPORT void ui_state_select(UI_State* state);
 | 
					EXPORT void ui_state_select(UI_State* state);
 | 
				
			||||||
EXPORT u8 ui_build_begin(OSWindow window, f64 frame_time, OSEventQueue* event_queue);
 | 
					EXPORT u8 ui_build_begin(OSWindow window, f64 frame_time, OSEventQueue* event_queue);
 | 
				
			||||||
@ -201,6 +229,3 @@ EXPORT UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string);
 | 
				
			|||||||
EXPORT UI_Size ui_pixels(u32 width, f32 strictness);
 | 
					EXPORT UI_Size ui_pixels(u32 width, f32 strictness);
 | 
				
			||||||
EXPORT UI_Size ui_percentage(f32 percentage, f32 strictness);
 | 
					EXPORT UI_Size ui_percentage(f32 percentage, f32 strictness);
 | 
				
			||||||
EXPORT UI_Size ui_em(f32 value, f32 strictness);
 | 
					EXPORT UI_Size ui_em(f32 value, f32 strictness);
 | 
				
			||||||
EXPORT void ui_pref_width(UI_Size size);
 | 
					 | 
				
			||||||
EXPORT void ui_pref_height(UI_Size size);
 | 
					 | 
				
			||||||
EXPORT void ui_font_size(f32 size);
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,42 +1,23 @@
 | 
				
			|||||||
 | 
					// This UI is heavily inspired by the ideas of Casey Muratori and Ryan Fleury ideas on GUI programming, to whom I am deeply grateful.
 | 
				
			||||||
 | 
					// Here are some links which helped me achieve this build
 | 
				
			||||||
 | 
					// https://www.youtube.com/watch?v=Z1qyvQsjK5Y
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-1-the-interaction-medium
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-2-build-it-every-frame-immediate
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-3-the-widget-building-language
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-4-the-widget-is-a-lie-node
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-5-visual-content
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-6-rendering
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-7-where-imgui-ends
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-8-state-mutation-jank-and
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-part-9-keyboard-and-gamepad-navigation
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/ui-bonus-1-simple-single-line-text
 | 
				
			||||||
 | 
					// https://www.rfleury.com/p/codebase-walkthrough-multi-window
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <std/ui_core.h>
 | 
					#include <std/ui_core.h>
 | 
				
			||||||
#include <std/format.h>
 | 
					#include <std/format.h>
 | 
				
			||||||
#include <std/string.h>
 | 
					#include <std/string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
global_variable UI_State* ui_state = 0;
 | 
					UI_State* ui_state = 0;
 | 
				
			||||||
#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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    auto length = stack->length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert(length > 0);
 | 
					 | 
				
			||||||
    auto next_length = length - 1;
 | 
					 | 
				
			||||||
    auto index = next_length;
 | 
					 | 
				
			||||||
    auto* result = &stack->pointer[index * element_size];
 | 
					 | 
				
			||||||
    stack->length = next_length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ui_stack_push(field_name, value) ui_stack_push_impl(field_name, value, 0)
 | 
					 | 
				
			||||||
#define ui_stack_push_next_only(field_name, value) ui_stack_push_impl(field_name, value, 1)
 | 
					 | 
				
			||||||
#define ui_stack_pop(field_name) (typeof(ui_state->stacks.field_name.pointer)) ui_pop_generic(&ui_state->stacks.field_name, sizeof(*ui_state->stacks.field_name.pointer))
 | 
					 | 
				
			||||||
#define ui_stack_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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ui_pref_width(UI_Size size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ui_stack_push(pref_width, size);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ui_pref_height(UI_Size size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ui_stack_push(pref_height, size);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn void ui_autopop(UI_State* state)
 | 
					fn void ui_autopop(UI_State* state)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -234,13 +215,13 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            table_widget_slot->last->next = widget;
 | 
					            table_widget_slot->last->hash_next = widget;
 | 
				
			||||||
            widget->previous = table_widget_slot->last;
 | 
					            widget->hash_previous = table_widget_slot->last;
 | 
				
			||||||
            table_widget_slot->last = widget;
 | 
					            table_widget_slot->last = widget;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto* parent = ui_stack_top(parent);
 | 
					    auto* parent = ui_top(parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (parent)
 | 
					    if (parent)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -253,9 +234,11 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            auto* previous_last = parent->last;
 | 
					            auto* previous_last = parent->last;
 | 
				
			||||||
            previous_last->next = widget;
 | 
					            previous_last->next = widget;
 | 
				
			||||||
 | 
					            widget->previous = previous_last;
 | 
				
			||||||
            parent->last = widget;
 | 
					            parent->last = widget;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parent->child_count += 1;
 | 
				
			||||||
        widget->parent = parent;
 | 
					        widget->parent = parent;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
@ -271,9 +254,9 @@ UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
 | 
				
			|||||||
    widget->first = 0;
 | 
					    widget->first = 0;
 | 
				
			||||||
    widget->last = 0;
 | 
					    widget->last = 0;
 | 
				
			||||||
    widget->last_build_touched = ui_state->build_count;
 | 
					    widget->last_build_touched = ui_state->build_count;
 | 
				
			||||||
    widget->pref_size[AXIS2_X] = ui_stack_top(pref_width);
 | 
					    widget->pref_size[AXIS2_X] = ui_top(pref_width);
 | 
				
			||||||
    widget->pref_size[AXIS2_Y] = ui_stack_top(pref_height);
 | 
					    widget->pref_size[AXIS2_Y] = ui_top(pref_height);
 | 
				
			||||||
    widget->child_layout_axis = ui_stack_top(child_layout_axis);
 | 
					    widget->child_layout_axis = ui_top(child_layout_axis);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ui_autopop(ui_state);
 | 
					    ui_autopop(ui_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -314,10 +297,6 @@ UI_Signal ui_signal_from_widget(UI_Widget* widget)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    auto rect = widget->rect;
 | 
					    auto rect = widget->rect;
 | 
				
			||||||
    auto mouse_position = ui_state->mouse_position;
 | 
					    auto mouse_position = ui_state->mouse_position;
 | 
				
			||||||
    if (widget->flags.mouse_clickable & (ui_state->mouse_button_events[OS_EVENT_MOUSE_LEFT].action == OS_EVENT_MOUSE_RELEASE))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        print("Clicked on {u32}x{u32}. Rect ({u32}, {u32}), ({u32}, {u32})\n", (u32)mouse_position.x, (u32)mouse_position.y, (u32)rect.p0.x, (u32)rect.p0.y, (u32)rect.p1.x, (u32)rect.p1.y);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    UI_Signal signal = {
 | 
					    UI_Signal signal = {
 | 
				
			||||||
        .clicked_left = 
 | 
					        .clicked_left = 
 | 
				
			||||||
            (widget->flags.mouse_clickable & (ui_state->mouse_button_events[OS_EVENT_MOUSE_LEFT].action == OS_EVENT_MOUSE_RELEASE)) &
 | 
					            (widget->flags.mouse_clickable & (ui_state->mouse_button_events[OS_EVENT_MOUSE_LEFT].action == OS_EVENT_MOUSE_RELEASE)) &
 | 
				
			||||||
@ -358,7 +337,7 @@ UI_Size ui_percentage(f32 percentage, f32 strictness)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
UI_Size ui_em(f32 value, f32 strictness)
 | 
					UI_Size ui_em(f32 value, f32 strictness)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto font_size = ui_stack_top(font_size);
 | 
					    auto font_size = ui_top(font_size);
 | 
				
			||||||
    assert(font_size);
 | 
					    assert(font_size);
 | 
				
			||||||
    return (UI_Size) {
 | 
					    return (UI_Size) {
 | 
				
			||||||
        .kind = UI_SIZE_PIXEL_COUNT,
 | 
					        .kind = UI_SIZE_PIXEL_COUNT,
 | 
				
			||||||
@ -367,11 +346,6 @@ UI_Size ui_em(f32 value, f32 strictness)
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_font_size(f32 size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ui_stack_push(font_size, size);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
 | 
					u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    ui_state->build_count += 1;
 | 
					    ui_state->build_count += 1;
 | 
				
			||||||
@ -478,20 +452,21 @@ u8 ui_build_begin(OSWindow os_window, f64 frame_time, OSEventQueue* event_queue)
 | 
				
			|||||||
        // }
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto framebuffer_size = os_window_framebuffer_size_get(os_window);
 | 
					        auto framebuffer_size = os_window_framebuffer_size_get(os_window);
 | 
				
			||||||
        ui_stack_push_next_only(pref_width, ui_pixels(framebuffer_size.width, 1.0f));
 | 
					        ui_push_next_only(pref_width, ui_pixels(framebuffer_size.width, 1.0f));
 | 
				
			||||||
        ui_stack_push_next_only(pref_height, ui_pixels(framebuffer_size.height, 1.0f));
 | 
					        ui_push_next_only(pref_height, ui_pixels(framebuffer_size.height, 1.0f));
 | 
				
			||||||
        ui_stack_push_next_only(child_layout_axis, AXIS2_Y);
 | 
					        ui_push_next_only(child_layout_axis, AXIS2_Y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto* root = ui_widget_make_format((UI_WidgetFlags) {}, "window_root_{u64}", os_window);
 | 
					        auto* root = ui_widget_make_format((UI_WidgetFlags) {}, "window_root_{u64}", os_window);
 | 
				
			||||||
        assert(!ui_state->stack_autopops.child_layout_axis);
 | 
					        assert(!ui_state->stack_autopops.child_layout_axis);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ui_stack_push(parent, root);
 | 
					        ui_push(parent, root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ui_stack_push(font_size, 12);
 | 
					        ui_push(font_size, 12);
 | 
				
			||||||
        ui_stack_push(text_color, Color4(1, 1, 1, 1));
 | 
					        ui_push(text_color, Color4(1, 1, 1, 1));
 | 
				
			||||||
        ui_stack_push(background_color, Color4(0, 0, 0, 1));
 | 
					        ui_push(background_color, Color4(0, 0, 0, 1));
 | 
				
			||||||
        ui_stack_push(pref_width, ui_percentage(1.0, 0.0));
 | 
					        ui_push(pref_width, ui_percentage(1.0, 0.0));
 | 
				
			||||||
        ui_stack_push(pref_height, ui_em(1.8, 0.0));
 | 
					        ui_push(pref_height, ui_percentage(1.0, 0.0));
 | 
				
			||||||
 | 
					        // ui_push(pref_height, ui_em(1.8, 0.0));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return open;
 | 
					    return open;
 | 
				
			||||||
@ -691,7 +666,7 @@ void ui_build_end()
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ui_stack_pop(parent);
 | 
					    ui_pop(parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ui_compute_independent_sizes(ui_state->root);
 | 
					    ui_compute_independent_sizes(ui_state->root);
 | 
				
			||||||
    ui_compute_upward_dependent_sizes(ui_state->root);
 | 
					    ui_compute_upward_dependent_sizes(ui_state->root);
 | 
				
			||||||
@ -709,6 +684,43 @@ fn RenderRect render_rect(F32Interval2 rect)
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STRUCT(WidgetIterator)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    UI_Widget* next;
 | 
				
			||||||
 | 
					    u32 push_count;
 | 
				
			||||||
 | 
					    u32 pop_count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ui_widget_recurse_depth_first_preorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, next), offset_of(UI_Widget, first))
 | 
				
			||||||
 | 
					#define ui_widget_recurse_depth_first_postorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, previous), offset_of(UI_Widget, last))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offset, u64 child_offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    WidgetIterator it = {};
 | 
				
			||||||
 | 
					    auto* child = member_from_offset(widget, UI_Widget*, child_offset);
 | 
				
			||||||
 | 
					    if (child)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        it.next = child;
 | 
				
			||||||
 | 
					        it.push_count += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (UI_Widget* w = widget; w; w = w->parent)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            auto* sibling = member_from_offset(w, UI_Widget*, sibling_offset);
 | 
				
			||||||
 | 
					            if (sibling)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                it.next = sibling;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            it.pop_count += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return it;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_draw()
 | 
					void ui_draw()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    UI_Widget* root = ui_state->root;
 | 
					    UI_Widget* root = ui_state->root;
 | 
				
			||||||
@ -717,9 +729,8 @@ void ui_draw()
 | 
				
			|||||||
    RenderWindow* window = ui_state->render_window;
 | 
					    RenderWindow* window = ui_state->render_window;
 | 
				
			||||||
    Renderer* renderer = ui_state->renderer;
 | 
					    Renderer* renderer = ui_state->renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (1)
 | 
					    while (widget)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // print("Widget 0x{u64:x}. {u32} {u32} {u32} {u32}\n", widget, (u32)widget->rect.p0.x, (u32)widget->rect.p0.y, (u32)widget->rect.p1.x, (u32)widget->rect.p1.y);
 | 
					 | 
				
			||||||
        if (widget->flags.draw_background)
 | 
					        if (widget->flags.draw_background)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            window_render_rect(window, (RectDraw) {
 | 
					            window_render_rect(window, (RectDraw) {
 | 
				
			||||||
@ -733,26 +744,7 @@ void ui_draw()
 | 
				
			|||||||
            window_render_text(renderer, window, widget->text, widget->text_color, RENDER_FONT_TYPE_PROPORTIONAL, widget->rect.x0, widget->rect.y0);
 | 
					            window_render_text(renderer, window, widget->text, widget->text_color, RENDER_FONT_TYPE_PROPORTIONAL, widget->rect.x0, widget->rect.y0);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (widget->first)
 | 
					        widget = ui_widget_recurse_depth_first_postorder(widget).next;
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            widget = widget->first;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (widget->next)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            widget = widget->next;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (widget->parent == ui_state->root)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (widget->parent)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            widget = widget->parent;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user