Compare commits
	
		
			2 Commits 
		
	
	
		
			b436268ffa
			...
			46dd862512
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 46dd862512 | |
|  | 74e3821233 | 
							
								
								
									
										4
									
								
								justfile
								
								
								
								
							
							
						
						
									
										4
									
								
								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 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
							
								
								
									
										39
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										39
									
								
								src/main.c
								
								
								
								
							|  | @ -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)); | ||||
|  | @ -301,33 +307,36 @@ void ed_init() { | |||
|         }, | ||||
|     }); | ||||
| 
 | ||||
|     queue_text(_String("But what even is text! []!@#$%^&*()_=+"), (float[]){ 0, 0 }); | ||||
|     queue_text(_String("v0.1.0"), (float[]){ 32, 128 }); | ||||
|     queue_text(_String("an_editor - what even"), (float[]){ 32, 256 }); | ||||
|     // queue_text(_String("But what even is text! []!@#$%^&*()_=+"), (float[]){ 0, 0 });
 | ||||
|     // queue_text(_String("v0.1.0"), (float[]){ 32, 128 });
 | ||||
|     // queue_text(_String("an_editor - what even"), (float[]){ 32, 256 });
 | ||||
| 
 | ||||
|     state.ui_cx = init_ui_context(); | ||||
|     state.ui_cx = ui_init_context(); | ||||
| 
 | ||||
