From 74e3821233dbdcecbb8e4a1337f96d5d83e0ba37 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Mon, 18 Mar 2024 15:36:11 -0500 Subject: [PATCH] ui layouting...? --- justfile | 4 +- shaders/vertex.wgsl | 12 +-- src/main.c | 10 ++- src/ui.h | 176 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 184 insertions(+), 18 deletions(-) diff --git a/justfile b/justfile index ffb30eb..d8e1dd5 100644 --- a/justfile +++ b/justfile @@ -3,8 +3,8 @@ alias r := run build: transpile_shaders_metal mkdir -p bin - # cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor - cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor + cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor + # cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor # cc bin/*.o -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor run: build diff --git a/shaders/vertex.wgsl b/shaders/vertex.wgsl index 84ad7d3..26db927 100644 --- a/shaders/vertex.wgsl +++ b/shaders/vertex.wgsl @@ -15,23 +15,23 @@ struct VertexOutput { @location(0) tex_coord: vec2, } -// struct Params { -// screen_size: vec4, -// } +struct Params { + screen_size: vec4, +} fn to_device_position(position: vec2, size: vec2) -> vec4 { return vec4((((position / size) * 2.) - 1.), 1., 1.); } @group(0) @binding(0) -var screen_size: vec4; +var params: Params; @vertex fn vs_main(input: VertexInput) -> VertexOutput { var out: VertexOutput; - var vertex_pos = to_device_position(((input.position.xy + 1.) / 2.) * (input.size/2.0) + input.target_position + vec2(0., (input.y_offset/2.0)+32), screen_size.xy); - // vertex_pos.y = -vertex_pos.y; + var vertex_pos = to_device_position(((input.position.xy + 1.) / 2.) * (input.size/2.0) + input.target_position + vec2(0., (input.y_offset/2.0)+32), params.screen_size.xy); + vertex_pos.y = -vertex_pos.y; var atlas_position = (((input.position.xy + 1.) / 2.) * input.size + input.atlas_position) / vec2(1024); out.position = vertex_pos; diff --git a/src/main.c b/src/main.c index 4285586..e230d85 100644 --- a/src/main.c +++ b/src/main.c @@ -14,8 +14,13 @@ #define SOKOL_LOG_IMPL // TODO: condition compilation -// #define SOKOL_METAL -#define SOKOL_GLCORE33 +#if defined (__APPLE__) + #define SOKOL_METAL +#elif defined (__linux__) || defined (__unix__) + #define SOKOL_GLCORE33 +#else + #error "Unsupported platform for shaders" +#endif #include #include @@ -140,6 +145,7 @@ void ed_init() { // TODO: grab default font from the system FILE *ttf_file = fopen("./bin/JetBrainsMono-Medium.ttf", "rb"); if (!ttf_file) { + fprintf(stderr, "failed to load font\n"); exit(1); } assert(fread(ttf_buffer, 1, 1<<20, ttf_file)); diff --git a/src/ui.h b/src/ui.h index 4b2879b..cd44bd9 100644 --- a/src/ui.h +++ b/src/ui.h @@ -5,7 +5,12 @@ #define MAX_UI_ELEMENTS 2048 +// TODO: replace this with functions +#define _FONT_WIDTH 16 +#define _FONT_HEIGHT 32 + #define _elm(index) (&cx->frame_elements.data[index]) +#define _flags(index, flgs) ((_elm(index)->flags & (flgs)) == (flgs)) #define _first(index) (_elm(index)->first) #define _last(index) (_elm(index)->last) @@ -93,22 +98,27 @@ arrayTemplate(ui_element_frame_data); typedef struct { ed_ht cached_elements; array(ui_element_frame_data) frame_elements; + array(ui_element_frame_data) frame_floating_elements; size_t frame_index; + uint32_t canvas_size[2]; size_t current_parent; } ui_context; +void ui_compute_layout(ui_context *cx, size_t element_index); #ifdef ED_UI_IMPLEMENTATION ui_context init_ui_context() { ed_ht cached_elements = ht_create(MAX_UI_ELEMENTS, sizeof(ui_element_cache_data)); array(ui_element_frame_data) frame_elements = newArray(ui_element_frame_data, MAX_UI_ELEMENTS); + array(ui_element_frame_data) frame_floating_elements = newArray(ui_element_frame_data, MAX_UI_ELEMENTS); return (ui_context) { .cached_elements = cached_elements, .frame_elements = frame_elements, + .frame_floating_elements = frame_floating_elements, .frame_index = 0, }; } @@ -120,9 +130,9 @@ size_t ui_element(ui_context *cx, string label) { // with the same label can't be created together .key = label, .label = label, - .first = 0, - .last = 0, - .next = 0, + .first = -1, + .last = -1, + .next = -1, .prev = cx->frame_elements.data[cx->current_parent].last, .parent = cx->current_parent, }; @@ -143,20 +153,170 @@ size_t ui_element(ui_context *cx, string label) { pushArray(ui_element_frame_data, &cx->frame_elements, frame_data); - if (frame_data.prev) { + if (frame_data.prev >= 0) { _prev_ref(frame_data.index)->next = frame_data.index; } - if (_elm(cx->current_parent)->first == 0) { + if (_elm(cx->current_parent)->first < 0) { _elm(cx->current_parent)->first = frame_data.index; } _elm(cx->current_parent)->last = frame_data.index; + + return frame_data.index; } -void ui_compute_layout(ui_context *cx, uint32_t canvas_size[2], size_t element_index) { +static uint32_t _ui_ancestor_size(ui_context *cx, size_t element_index, ui_axis axis) { + if (element_index < 0 || _parent(element_index) < 0) return 0; + + switch (_parent_ref(element_index)->size.semantic_size[axis].type) { + case UI_SEMANTIC_SIZE_FIT_TEXT: + case UI_SEMANTIC_SIZE_FILL: + case UI_SEMANTIC_SIZE_EXACT: + case UI_SEMANTIC_SIZE_PERCENT_OF_PARENT: + return _parent_ref(element_index)->size.computed_size[axis]; + break; + + case UI_SEMANTIC_SIZE_CHILDREN_SUM: + return _ui_ancestor_size(cx, _parent(element_index), axis); + break; + } +} + +static void _ui_compute_simple_layout(ui_context *cx, ui_element_frame_data *elm, ui_axis axis, bool *post_compute) { + switch (elm->size.semantic_size[axis].type) { + case UI_SEMANTIC_SIZE_FIT_TEXT: + if (axis == UI_AXIS_HORIZONTAL) { + elm->size.computed_size[axis] = elm->label.len * _FONT_WIDTH; + } else if (axis == UI_AXIS_VERTICAL) { + elm->size.computed_size[axis] = _FONT_WIDTH; + } + break; + + case UI_SEMANTIC_SIZE_CHILDREN_SUM: + post_compute[axis] = true; + break; + + case UI_SEMANTIC_SIZE_FILL: + // TODO: set to ancestor size for floating + break; + + case UI_SEMANTIC_SIZE_EXACT: + elm->size.computed_size[axis] = elm->size.semantic_size[axis].integer; + break; + + case UI_SEMANTIC_SIZE_PERCENT_OF_PARENT: + { + float semantic_value = (float)elm->size.semantic_size[axis].integer; + elm->size.computed_size[axis] = (uint32_t)((float)(_ui_ancestor_size(cx, elm->index, axis)) * (semantic_value/100.0)); + } + break; + } +} + +static void _ui_compute_children_layout(ui_context *cx, ui_element_frame_data *elm) { + uint32_t child_size[2] = { 0 }; + + // NOTE: the number of fills for the opposite axis of this box needs to be 1 + // because it will never get incremented in the loop below and cause a divide by zero + // and the number of fills for the axis of the box needs to start at zero or else it will + // be n+1 causing incorrect sizes + uint32_t num_fills[2] = { 1 }; + num_fills[elm->size.axis] = 0; + + // TODO: maybe just use the actual data instead of copying? + uint32_t elm_size[2] = { elm->size.computed_size[0], elm->size.computed_size[1] }; + + size_t child_index = elm->first; + do { + ui_compute_layout(cx, child_index); + + if (_elm(child_index)->size.semantic_size[elm->size.axis].type == UI_SEMANTIC_SIZE_FILL) { + num_fills[elm->size.axis] += 1; + } else { + child_size[elm->size.axis] += _elm(child_index)->size.computed_size[elm->size.axis]; + } + } while ((child_index = _next(child_index)) >= 0); + + child_index = elm->first; + do { + for (size_t axis = UI_AXIS_HORIZONTAL; axis < UI_AXIS_VERTICAL; ++axis) { + if (_elm(child_index)->size.semantic_size[axis].type == UI_SEMANTIC_SIZE_FILL) { + _elm(child_index)->size.computed_pos[axis] = (elm_size[axis] - child_size[axis]) / num_fills[axis]; + } + } + + ui_compute_layout(cx, child_index); + } while ((child_index = _next(child_index)) >= 0); +} + +void ui_compute_layout(ui_context *cx, size_t element_index) { + if (element_index <= 0) return; + ui_axis axis = UI_AXIS_HORIZONTAL; + __auto_type elm = _elm(element_index); - // FIXME: change me to use -1 for no reference to element - // TODO: actually compute layout + if (_parent(element_index) >= 0 && (_elm(element_index)->flags & UI_FLAG_FLOATING) > 0) { + __auto_type parent = _parent_ref(element_index); + axis = parent->size.axis; + elm->size.computed_pos[0] = parent->size.computed_pos[0]; + elm->size.computed_pos[1] = parent->size.computed_pos[1]; + + // TODO: implement scrolling + // elm->size.computed_pos[axis] += parent.scroll_offset; + } + + if (!_flags(element_index, UI_FLAG_FLOATING) && _prev(element_index) >= 0) { + __auto_type prev = _prev_ref(element_index); + + if (prev>= 0) { + elm->size.computed_pos[axis] = prev->size.computed_pos[axis] + prev->size.computed_size[axis]; + } + } + + bool post_compute[2] = { false, false }; + _ui_compute_simple_layout(cx, elm, UI_AXIS_HORIZONTAL, post_compute); + _ui_compute_children_layout(cx, elm); + + // NOTE(pcleavelin): the only difference between these two blocks is the ordering of the switch block + // they can probably be merged + if (post_compute[UI_AXIS_HORIZONTAL]) { + elm->size.computed_size[UI_AXIS_HORIZONTAL] = 0; + + size_t child_index = elm->first; + do { + __auto_type child = _elm(child_index); + + switch (elm->size.axis) { + case UI_AXIS_HORIZONTAL: + elm->size.computed_size[UI_AXIS_HORIZONTAL] += child->size.computed_size[UI_AXIS_HORIZONTAL]; + break; + + case UI_AXIS_VERTICAL: + if (child->size.computed_size[UI_AXIS_HORIZONTAL] > elm->size.computed_size[UI_AXIS_HORIZONTAL]) { + elm->size.computed_size[UI_AXIS_HORIZONTAL] = child->size.computed_size[UI_AXIS_HORIZONTAL]; + } + break; + } + } while ((child_index = _next(child_index)) >= 0); + } + if (post_compute[UI_AXIS_VERTICAL]) { + elm->size.computed_size[UI_AXIS_VERTICAL] = 0; + + size_t child_index = elm->first; + do { + __auto_type child = _elm(child_index); + + switch (elm->size.axis) { + case UI_AXIS_HORIZONTAL: + if (child->size.computed_size[UI_AXIS_VERTICAL] > elm->size.computed_size[UI_AXIS_VERTICAL]) { + elm->size.computed_size[UI_AXIS_VERTICAL] = child->size.computed_size[UI_AXIS_VERTICAL]; + } + break; + case UI_AXIS_VERTICAL: + elm->size.computed_size[UI_AXIS_VERTICAL] += child->size.computed_size[UI_AXIS_VERTICAL]; + break; + } + } while ((child_index = _next(child_index)) >= 0); + } } #endif