ui layouting...?

main
Patrick Cleavelin 2024-03-18 15:36:11 -05:00
parent b436268ffa
commit 74e3821233
4 changed files with 184 additions and 18 deletions

View File

@ -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

View File

@ -15,23 +15,23 @@ struct VertexOutput {
@location(0) tex_coord: vec2<f32>,
}
// struct Params {
// screen_size: vec4<f32>,
// }
struct Params {
screen_size: vec4<f32>,
}
fn to_device_position(position: vec2<f32>, size: vec2<f32>) -> vec4<f32> {
return vec4<f32>((((position / size) * 2.) - 1.), 1., 1.);
}
@group(0) @binding(0)
var<uniform> screen_size: vec4<f32>;
var<uniform> 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<f32>(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<f32>(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<f32>(1024);
out.position = vertex_pos;

View File

@ -14,8 +14,13 @@
#define SOKOL_LOG_IMPL
// TODO: condition compilation
// #define SOKOL_METAL
#if defined (__APPLE__)
#define SOKOL_METAL
#elif defined (__linux__) || defined (__unix__)
#define SOKOL_GLCORE33
#else
#error "Unsupported platform for shaders"
#endif
#include <sokol/sokol_log.h>
#include <sokol/sokol_gfx.h>
@ -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));

176
src/ui.h
View File

@ -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