|     string label = _String("Number 1"); | ||||
|     ht_set(&state.ui_cx.cached_elements, label, &(ui_element_cache_data) { | ||||
|         .label = label, | ||||
|         .size = { | ||||
|             .axis = UI_AXIS_HORIZONTAL, | ||||
|             .computed_size = { 200, 256 }, | ||||
| } | ||||
|     }); | ||||
| void ed_frame() { | ||||
|     string label = _String("Number 1"); | ||||
|     ui_element(&state.ui_cx, label); | ||||
|     ui_element(&state.ui_cx, _String("ui element 2")); | ||||
|     ui_element(&state.ui_cx, _String("ui element 3")); | ||||
| 
 | ||||
|     ui_compute_layout(&state.ui_cx, 0); | ||||
|     state.ui_cx.frame_elements.data[0].size.computed_size[0] = sapp_width(); | ||||
|     state.ui_cx.frame_elements.data[0].size.computed_size[1] = sapp_height(); | ||||
|     ui_update_cache(&state.ui_cx, 0); | ||||
| 
 | ||||
|     state.gpu_glyphs.size = 0; | ||||
|     for (size_t i = 0; i < state.ui_cx.cached_elements.capacity; ++i) { | ||||
|         if (state.ui_cx.cached_elements.key_slots[i].key.data != NULL) { | ||||
|             string text = state.ui_cx.cached_elements.key_slots[i].key; | ||||
| 
 | ||||
|             ui_element_cache_data *value = ht_get(&state.ui_cx.cached_elements, text); | ||||
|             if (value) { | ||||
|                 queue_text(text, (float[]){ (float)value->size.computed_size[0], (float)value->size.computed_size[1] }); | ||||
|                 queue_text(text, (float[]){ (float)value->size.computed_pos[0], (float)value->size.computed_pos[1] }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void ed_frame() { | ||||
| 
 | ||||
|     if (state.gpu_glyphs.size > 0) { | ||||
|         sg_update_buffer(state.bind.vertex_buffers[1], &(sg_range) { | ||||
|             .ptr = state.gpu_glyphs.data, | ||||
|  |  | |||
							
								
								
									
										245
									
								
								src/ui.h
								
								
								
								
							
							
						
						
									
										245
									
								
								src/ui.h
								
								
								
								
							|  | @ -5,7 +5,12 @@ | |||
| 
 | ||||
| #define MAX_UI_ELEMENTS 2048 | ||||
| 
 | ||||
| #define _elm(index) (&cx->frame_elements.data[index]) | ||||
| // 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,45 @@ 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() { | ||||
| ui_context ui_init_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); | ||||
| 
 | ||||
|     ui_element_frame_data frame_data = (ui_element_frame_data) { | ||||
|         .index = 0, | ||||
|         // TODO: don't just set this to label, because then elements
 | ||||
|         // with the same label can't be created together
 | ||||
|         .key = _String("root"), | ||||
|         .label = _String("root"), | ||||
|         .first = -1, | ||||
|         .last = -1, | ||||
|         .next = -1, | ||||
|         .prev = -1, | ||||
|         .parent = -1, | ||||
|         .size = { | ||||
|             .axis = UI_AXIS_HORIZONTAL, | ||||
|             .computed_size = { 640, 480 }, | ||||
|         } | ||||
|     }; | ||||
|     pushArray(ui_element_frame_data, &frame_elements, frame_data); | ||||
| 
 | ||||
|     return (ui_context) { | ||||
|         .cached_elements = cached_elements, | ||||
|         .frame_elements = frame_elements, | ||||
|         .frame_floating_elements = frame_floating_elements, | ||||
|         .frame_index = 0, | ||||
|     }; | ||||
| } | ||||
|  | @ -120,11 +148,13 @@ 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, | ||||
|         .size.semantic_size[0].type = UI_SEMANTIC_SIZE_FILL, | ||||
|         .size.semantic_size[1].type = UI_SEMANTIC_SIZE_FILL, | ||||
|     }; | ||||
| 
 | ||||
|     // Get cached element data
 | ||||
|  | @ -143,20 +173,215 @@ 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 < SIZE_MAX) { | ||||
|         _prev_ref(frame_data.index)->next = frame_data.index; | ||||
|     } | ||||
|     if (_elm(cx->current_parent)->first == 0) { | ||||
|     if (_elm(cx->current_parent)->first == SIZE_MAX) { | ||||
|         _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 == SIZE_MAX || _parent(element_index) == SIZE_MAX) { | ||||
|         return cx->frame_elements.data[0].size.computed_size[axis]; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     if (child_index < SIZE_MAX) { | ||||
|         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)) < SIZE_MAX); | ||||
|     } | ||||
| 
 | ||||
|     child_index = elm->first; | ||||
|     if (child_index < SIZE_MAX) { | ||||
|         do { | ||||
|             for (size_t axis = 0; axis < 2; ++axis) { | ||||
|                 if (_elm(child_index)->size.semantic_size[axis].type == UI_SEMANTIC_SIZE_FILL) { | ||||
|                     _elm(child_index)->size.computed_size[axis] = (elm_size[axis] - child_size[axis]) / num_fills[axis]; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             ui_compute_layout(cx, child_index); | ||||
|         } while ((child_index = _next(child_index)) < SIZE_MAX); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ui_compute_layout(ui_context *cx, size_t element_index) { | ||||
|     if (element_index == SIZE_MAX) 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) < SIZE_MAX && !_flags(element_index, UI_FLAG_FLOATING)) { | ||||
|         __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) < SIZE_MAX) { | ||||
|         __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 }; | ||||
|     // only compute layout for children of root
 | ||||
|     if (elm->index > 0) { | ||||
|         _ui_compute_simple_layout(cx, elm, axis, 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; | ||||
|         if (child_index < SIZE_MAX) { | ||||
|             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)) < SIZE_MAX); | ||||
|         } | ||||
|     } | ||||
|     if (post_compute[UI_AXIS_VERTICAL]) { | ||||
|         elm->size.computed_size[UI_AXIS_VERTICAL] = 0; | ||||
| 
 | ||||
|         size_t child_index = elm->first; | ||||
|         if (child_index < SIZE_MAX) { | ||||
|             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)) < SIZE_MAX); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ui_update_cache(ui_context *cx, size_t element_index) { | ||||
|     if (element_index == SIZE_MAX) return; | ||||
| 
 | ||||
|     size_t child_index = _elm(element_index)->first; | ||||
|     if (child_index < SIZE_MAX) { | ||||
|         do { | ||||
|             __auto_type child = _elm(child_index); | ||||
| 
 | ||||
|             ht_set(&cx->cached_elements, child->key, &(ui_element_cache_data) { | ||||
|                 .label = child->label, | ||||
|                 .size = { | ||||
|                     .axis = child->size.axis, | ||||
|                     .semantic_size = { child->size.semantic_size[0], child->size.semantic_size[1] }, | ||||
|                     .computed_size = { child->size.computed_size[0], child->size.computed_size[1] }, | ||||
|                     .computed_pos = { child->size.computed_pos[0], child->size.computed_pos[1] }, | ||||
|                 } | ||||
|                 // FIXME: don't mangle last_instantiated_index
 | ||||
|             }); | ||||
| 
 | ||||
|             ui_update_cache(cx, child_index); | ||||
|         } while ((child_index = _next(child_index)) < SIZE_MAX); | ||||
|     } | ||||
| 
 | ||||
|     cx->frame_elements.size = 1; | ||||
|     cx->frame_elements.data[0].first = SIZE_MAX; | ||||
|     cx->frame_elements.data[0].prev = SIZE_MAX; | ||||
|     cx->frame_elements.data[0].next = SIZE_MAX; | ||||
|     cx->frame_elements.data[0].last = SIZE_MAX; | ||||
|     cx->frame_elements.data[0].parent = SIZE_MAX; | ||||
|     cx->current_parent = 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue