fix view not updating with rendered contents
							parent
							
								
									0d8b0b738a
								
							
						
					
					
						commit
						2eaca7a4b9
					
				| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					TabWidth: 4
 | 
				
			||||||
 | 
					IndentWidth: 4
 | 
				
			||||||
 | 
					UseTab: Never
 | 
				
			||||||
							
								
								
									
										210
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										210
									
								
								src/main.c
								
								
								
								
							| 
						 | 
					@ -25,131 +25,151 @@
 | 
				
			||||||
// static Arena *context_arena = &default_arena;
 | 
					// static Arena *context_arena = &default_arena;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *context_alloc(size_t size) {
 | 
					void *context_alloc(size_t size) {
 | 
				
			||||||
  // assert(context_arena);
 | 
					    // assert(context_arena);
 | 
				
			||||||
  // return arena_alloc(context_arena, size);
 | 
					    // return arena_alloc(context_arena, size);
 | 
				
			||||||
  return malloc(size);
 | 
					    return malloc(size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct {
 | 
					static struct {
 | 
				
			||||||
  bool should_exit;
 | 
					    bool should_exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_context ui_cx;
 | 
					    ui_context ui_cx;
 | 
				
			||||||
  gfx_context_t *gfx_cx;
 | 
					    gfx_context_t *gfx_cx;
 | 
				
			||||||
} state;
 | 
					} state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ed_init(_gfx_frame_func frame_func) {
 | 
					void ed_init(_gfx_frame_func frame_func) {
 | 
				
			||||||
  state.gfx_cx = gfx_init_context(frame_func, 640, 480);
 | 
					    state.gfx_cx = gfx_init_context(frame_func, 640, 480);
 | 
				
			||||||
  state.ui_cx = ui_init_context();
 | 
					    state.ui_cx = ui_init_context();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: grab default font from the system
 | 
					    // TODO: grab default font from the system
 | 
				
			||||||
  uint8_t ttf_buffer[1 << 20];
 | 
					    uint8_t ttf_buffer[1 << 20];
 | 
				
			||||||
  assert("failed to load font" &&
 | 
					    assert("failed to load font" &&
 | 
				
			||||||
         load_file(_String("./bin/JetBrainsMono-Medium.ttf"), 1 << 20,
 | 
					           load_file(_String("./bin/JetBrainsMono-Medium.ttf"), 1 << 20,
 | 
				
			||||||
                   ttf_buffer));
 | 
					                     ttf_buffer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  stbtt_fontinfo font;
 | 
					    stbtt_fontinfo font;
 | 
				
			||||||
  stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0));
 | 
					    stbtt_InitFont(&font, ttf_buffer,
 | 
				
			||||||
 | 
					                   stbtt_GetFontOffsetForIndex(ttf_buffer, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const int font_bitmap_size = 512;
 | 
					    const int font_bitmap_size = 512;
 | 
				
			||||||
  const int rasterized_font_height = _FONT_HEIGHT;
 | 
					    const int rasterized_font_height = _FONT_HEIGHT;
 | 
				
			||||||
  uint8_t *font_bitmap =
 | 
					    uint8_t *font_bitmap =
 | 
				
			||||||
      context_alloc(font_bitmap_size * font_bitmap_size * sizeof(uint8_t));
 | 
					        context_alloc(font_bitmap_size * font_bitmap_size * sizeof(uint8_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int ascent, descent, line_gap;
 | 
					    int ascent, descent, line_gap;
 | 
				
			||||||
  float scale = stbtt_ScaleForPixelHeight(&font, rasterized_font_height * 2);
 | 
					    float scale = stbtt_ScaleForPixelHeight(&font, rasterized_font_height * 2);
 | 
				
			||||||
  stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
 | 
					    stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t xx = 0; xx < rasterized_font_height / 2; ++xx) {
 | 
					    for (size_t xx = 0; xx < rasterized_font_height / 2; ++xx) {
 | 
				
			||||||
    for (size_t yy = 0; yy < rasterized_font_height; ++yy) {
 | 
					        for (size_t yy = 0; yy < rasterized_font_height; ++yy) {
 | 
				
			||||||
      font_bitmap[xx + yy * font_bitmap_size] = 0;
 | 
					            font_bitmap[xx + yy * font_bitmap_size] = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					    // manually add glyph for SPACE
 | 
				
			||||||
  // manually add glyph for SPACE
 | 
					 | 
				
			||||||
  pushArray(GpuGlyph, &state.gfx_cx->glyph_cache,
 | 
					 | 
				
			||||||
            ((GpuGlyph){
 | 
					 | 
				
			||||||
                .atlas_position = {0},
 | 
					 | 
				
			||||||
                .size = {rasterized_font_height / 4, 1},
 | 
					 | 
				
			||||||
                .position = {0},
 | 
					 | 
				
			||||||
                .y_offset = -rasterized_font_height,
 | 
					 | 
				
			||||||
            }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  int x = rasterized_font_height / 4;
 | 
					 | 
				
			||||||
  int y = 0;
 | 
					 | 
				
			||||||
  for (size_t i = 33; i < 33 + 96; ++i) {
 | 
					 | 
				
			||||||
    int width, height, xoff, yoff;
 | 
					 | 
				
			||||||
    uint8_t *bitmap = stbtt_GetCodepointBitmap(&font, scale, scale, (int)i,
 | 
					 | 
				
			||||||
                                               &width, &height, &xoff, &yoff);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (x + width >= font_bitmap_size) {
 | 
					 | 
				
			||||||
      x = 0;
 | 
					 | 
				
			||||||
      y += (int)((float)(ascent - descent + line_gap) * scale);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    size_t xxx = x;
 | 
					 | 
				
			||||||
    for (size_t xx = 0; xx < (size_t)width; ++xx) {
 | 
					 | 
				
			||||||
      size_t yyy = y;
 | 
					 | 
				
			||||||
      for (size_t yy = 0; yy < (size_t)height; ++yy) {
 | 
					 | 
				
			||||||
        font_bitmap[xxx + yyy * font_bitmap_size] = bitmap[xx + yy * width];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        yyy += 1;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      xxx += 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pushArray(GpuGlyph, &state.gfx_cx->glyph_cache,
 | 
					    pushArray(GpuGlyph, &state.gfx_cx->glyph_cache,
 | 
				
			||||||
              ((GpuGlyph){
 | 
					              ((GpuGlyph){
 | 
				
			||||||
                  .atlas_position = {(float)(x), (float)(y)},
 | 
					                  .atlas_position = {0},
 | 
				
			||||||
                  .size = {(float)width, (float)height},
 | 
					                  .size = {rasterized_font_height / 4, 1},
 | 
				
			||||||
                  .position = {(float)x, (float)y},
 | 
					                  .position = {0},
 | 
				
			||||||
                  .y_offset = (float)(yoff),
 | 
					                  .y_offset = -rasterized_font_height,
 | 
				
			||||||
              }));
 | 
					              }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    x += width;
 | 
					    int x = rasterized_font_height / 4;
 | 
				
			||||||
  }
 | 
					    int y = 0;
 | 
				
			||||||
 | 
					    for (size_t i = 33; i < 33 + 96; ++i) {
 | 
				
			||||||
 | 
					        int width, height, xoff, yoff;
 | 
				
			||||||
 | 
					        uint8_t *bitmap = stbtt_GetCodepointBitmap(
 | 
				
			||||||
 | 
					            &font, scale, scale, (int)i, &width, &height, &xoff, &yoff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gfx_push_texture_buffer(
 | 
					        if (x + width >= font_bitmap_size) {
 | 
				
			||||||
      state.gfx_cx, font_bitmap_size, font_bitmap_size, font_bitmap,
 | 
					            x = 0;
 | 
				
			||||||
      font_bitmap_size * font_bitmap_size * sizeof(uint8_t));
 | 
					            y += (int)((float)(ascent - descent + line_gap) * scale);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size_t xxx = x;
 | 
				
			||||||
 | 
					        for (size_t xx = 0; xx < (size_t)width; ++xx) {
 | 
				
			||||||
 | 
					            size_t yyy = y;
 | 
				
			||||||
 | 
					            for (size_t yy = 0; yy < (size_t)height; ++yy) {
 | 
				
			||||||
 | 
					                font_bitmap[xxx + yyy * font_bitmap_size] =
 | 
				
			||||||
 | 
					                    bitmap[xx + yy * width];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                yyy += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            xxx += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pushArray(GpuGlyph, &state.gfx_cx->glyph_cache,
 | 
				
			||||||
 | 
					                  ((GpuGlyph){
 | 
				
			||||||
 | 
					                      .atlas_position = {(float)(x), (float)(y)},
 | 
				
			||||||
 | 
					                      .size = {(float)width, (float)height},
 | 
				
			||||||
 | 
					                      .position = {(float)x, (float)y},
 | 
				
			||||||
 | 
					                      .y_offset = (float)(yoff),
 | 
				
			||||||
 | 
					                  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        x += width;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gfx_push_texture_buffer(
 | 
				
			||||||
 | 
					        state.gfx_cx, font_bitmap_size, font_bitmap_size, font_bitmap,
 | 
				
			||||||
 | 
					        font_bitmap_size * font_bitmap_size * sizeof(uint8_t));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void render_ui_text(string text, float position[2]) {
 | 
					void render_ui_text(string text, float position[2]) {
 | 
				
			||||||
  gfx_queue_text(state.gfx_cx, text, position);
 | 
					    gfx_queue_text(state.gfx_cx, text, position);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void render_ui_rect(float position[2], float size[2], float color[4]) {
 | 
					void render_ui_rect(float position[2], float size[2], float color[4]) {
 | 
				
			||||||
  gfx_queue_ui_rect(state.gfx_cx, position, size, 0, color);
 | 
					    gfx_queue_ui_rect(state.gfx_cx, position, size, 0, color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ed_frame() {
 | 
					void ed_frame(int mouse_x, int mouse_y) {
 | 
				
			||||||
  state.ui_cx.frame_elements.data[0].size.computed_size[0] =
 | 
					    state.ui_cx.frame_elements.data[0].size.computed_size[0] =
 | 
				
			||||||
      state.gfx_cx->frame_width;
 | 
					        state.gfx_cx->frame_width;
 | 
				
			||||||
  state.ui_cx.frame_elements.data[0].size.computed_size[1] =
 | 
					    state.ui_cx.frame_elements.data[0].size.computed_size[1] =
 | 
				
			||||||
      state.gfx_cx->frame_height;
 | 
					        state.gfx_cx->frame_height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_element(&state.ui_cx, _String("channel sidebar"), UI_AXIS_VERTICAL,
 | 
					    uint8_t buffer[256] = {};
 | 
				
			||||||
             ui_make_size(ui_children_sum, ui_fill), UI_FLAG_DRAW_BACKGROUND);
 | 
					    snprintf(buffer, 256, "Mouse X: %d, Mouse Y: %d, Mouse %s", mouse_x,
 | 
				
			||||||
  ui_push_parent(&state.ui_cx);
 | 
					             mouse_y, state.ui_cx.input.mouse_left_down ? "Down" : "Up");
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    ui_element(&state.ui_cx, _String("#dev-general"), UI_AXIS_HORIZONTAL,
 | 
					 | 
				
			||||||
               ui_make_size(ui_fit_text, ui_fit_text),
 | 
					 | 
				
			||||||
               UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_TEXT);
 | 
					 | 
				
			||||||
    ui_element(&state.ui_cx, _String("#dev-help"), UI_AXIS_HORIZONTAL,
 | 
					 | 
				
			||||||
               ui_make_size(ui_fit_text, ui_fit_text),
 | 
					 | 
				
			||||||
               UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_TEXT);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ui_pop_parent(&state.ui_cx);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_compute_layout(&state.ui_cx, 0);
 | 
					    render_ui_rect((float[2]){mouse_x, mouse_y}, (float[2]){32, 32},
 | 
				
			||||||
  ui_render(&state.ui_cx, render_ui_text, render_ui_rect);
 | 
					                   (float[4]){1, 1, 1, 1});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_update_cache(&state.ui_cx, 0);
 | 
					    ui_element(&state.ui_cx, _String("channel sidebar"), UI_AXIS_VERTICAL,
 | 
				
			||||||
  ui_prune(&state.ui_cx);
 | 
					               ui_make_size(ui_children_sum, ui_fill), UI_FLAG_DRAW_BACKGROUND);
 | 
				
			||||||
 | 
					    ui_push_parent(&state.ui_cx);
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ui_element(&state.ui_cx, _String(buffer), UI_AXIS_HORIZONTAL,
 | 
				
			||||||
 | 
					                   ui_make_size(ui_fit_text, ui_fit_text),
 | 
				
			||||||
 | 
					                   UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_TEXT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ui_element(&state.ui_cx, _String("#dev-general"), UI_AXIS_HORIZONTAL,
 | 
				
			||||||
 | 
					                   ui_make_size(ui_fit_text, ui_fit_text),
 | 
				
			||||||
 | 
					                   UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_TEXT);
 | 
				
			||||||
 | 
					        ui_element(&state.ui_cx, _String("#dev-help"), UI_AXIS_HORIZONTAL,
 | 
				
			||||||
 | 
					                   ui_make_size(ui_fit_text, ui_fit_text),
 | 
				
			||||||
 | 
					                   UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_TEXT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ui_pop_parent(&state.ui_cx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui_compute_layout(&state.ui_cx, 0);
 | 
				
			||||||
 | 
					    ui_render(&state.ui_cx, render_ui_text, render_ui_rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui_update_cache(&state.ui_cx, 0);
 | 
				
			||||||
 | 
					    ui_prune(&state.ui_cx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui_update_input(&state.ui_cx, (ui_context_input){
 | 
				
			||||||
 | 
					                                      .mouse_x = mouse_x,
 | 
				
			||||||
 | 
					                                      .mouse_y = mouse_y,
 | 
				
			||||||
 | 
					                                      .mouse_left_down = false,
 | 
				
			||||||
 | 
					                                      .mouse_right_down = false,
 | 
				
			||||||
 | 
					                                  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[]) {
 | 
					int main(int argc, char *argv[]) {
 | 
				
			||||||
  ed_init(ed_frame);
 | 
					    ed_init(ed_frame);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  while (keep_running) {
 | 
					    while (keep_running) {
 | 
				
			||||||
    gfx_run_events(state.gfx_cx);
 | 
					        gfx_run_events(state.gfx_cx);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/string.h
								
								
								
								
							
							
						
						
									
										16
									
								
								src/string.h
								
								
								
								
							| 
						 | 
					@ -1,12 +1,13 @@
 | 
				
			||||||
#ifndef ED_STRING_INCLUDED
 | 
					#ifndef ED_STRING_INCLUDED
 | 
				
			||||||
#define ED_STRING_INCLUDED
 | 
					#define ED_STRING_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <stddef.h>
 | 
					#include <stddef.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text), .owned = false })
 | 
					#define _String(text)                                                          \
 | 
				
			||||||
 | 
					    ((string){.data = (uint8_t *)text, .len = sizeof(text), .owned = false})
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    uint8_t *data;
 | 
					    uint8_t *data;
 | 
				
			||||||
    size_t len;
 | 
					    size_t len;
 | 
				
			||||||
| 
						 | 
					@ -14,13 +15,14 @@ typedef struct {
 | 
				
			||||||
    bool owned;
 | 
					    bool owned;
 | 
				
			||||||
} string;
 | 
					} string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef ED_STRING_IMPLEMENTATION
 | 
					#ifdef ED_STRING_IMPLEMENTATION
 | 
				
			||||||
bool string_eq(string a, string b) {
 | 
					bool string_eq(string a, string b) {
 | 
				
			||||||
    if (a.len != b.len) return false;
 | 
					    if (a.len != b.len)
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (size_t i=0; i<a.len; ++i) {
 | 
					    for (size_t i = 0; i < a.len; ++i) {
 | 
				
			||||||
        if (a.data[i] != b.data[i]) return false;
 | 
					        if (a.data[i] != b.data[i])
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										716
									
								
								src/ui.h
								
								
								
								
							
							
						
						
									
										716
									
								
								src/ui.h
								
								
								
								
							| 
						 | 
					@ -33,88 +33,105 @@
 | 
				
			||||||
#include "ht.h"
 | 
					#include "ht.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
  UI_AXIS_HORIZONTAL,
 | 
					    UI_AXIS_HORIZONTAL,
 | 
				
			||||||
  UI_AXIS_VERTICAL,
 | 
					    UI_AXIS_VERTICAL,
 | 
				
			||||||
} ui_axis;
 | 
					} ui_axis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
  UI_SEMANTIC_SIZE_FIT_TEXT,
 | 
					    UI_SEMANTIC_SIZE_FIT_TEXT,
 | 
				
			||||||
  UI_SEMANTIC_SIZE_CHILDREN_SUM,
 | 
					    UI_SEMANTIC_SIZE_CHILDREN_SUM,
 | 
				
			||||||
  UI_SEMANTIC_SIZE_FILL,
 | 
					    UI_SEMANTIC_SIZE_FILL,
 | 
				
			||||||
  UI_SEMANTIC_SIZE_EXACT,
 | 
					    UI_SEMANTIC_SIZE_EXACT,
 | 
				
			||||||
  UI_SEMANTIC_SIZE_PERCENT_OF_PARENT,
 | 
					    UI_SEMANTIC_SIZE_PERCENT_OF_PARENT,
 | 
				
			||||||
} ui_semantic_size_t;
 | 
					} ui_semantic_size_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  ui_semantic_size_t type;
 | 
					    ui_semantic_size_t type;
 | 
				
			||||||
  union {
 | 
					    union {
 | 
				
			||||||
    uint32_t integer;
 | 
					        uint32_t integer;
 | 
				
			||||||
  };
 | 
					    };
 | 
				
			||||||
} ui_semantic_size;
 | 
					} ui_semantic_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ui_make_size(horizontal, vertical)                                     \
 | 
					#define ui_make_size(horizontal, vertical)                                     \
 | 
				
			||||||
  ((ui_semantic_size[2]){horizontal, vertical})
 | 
					    ((ui_semantic_size[2]){horizontal, vertical})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ui_fit_text ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_FIT_TEXT})
 | 
					#define ui_fit_text ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_FIT_TEXT})
 | 
				
			||||||
#define ui_fill ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_FILL})
 | 
					#define ui_fill ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_FILL})
 | 
				
			||||||
#define ui_children_sum                                                        \
 | 
					#define ui_children_sum                                                        \
 | 
				
			||||||
  ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_CHILDREN_SUM})
 | 
					    ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_CHILDREN_SUM})
 | 
				
			||||||
#define ui_exact(value)                                                        \
 | 
					#define ui_exact(value)                                                        \
 | 
				
			||||||
  ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_EXACT, .integer = value})
 | 
					    ((ui_semantic_size){.type = UI_SEMANTIC_SIZE_EXACT, .integer = value})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  ui_axis axis;
 | 
					    ui_axis axis;
 | 
				
			||||||
  ui_semantic_size semantic_size[2];
 | 
					    ui_semantic_size semantic_size[2];
 | 
				
			||||||
  uint32_t computed_size[2];
 | 
					    uint32_t computed_size[2];
 | 
				
			||||||
  uint32_t computed_pos[2];
 | 
					    uint32_t computed_pos[2];
 | 
				
			||||||
} ui_size;
 | 
					} ui_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    bool hovering;
 | 
				
			||||||
 | 
					    bool clicked;
 | 
				
			||||||
 | 
					    bool dragging;
 | 
				
			||||||
 | 
					} ui_interaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UI Element data persisted across frames
 | 
					// UI Element data persisted across frames
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  string label;
 | 
					    string label;
 | 
				
			||||||
  ui_size size;
 | 
					    ui_size size;
 | 
				
			||||||
  size_t last_instantiated_index;
 | 
					    size_t last_instantiated_index;
 | 
				
			||||||
} ui_element_cache_data;
 | 
					} ui_element_cache_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
  UI_FLAG_CLICKABLE = 0b000000001,
 | 
					    UI_FLAG_CLICKABLE = 0b000000001,
 | 
				
			||||||
  UI_FLAG_HOVERABLE = 0b000000010,
 | 
					    UI_FLAG_HOVERABLE = 0b000000010,
 | 
				
			||||||
  UI_FLAG_SCROLLABLE = 0b000000100,
 | 
					    UI_FLAG_SCROLLABLE = 0b000000100,
 | 
				
			||||||
  UI_FLAG_DRAW_TEXT = 0b000001000,
 | 
					    UI_FLAG_DRAW_TEXT = 0b000001000,
 | 
				
			||||||
  UI_FLAG_DRAW_BORDER = 0b000010000,
 | 
					    UI_FLAG_DRAW_BORDER = 0b000010000,
 | 
				
			||||||
  UI_FLAG_DRAW_BACKGROUND = 0b000100000,
 | 
					    UI_FLAG_DRAW_BACKGROUND = 0b000100000,
 | 
				
			||||||
  UI_FLAG_ROUNDED_BORDER = 0b001000000,
 | 
					    UI_FLAG_ROUNDED_BORDER = 0b001000000,
 | 
				
			||||||
  UI_FLAG_FLOATING = 0b010000000,
 | 
					    UI_FLAG_FLOATING = 0b010000000,
 | 
				
			||||||
  UI_FLAG_CUSTOM_DRAW_FUNC = 0b100000000,
 | 
					    UI_FLAG_CUSTOM_DRAW_FUNC = 0b100000000,
 | 
				
			||||||
} ui_flags;
 | 
					} ui_flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Ephemeral frame only UI Element data
 | 
					// Ephemeral frame only UI Element data
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  size_t index;
 | 
					    size_t index;
 | 
				
			||||||
  string key;
 | 
					    string key;
 | 
				
			||||||
  string label;
 | 
					    string label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_size size;
 | 
					    ui_size size;
 | 
				
			||||||
  ui_flags flags;
 | 
					    ui_flags flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // optional types
 | 
					    // optional types
 | 
				
			||||||
  size_t first;
 | 
					    size_t first;
 | 
				
			||||||
  size_t last;
 | 
					    size_t last;
 | 
				
			||||||
  size_t next;
 | 
					    size_t next;
 | 
				
			||||||
  size_t prev;
 | 
					    size_t prev;
 | 
				
			||||||
  size_t parent;
 | 
					    size_t parent;
 | 
				
			||||||
} ui_element_frame_data;
 | 
					} ui_element_frame_data;
 | 
				
			||||||
arrayTemplate(ui_element_frame_data);
 | 
					arrayTemplate(ui_element_frame_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  ed_ht cached_elements;
 | 
					    bool mouse_left_down;
 | 
				
			||||||
  array(ui_element_frame_data) frame_elements;
 | 
					    bool mouse_right_down;
 | 
				
			||||||
  array(ui_element_frame_data) frame_floating_elements;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t frame_index;
 | 
					    uint32_t mouse_x;
 | 
				
			||||||
  uint32_t canvas_size[2];
 | 
					    uint32_t mouse_y;
 | 
				
			||||||
 | 
					} ui_context_input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t current_parent;
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    ed_ht cached_elements;
 | 
				
			||||||
 | 
					    array(ui_element_frame_data) frame_elements;
 | 
				
			||||||
 | 
					    array(ui_element_frame_data) frame_floating_elements;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ui_context_input input;
 | 
				
			||||||
 | 
					    ui_context_input last_input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t frame_index;
 | 
				
			||||||
 | 
					    uint32_t canvas_size[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t current_parent;
 | 
				
			||||||
} ui_context;
 | 
					} ui_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_compute_layout(ui_context *cx, size_t element_index);
 | 
					void ui_compute_layout(ui_context *cx, size_t element_index);
 | 
				
			||||||
| 
						 | 
					@ -122,326 +139,326 @@ void ui_compute_layout(ui_context *cx, size_t element_index);
 | 
				
			||||||
#ifdef ED_UI_IMPLEMENTATION
 | 
					#ifdef ED_UI_IMPLEMENTATION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ui_context ui_init_context() {
 | 
					ui_context ui_init_context() {
 | 
				
			||||||
  ed_ht cached_elements =
 | 
					    ed_ht cached_elements =
 | 
				
			||||||
      ht_create(MAX_UI_ELEMENTS, sizeof(ui_element_cache_data));
 | 
					        ht_create(MAX_UI_ELEMENTS, sizeof(ui_element_cache_data));
 | 
				
			||||||
  array(ui_element_frame_data) frame_elements =
 | 
					    array(ui_element_frame_data) frame_elements =
 | 
				
			||||||
      newArray(ui_element_frame_data, MAX_UI_ELEMENTS);
 | 
					        newArray(ui_element_frame_data, MAX_UI_ELEMENTS);
 | 
				
			||||||
  array(ui_element_frame_data) frame_floating_elements =
 | 
					    array(ui_element_frame_data) frame_floating_elements =
 | 
				
			||||||
      newArray(ui_element_frame_data, MAX_UI_ELEMENTS);
 | 
					        newArray(ui_element_frame_data, MAX_UI_ELEMENTS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_element_frame_data frame_data = (ui_element_frame_data){
 | 
					    ui_element_frame_data frame_data = (ui_element_frame_data){
 | 
				
			||||||
      .index = 0,
 | 
					        .index = 0,
 | 
				
			||||||
      // TODO: don't just set this to label, because then elements
 | 
					        // TODO: don't just set this to label, because then elements
 | 
				
			||||||
      // with the same label can't be created together
 | 
					        // with the same label can't be created together
 | 
				
			||||||
      .key = _String("root"),
 | 
					        .key = _String("root"),
 | 
				
			||||||
      .label = _String("root"),
 | 
					        .label = _String("root"),
 | 
				
			||||||
      .first = -1,
 | 
					        .first = -1,
 | 
				
			||||||
      .last = -1,
 | 
					        .last = -1,
 | 
				
			||||||
      .next = -1,
 | 
					        .next = -1,
 | 
				
			||||||
      .prev = -1,
 | 
					        .prev = -1,
 | 
				
			||||||
      .parent = -1,
 | 
					        .parent = -1,
 | 
				
			||||||
      .size = {
 | 
					        .size = {
 | 
				
			||||||
          .axis = UI_AXIS_HORIZONTAL,
 | 
					            .axis = UI_AXIS_HORIZONTAL,
 | 
				
			||||||
          .computed_size = {640, 480},
 | 
					            .computed_size = {640, 480},
 | 
				
			||||||
      }};
 | 
					        }};
 | 
				
			||||||
  pushArray(ui_element_frame_data, &frame_elements, frame_data);
 | 
					    pushArray(ui_element_frame_data, &frame_elements, frame_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (ui_context){
 | 
					    return (ui_context){
 | 
				
			||||||
      .cached_elements = cached_elements,
 | 
					        .cached_elements = cached_elements,
 | 
				
			||||||
      .frame_elements = frame_elements,
 | 
					        .frame_elements = frame_elements,
 | 
				
			||||||
      .frame_floating_elements = frame_floating_elements,
 | 
					        .frame_floating_elements = frame_floating_elements,
 | 
				
			||||||
      .frame_index = 0,
 | 
					        .frame_index = 0,
 | 
				
			||||||
  };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_push_parent(ui_context *cx) {
 | 
					void ui_push_parent(ui_context *cx) {
 | 
				
			||||||
  if (cx->frame_elements.size > 0) {
 | 
					    if (cx->frame_elements.size > 0) {
 | 
				
			||||||
    cx->current_parent = cx->frame_elements.size - 1;
 | 
					        cx->current_parent = cx->frame_elements.size - 1;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_pop_parent(ui_context *cx) {
 | 
					void ui_pop_parent(ui_context *cx) {
 | 
				
			||||||
  if (_parent(cx->current_parent) < SIZE_MAX) {
 | 
					    if (_parent(cx->current_parent) < SIZE_MAX) {
 | 
				
			||||||
    cx->current_parent = _parent(cx->current_parent);
 | 
					        cx->current_parent = _parent(cx->current_parent);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t ui_element(ui_context *cx, string label, ui_axis axis,
 | 
					size_t ui_element(ui_context *cx, string label, ui_axis axis,
 | 
				
			||||||
                  ui_semantic_size size[2], ui_flags flags) {
 | 
					                  ui_semantic_size size[2], ui_flags flags) {
 | 
				
			||||||
  ui_element_frame_data frame_data = (ui_element_frame_data){
 | 
					    ui_element_frame_data frame_data = (ui_element_frame_data){
 | 
				
			||||||
      .index = cx->frame_elements.size,
 | 
					        .index = cx->frame_elements.size,
 | 
				
			||||||
      // TODO: don't just set this to label, because then elements
 | 
					        // TODO: don't just set this to label, because then elements
 | 
				
			||||||
      // with the same label can't be created together
 | 
					        // with the same label can't be created together
 | 
				
			||||||
      .key = label,
 | 
					        .key = label,
 | 
				
			||||||
      .label = label,
 | 
					        .label = label,
 | 
				
			||||||
      .first = -1,
 | 
					        .first = -1,
 | 
				
			||||||
      .last = -1,
 | 
					        .last = -1,
 | 
				
			||||||
      .next = -1,
 | 
					        .next = -1,
 | 
				
			||||||
      .prev = cx->frame_elements.data[cx->current_parent].last,
 | 
					        .prev = cx->frame_elements.data[cx->current_parent].last,
 | 
				
			||||||
      .parent = cx->current_parent,
 | 
					        .parent = cx->current_parent,
 | 
				
			||||||
      .size.axis = axis,
 | 
					        .size.axis = axis,
 | 
				
			||||||
      .size.semantic_size[0] = size[0],
 | 
					        .size.semantic_size[0] = size[0],
 | 
				
			||||||
      .size.semantic_size[1] = size[1],
 | 
					        .size.semantic_size[1] = size[1],
 | 
				
			||||||
      .flags = flags,
 | 
					        .flags = flags,
 | 
				
			||||||
  };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Get cached element data
 | 
					    // Get cached element data
 | 
				
			||||||
  ui_element_cache_data *cache_data = ht_get(&cx->cached_elements, label);
 | 
					    ui_element_cache_data *cache_data = ht_get(&cx->cached_elements, label);
 | 
				
			||||||
  if (cache_data) {
 | 
					    if (cache_data) {
 | 
				
			||||||
    cache_data->last_instantiated_index = cx->frame_index;
 | 
					        cache_data->last_instantiated_index = cx->frame_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    frame_data.size.computed_pos[0] = cache_data->size.computed_pos[0];
 | 
					        frame_data.size.computed_pos[0] = cache_data->size.computed_pos[0];
 | 
				
			||||||
    frame_data.size.computed_pos[1] = cache_data->size.computed_pos[1];
 | 
					        frame_data.size.computed_pos[1] = cache_data->size.computed_pos[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    frame_data.size.computed_size[0] = cache_data->size.computed_size[0];
 | 
					        frame_data.size.computed_size[0] = cache_data->size.computed_size[0];
 | 
				
			||||||
    frame_data.size.computed_size[1] = cache_data->size.computed_size[1];
 | 
					        frame_data.size.computed_size[1] = cache_data->size.computed_size[1];
 | 
				
			||||||
  } else {
 | 
					    } else {
 | 
				
			||||||
    bool did_insert = ht_set(&cx->cached_elements, label,
 | 
					        bool did_insert = ht_set(&cx->cached_elements, label,
 | 
				
			||||||
                             &(ui_element_cache_data){
 | 
					                                 &(ui_element_cache_data){
 | 
				
			||||||
                                 .label = label,
 | 
					                                     .label = label,
 | 
				
			||||||
                                 .size = {0},
 | 
					                                     .size = {0},
 | 
				
			||||||
                                 .last_instantiated_index = cx->frame_index,
 | 
					                                     .last_instantiated_index = cx->frame_index,
 | 
				
			||||||
                             });
 | 
					                                 });
 | 
				
			||||||
    assert("couldn't insert into ui element cache" && did_insert);
 | 
					        assert("couldn't insert into ui element cache" && did_insert);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pushArray(ui_element_frame_data, &cx->frame_elements, frame_data);
 | 
					    pushArray(ui_element_frame_data, &cx->frame_elements, frame_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (frame_data.prev < SIZE_MAX) {
 | 
					    if (frame_data.prev < SIZE_MAX) {
 | 
				
			||||||
    _prev_ref(frame_data.index)->next = frame_data.index;
 | 
					        _prev_ref(frame_data.index)->next = frame_data.index;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
  if (_elm(cx->current_parent)->first == SIZE_MAX) {
 | 
					    if (_elm(cx->current_parent)->first == SIZE_MAX) {
 | 
				
			||||||
    _elm(cx->current_parent)->first = frame_data.index;
 | 
					        _elm(cx->current_parent)->first = frame_data.index;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
  _elm(cx->current_parent)->last = frame_data.index;
 | 
					    _elm(cx->current_parent)->last = frame_data.index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return frame_data.index;
 | 
					    return frame_data.index;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint32_t _ui_ancestor_size(ui_context *cx, size_t element_index,
 | 
					static uint32_t _ui_ancestor_size(ui_context *cx, size_t element_index,
 | 
				
			||||||
                                  ui_axis axis) {
 | 
					                                  ui_axis axis) {
 | 
				
			||||||
  if (element_index == SIZE_MAX || _parent(element_index) == SIZE_MAX) {
 | 
					    if (element_index == SIZE_MAX || _parent(element_index) == SIZE_MAX) {
 | 
				
			||||||
    return cx->frame_elements.data[0].size.computed_size[axis];
 | 
					        return cx->frame_elements.data[0].size.computed_size[axis];
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  switch (_parent_ref(element_index)->size.semantic_size[axis].type) {
 | 
					    switch (_parent_ref(element_index)->size.semantic_size[axis].type) {
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_FIT_TEXT:
 | 
					    case UI_SEMANTIC_SIZE_FIT_TEXT:
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_FILL:
 | 
					    case UI_SEMANTIC_SIZE_FILL:
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_EXACT:
 | 
					    case UI_SEMANTIC_SIZE_EXACT:
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_PERCENT_OF_PARENT:
 | 
					    case UI_SEMANTIC_SIZE_PERCENT_OF_PARENT:
 | 
				
			||||||
    return _parent_ref(element_index)->size.computed_size[axis];
 | 
					        return _parent_ref(element_index)->size.computed_size[axis];
 | 
				
			||||||
    break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_CHILDREN_SUM:
 | 
					    case UI_SEMANTIC_SIZE_CHILDREN_SUM:
 | 
				
			||||||
    return _ui_ancestor_size(cx, _parent(element_index), axis);
 | 
					        return _ui_ancestor_size(cx, _parent(element_index), axis);
 | 
				
			||||||
    break;
 | 
					        break;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _ui_compute_simple_layout(ui_context *cx,
 | 
					static void _ui_compute_simple_layout(ui_context *cx,
 | 
				
			||||||
                                      ui_element_frame_data *elm, ui_axis axis,
 | 
					                                      ui_element_frame_data *elm, ui_axis axis,
 | 
				
			||||||
                                      bool *post_compute) {
 | 
					                                      bool *post_compute) {
 | 
				
			||||||
  switch (elm->size.semantic_size[axis].type) {
 | 
					    switch (elm->size.semantic_size[axis].type) {
 | 
				
			||||||
  case UI_SEMANTIC_SIZE_FIT_TEXT:
 | 
					    case UI_SEMANTIC_SIZE_FIT_TEXT:
 | 
				
			||||||
    if (axis == UI_AXIS_HORIZONTAL) {
 | 
					        if (axis == UI_AXIS_HORIZONTAL) {
 | 
				
			||||||
      elm->size.computed_size[axis] = elm->label.len * _FONT_WIDTH;
 | 
					            elm->size.computed_size[axis] = elm->label.len * _FONT_WIDTH;
 | 
				
			||||||
    } else if (axis == UI_AXIS_VERTICAL) {
 | 
					        } else if (axis == UI_AXIS_VERTICAL) {
 | 
				
			||||||
      elm->size.computed_size[axis] = _FONT_HEIGHT;
 | 
					            elm->size.computed_size[axis] = _FONT_HEIGHT;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    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,
 | 
					static void _ui_compute_children_layout(ui_context *cx,
 | 
				
			||||||
                                        ui_element_frame_data *elm) {
 | 
					                                        ui_element_frame_data *elm) {
 | 
				
			||||||
  uint32_t child_size[2] = {0, 0};
 | 
					    uint32_t child_size[2] = {0, 0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // NOTE: the number of fills for the opposite axis of this box needs to be 1
 | 
					    // 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
 | 
					    // because it will never get incremented in the loop below and cause a
 | 
				
			||||||
  // by zero and the number of fills for the axis of the box needs to start at
 | 
					    // divide by zero and the number of fills for the axis of the box needs to
 | 
				
			||||||
  // zero or else it will be n+1 causing incorrect sizes
 | 
					    // start at zero or else it will be n+1 causing incorrect sizes
 | 
				
			||||||
  uint32_t num_fills[2] = {1, 1};
 | 
					    uint32_t num_fills[2] = {1, 1};
 | 
				
			||||||
  num_fills[elm->size.axis] = 0;
 | 
					    num_fills[elm->size.axis] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: maybe just use the actual data instead of copying?
 | 
					    // TODO: maybe just use the actual data instead of copying?
 | 
				
			||||||
  uint32_t elm_size[2] = {elm->size.computed_size[0],
 | 
					    uint32_t elm_size[2] = {elm->size.computed_size[0],
 | 
				
			||||||
                          elm->size.computed_size[1]};
 | 
					                            elm->size.computed_size[1]};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t child_index = elm->first;
 | 
					    size_t child_index = elm->first;
 | 
				
			||||||
  if (child_index < SIZE_MAX) {
 | 
					    if (child_index < SIZE_MAX) {
 | 
				
			||||||
    do {
 | 
					        do {
 | 
				
			||||||
      ui_compute_layout(cx, child_index);
 | 
					            ui_compute_layout(cx, child_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (_elm(child_index)->size.semantic_size[elm->size.axis].type ==
 | 
					            if (_elm(child_index)->size.semantic_size[elm->size.axis].type ==
 | 
				
			||||||
          UI_SEMANTIC_SIZE_FILL) {
 | 
					                UI_SEMANTIC_SIZE_FILL) {
 | 
				
			||||||
        num_fills[elm->size.axis] += 1;
 | 
					                num_fills[elm->size.axis] += 1;
 | 
				
			||||||
      } else {
 | 
					            } else {
 | 
				
			||||||
        child_size[elm->size.axis] +=
 | 
					                child_size[elm->size.axis] +=
 | 
				
			||||||
            _elm(child_index)->size.computed_size[elm->size.axis];
 | 
					                    _elm(child_index)->size.computed_size[elm->size.axis];
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
    } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
					        } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  child_index = elm->first;
 | 
					    child_index = elm->first;
 | 
				
			||||||
  if (child_index < SIZE_MAX) {
 | 
					    if (child_index < SIZE_MAX) {
 | 
				
			||||||
    do {
 | 
					        do {
 | 
				
			||||||
      for (size_t axis = 0; axis < 2; ++axis) {
 | 
					            for (size_t axis = 0; axis < 2; ++axis) {
 | 
				
			||||||
        if (_elm(child_index)->size.semantic_size[axis].type ==
 | 
					                if (_elm(child_index)->size.semantic_size[axis].type ==
 | 
				
			||||||
            UI_SEMANTIC_SIZE_FILL) {
 | 
					                    UI_SEMANTIC_SIZE_FILL) {
 | 
				
			||||||
          _elm(child_index)->size.computed_size[axis] =
 | 
					                    _elm(child_index)->size.computed_size[axis] =
 | 
				
			||||||
              (elm_size[axis] - child_size[axis]) / num_fills[axis];
 | 
					                        (elm_size[axis] - child_size[axis]) / num_fills[axis];
 | 
				
			||||||
        }
 | 
					                }
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ui_compute_layout(cx, child_index);
 | 
					            ui_compute_layout(cx, child_index);
 | 
				
			||||||
    } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
					        } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_compute_layout(ui_context *cx, size_t element_index) {
 | 
					void ui_compute_layout(ui_context *cx, size_t element_index) {
 | 
				
			||||||
  if (element_index == SIZE_MAX)
 | 
					    if (element_index == SIZE_MAX)
 | 
				
			||||||
    return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ui_axis axis = UI_AXIS_HORIZONTAL;
 | 
					    ui_axis axis = UI_AXIS_HORIZONTAL;
 | 
				
			||||||
  __auto_type elm = _elm(element_index);
 | 
					    __auto_type elm = _elm(element_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (_parent(element_index) < SIZE_MAX &&
 | 
					    if (_parent(element_index) < SIZE_MAX &&
 | 
				
			||||||
      !_flags(element_index, UI_FLAG_FLOATING)) {
 | 
					        !_flags(element_index, UI_FLAG_FLOATING)) {
 | 
				
			||||||
    __auto_type parent = _parent_ref(element_index);
 | 
					        __auto_type parent = _parent_ref(element_index);
 | 
				
			||||||
    axis = parent->size.axis;
 | 
					        axis = parent->size.axis;
 | 
				
			||||||
    elm->size.computed_pos[0] = parent->size.computed_pos[0];
 | 
					        elm->size.computed_pos[0] = parent->size.computed_pos[0];
 | 
				
			||||||
    elm->size.computed_pos[1] = parent->size.computed_pos[1];
 | 
					        elm->size.computed_pos[1] = parent->size.computed_pos[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: implement scrolling
 | 
					        // TODO: implement scrolling
 | 
				
			||||||
    // elm->size.computed_pos[axis] += parent.scroll_offset;
 | 
					        // 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};
 | 
					    if (!_flags(element_index, UI_FLAG_FLOATING) &&
 | 
				
			||||||
  // only compute layout for children of root
 | 
					        _prev(element_index) < SIZE_MAX) {
 | 
				
			||||||
  if (elm->index > 0) {
 | 
					        __auto_type prev = _prev_ref(element_index);
 | 
				
			||||||
    for (int i = 0; i < 2; ++i) {
 | 
					 | 
				
			||||||
      _ui_compute_simple_layout(cx, elm, i, post_compute);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  _ui_compute_children_layout(cx, elm);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // NOTE(pcleavelin): the only difference between these two blocks is the
 | 
					        if (prev >= 0) {
 | 
				
			||||||
  // ordering of the switch block they can probably be merged
 | 
					            elm->size.computed_pos[axis] =
 | 
				
			||||||
  if (post_compute[UI_AXIS_HORIZONTAL]) {
 | 
					                prev->size.computed_pos[axis] + prev->size.computed_size[axis];
 | 
				
			||||||
    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;
 | 
					    bool post_compute[2] = {false, false};
 | 
				
			||||||
    if (child_index < SIZE_MAX) {
 | 
					    // only compute layout for children of root
 | 
				
			||||||
      do {
 | 
					    if (elm->index > 0) {
 | 
				
			||||||
        __auto_type child = _elm(child_index);
 | 
					        for (int i = 0; i < 2; ++i) {
 | 
				
			||||||
 | 
					            _ui_compute_simple_layout(cx, elm, i, post_compute);
 | 
				
			||||||
        switch (elm->size.axis) {
 | 
					        }
 | 
				
			||||||
        case UI_AXIS_HORIZONTAL:
 | 
					    }
 | 
				
			||||||
          if (child->size.computed_size[UI_AXIS_VERTICAL] >
 | 
					    _ui_compute_children_layout(cx, elm);
 | 
				
			||||||
              elm->size.computed_size[UI_AXIS_VERTICAL]) {
 | 
					
 | 
				
			||||||
            elm->size.computed_size[UI_AXIS_VERTICAL] =
 | 
					    // NOTE(pcleavelin): the only difference between these two blocks is the
 | 
				
			||||||
                child->size.computed_size[UI_AXIS_VERTICAL];
 | 
					    // ordering of the switch block they can probably be merged
 | 
				
			||||||
          }
 | 
					    if (post_compute[UI_AXIS_HORIZONTAL]) {
 | 
				
			||||||
          break;
 | 
					        elm->size.computed_size[UI_AXIS_HORIZONTAL] = 0;
 | 
				
			||||||
        case UI_AXIS_VERTICAL:
 | 
					
 | 
				
			||||||
          elm->size.computed_size[UI_AXIS_VERTICAL] +=
 | 
					        size_t child_index = elm->first;
 | 
				
			||||||
              child->size.computed_size[UI_AXIS_VERTICAL];
 | 
					        if (child_index < SIZE_MAX) {
 | 
				
			||||||
          break;
 | 
					            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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_update_cache(ui_context *cx, size_t element_index) {
 | 
					void ui_update_cache(ui_context *cx, size_t element_index) {
 | 
				
			||||||
  if (element_index == SIZE_MAX)
 | 
					    if (element_index == SIZE_MAX)
 | 
				
			||||||
    return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t child_index = _elm(element_index)->first;
 | 
					    size_t child_index = _elm(element_index)->first;
 | 
				
			||||||
  if (child_index < SIZE_MAX) {
 | 
					    if (child_index < SIZE_MAX) {
 | 
				
			||||||
    do {
 | 
					        do {
 | 
				
			||||||
      __auto_type child = _elm(child_index);
 | 
					            __auto_type child = _elm(child_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      size_t last_instantiated_index = cx->frame_index;
 | 
					            size_t last_instantiated_index = cx->frame_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ui_element_cache_data *cache;
 | 
					            ui_element_cache_data *cache;
 | 
				
			||||||
      if ((cache = ht_get(&cx->cached_elements, child->key))) {
 | 
					            if ((cache = ht_get(&cx->cached_elements, child->key))) {
 | 
				
			||||||
        last_instantiated_index = cache->last_instantiated_index;
 | 
					                last_instantiated_index = cache->last_instantiated_index;
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ht_set(&cx->cached_elements, child->key,
 | 
					            ht_set(&cx->cached_elements, child->key,
 | 
				
			||||||
             &(ui_element_cache_data){
 | 
					                   &(ui_element_cache_data){
 | 
				
			||||||
                 .label = child->label,
 | 
					                       .label = child->label,
 | 
				
			||||||
                 .size =
 | 
					                       .size =
 | 
				
			||||||
                     {
 | 
					                           {
 | 
				
			||||||
                         .axis = child->size.axis,
 | 
					                               .axis = child->size.axis,
 | 
				
			||||||
                         .semantic_size = {child->size.semantic_size[0],
 | 
					                               .semantic_size = {child->size.semantic_size[0],
 | 
				
			||||||
                                           child->size.semantic_size[1]},
 | 
					                                                 child->size.semantic_size[1]},
 | 
				
			||||||
                         .computed_size = {child->size.computed_size[0],
 | 
					                               .computed_size = {child->size.computed_size[0],
 | 
				
			||||||
                                           child->size.computed_size[1]},
 | 
					                                                 child->size.computed_size[1]},
 | 
				
			||||||
                         .computed_pos = {child->size.computed_pos[0],
 | 
					                               .computed_pos = {child->size.computed_pos[0],
 | 
				
			||||||
                                          child->size.computed_pos[1]},
 | 
					                                                child->size.computed_pos[1]},
 | 
				
			||||||
                     },
 | 
					                           },
 | 
				
			||||||
                 .last_instantiated_index = last_instantiated_index,
 | 
					                       .last_instantiated_index = last_instantiated_index,
 | 
				
			||||||
             });
 | 
					                   });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ui_update_cache(cx, child_index);
 | 
					            ui_update_cache(cx, child_index);
 | 
				
			||||||
    } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
					        } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*_ui_render_text_func)(string text, float position[2]);
 | 
					typedef void (*_ui_render_text_func)(string text, float position[2]);
 | 
				
			||||||
| 
						 | 
					@ -449,58 +466,65 @@ typedef void (*_ui_render_rect_func)(float position[2], float size[2],
 | 
				
			||||||
                                     float color[4]);
 | 
					                                     float color[4]);
 | 
				
			||||||
void ui_render(ui_context *cx, _ui_render_text_func text_func,
 | 
					void ui_render(ui_context *cx, _ui_render_text_func text_func,
 | 
				
			||||||
               _ui_render_rect_func rect_func) {
 | 
					               _ui_render_rect_func rect_func) {
 | 
				
			||||||
  for (size_t i = 1; i < cx->frame_elements.size; ++i) {
 | 
					    for (size_t i = 1; i < cx->frame_elements.size; ++i) {
 | 
				
			||||||
    string text = cx->frame_elements.data[i].key;
 | 
					        string text = cx->frame_elements.data[i].key;
 | 
				
			||||||
    ui_element_frame_data *elm = &cx->frame_elements.data[i];
 | 
					        ui_element_frame_data *elm = &cx->frame_elements.data[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (_flags(i, UI_FLAG_DRAW_TEXT)) {
 | 
					        if (_flags(i, UI_FLAG_DRAW_TEXT)) {
 | 
				
			||||||
      text_func(text, (float[]){(float)elm->size.computed_pos[0],
 | 
					            text_func(text, (float[]){(float)elm->size.computed_pos[0],
 | 
				
			||||||
                                (float)elm->size.computed_pos[1]});
 | 
					                                      (float)elm->size.computed_pos[1]});
 | 
				
			||||||
    }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (_flags(i, UI_FLAG_DRAW_BACKGROUND)) {
 | 
					        if (_flags(i, UI_FLAG_DRAW_BACKGROUND)) {
 | 
				
			||||||
      rect_func((float[]){(float)elm->size.computed_pos[0],
 | 
					            rect_func((float[]){(float)elm->size.computed_pos[0],
 | 
				
			||||||
                          (float)elm->size.computed_pos[1]},
 | 
					                                (float)elm->size.computed_pos[1]},
 | 
				
			||||||
                (float[]){(float)elm->size.computed_size[0],
 | 
					                      (float[]){(float)elm->size.computed_size[0],
 | 
				
			||||||
                          (float)elm->size.computed_size[1]},
 | 
					                                (float)elm->size.computed_size[1]},
 | 
				
			||||||
                (float[]){1, 1, 1, 0.2});
 | 
					                      (float[]){1, 1, 1, 0.2});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ui_prune(ui_context *cx) {
 | 
					void ui_prune(ui_context *cx) {
 | 
				
			||||||
  for (size_t i = 0; i < cx->cached_elements.capacity; ++i) {
 | 
					    for (size_t i = 0; i < cx->cached_elements.capacity; ++i) {
 | 
				
			||||||
    if (cx->cached_elements.key_slots[i].key.data != NULL) {
 | 
					        if (cx->cached_elements.key_slots[i].key.data != NULL) {
 | 
				
			||||||
      string key = cx->cached_elements.key_slots[i].key;
 | 
					            string key = cx->cached_elements.key_slots[i].key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // if this element hasn't been created in the past 5 frames, remove it
 | 
					            // if this element hasn't been created in the past 5 frames, remove
 | 
				
			||||||
      ui_element_cache_data *cached = ht_get(&cx->cached_elements, key);
 | 
					            // it
 | 
				
			||||||
      if (cached && cached->last_instantiated_index < cx->frame_index - 5) {
 | 
					            ui_element_cache_data *cached = ht_get(&cx->cached_elements, key);
 | 
				
			||||||
        // fprintf(stderr, "removing %.*s from cache, cache index: %zu, frame
 | 
					            if (cached &&
 | 
				
			||||||
        // index: %zu\n", (int)key.len, key.data,
 | 
					                cached->last_instantiated_index < cx->frame_index - 5) {
 | 
				
			||||||
        // cached->last_instantiated_index, cx->frame_index);
 | 
					                // fprintf(stderr, "removing %.*s from cache, cache index: %zu,
 | 
				
			||||||
 | 
					                // frame index: %zu\n", (int)key.len, key.data,
 | 
				
			||||||
 | 
					                // cached->last_instantiated_index, cx->frame_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ht_remove(&cx->cached_elements, key);
 | 
					                ht_remove(&cx->cached_elements, key);
 | 
				
			||||||
      }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t child_index = _elm(0)->first;
 | 
					    size_t child_index = _elm(0)->first;
 | 
				
			||||||
  do {
 | 
					    do {
 | 
				
			||||||
    __auto_type elm = _elm(child_index);
 | 
					        __auto_type elm = _elm(child_index);
 | 
				
			||||||
    if (elm->label.owned) {
 | 
					        if (elm->label.owned) {
 | 
				
			||||||
      free(elm->label.data);
 | 
					            free(elm->label.data);
 | 
				
			||||||
    }
 | 
					        }
 | 
				
			||||||
  } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
					    } while ((child_index = _next(child_index)) < SIZE_MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cx->frame_index += 1;
 | 
					    cx->frame_index += 1;
 | 
				
			||||||
  cx->frame_elements.size = 1;
 | 
					    cx->frame_elements.size = 1;
 | 
				
			||||||
  cx->frame_elements.data[0].first = SIZE_MAX;
 | 
					    cx->frame_elements.data[0].first = SIZE_MAX;
 | 
				
			||||||
  cx->frame_elements.data[0].prev = SIZE_MAX;
 | 
					    cx->frame_elements.data[0].prev = SIZE_MAX;
 | 
				
			||||||
  cx->frame_elements.data[0].next = SIZE_MAX;
 | 
					    cx->frame_elements.data[0].next = SIZE_MAX;
 | 
				
			||||||
  cx->frame_elements.data[0].last = SIZE_MAX;
 | 
					    cx->frame_elements.data[0].last = SIZE_MAX;
 | 
				
			||||||
  cx->frame_elements.data[0].parent = SIZE_MAX;
 | 
					    cx->frame_elements.data[0].parent = SIZE_MAX;
 | 
				
			||||||
  cx->current_parent = 0;
 | 
					    cx->current_parent = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ui_update_input(ui_context *cx, ui_context_input new_input) {
 | 
				
			||||||
 | 
					    cx->last_input = cx->input;
 | 
				
			||||||
 | 
					    cx->input = new_input;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue