ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 017d2d9a542d587a0755acac2840a7b80a0eac74
Parent: 64db6913f4c561e26884a9e1a83c65c02ff9e14a
Author: Randy Palamar
Date:   Sat, 14 Jun 2025 14:03:18 -0600

ui: merge FloatingWidget into UIView

still need to merge the draw paths

Diffstat:
Mui.c | 231+++++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 107 insertions(+), 124 deletions(-)

diff --git a/ui.c b/ui.c @@ -139,10 +139,10 @@ typedef enum { VT_COMPUTE_LATEST_STATS_VIEW, VT_COMPUTE_PROGRESS_BAR, VT_SCALE_BAR, - VT_TEXT_BOX, VT_UI_BUTTON, - VT_UI_FLOATING_WIDGET, + VT_UI_MENU, VT_UI_REGION_SPLIT, + VT_UI_TEXT_BOX, VT_UI_VIEW, } VariableType; @@ -163,16 +163,22 @@ typedef struct { } VariableGroup; typedef enum { - UI_VIEW_CUSTOM_TEXT = 1 << 0, + UIViewFlag_CustomText = 1 << 0, + UIViewFlag_Floating = 1 << 1, } UIViewFlags; typedef struct { - Variable *child; - Variable *close; - Variable *menu; - f32 needed_height; - f32 offset; - UIViewFlags flags; + Variable *child; + Variable *close; + Variable *menu; + union { + struct { + v2 desired_size; + v2 offset; + }; + Rect rect; + }; + UIViewFlags flags; } UIView; /* X(id, text) */ @@ -186,8 +192,7 @@ typedef struct { #define X(id, text) UI_BID_ ##id, typedef enum { - UI_BID_CLOSE_VIEW, - UI_BID_CLOSE_WIDGET, + UI_BID_VIEW_CLOSE, GLOBAL_MENU_BUTTONS FRAME_VIEW_BUTTONS } UIButtonID; @@ -209,19 +214,6 @@ typedef struct { } BeamformerVariable; typedef enum { - FloatingWidgetKind_Menu, - FloatingWidgetKind_TextBox, -} FloatingWidgetKind; - -/* TODO(rnp): collapse with UIView? */ -typedef struct { - Variable *reference; - Variable *close; - Rect rect; - FloatingWidgetKind kind; -} FloatingWidget; - -typedef enum { V_INPUT = 1 << 0, V_TEXT = 1 << 1, V_RADIO_BUTTON = 1 << 2, @@ -236,7 +228,6 @@ struct Variable { BeamformerVariable beamformer_variable; ComputeProgressBar compute_progress_bar; ComputeStatsView compute_stats_view; - FloatingWidget floating_widget; RegionSplit region_split; ScaleBar scale_bar; UIButtonID button; @@ -861,25 +852,6 @@ add_button(BeamformerUI *ui, Variable *group, Arena *arena, s8 name, UIButtonID } function Variable * -add_floating_widget(BeamformerUI *ui, Arena *arena, FloatingWidgetKind kind, v2 at, - Variable *reference, b32 closable) -{ - Variable *result = add_variable(ui, 0, arena, s8(""), 0, VT_UI_FLOATING_WIDGET, ui->small_font); - result->floating_widget.rect.pos = at; - result->floating_widget.kind = kind; - result->floating_widget.reference = reference; - if (closable) { - result->floating_widget.close = add_button(ui, result, arena, s8(""), - UI_BID_CLOSE_WIDGET, 0, ui->small_font); - } - result->parent = &ui->floating_widget_sentinal; - result->next = ui->floating_widget_sentinal.next; - result->next->parent = result; - ui->floating_widget_sentinal.next = result; - return result; -} - -function Variable * add_ui_split(BeamformerUI *ui, Variable *parent, Arena *arena, s8 name, f32 fraction, RegionSplitDirection direction, Font font) { @@ -901,20 +873,35 @@ add_global_menu(BeamformerUI *ui, Arena *arena, Variable *parent) } function Variable * -add_ui_view(BeamformerUI *ui, Variable *parent, Arena *arena, s8 name, u32 view_flags, b32 closable) +add_ui_view(BeamformerUI *ui, Variable *parent, Arena *arena, s8 name, u32 view_flags, b32 menu, b32 closable) { Variable *result = add_variable(ui, parent, arena, name, 0, VT_UI_VIEW, ui->small_font); UIView *view = &result->view; view->flags = view_flags; - view->menu = add_global_menu(ui, arena, result); + if (menu) view->menu = add_global_menu(ui, arena, result); if (closable) { - view->close = add_button(ui, 0, arena, s8(""), UI_BID_CLOSE_VIEW, 0, ui->small_font); + view->close = add_button(ui, 0, arena, s8(""), UI_BID_VIEW_CLOSE, 0, ui->small_font); /* NOTE(rnp): we do this explicitly so that close doesn't end up in the view group */ view->close->parent = result; } return result; } +function Variable * +add_floating_view(BeamformerUI *ui, Arena *arena, VariableType type, v2 at, Variable *child, b32 closable) +{ + Variable *result = add_ui_view(ui, 0, arena, s8(""), UIViewFlag_Floating, 0, closable); + result->type = type; + result->view.rect.pos = at; + result->view.child = child; + + result->parent = &ui->floating_widget_sentinal; + result->next = ui->floating_widget_sentinal.next; + result->next->parent = result; + ui->floating_widget_sentinal.next = result; + return result; +} + function void add_beamformer_variable_f32(BeamformerUI *ui, Variable *group, Arena *arena, s8 name, s8 suffix, f32 *store, v2 limits, f32 display_scale, f32 scroll_scale, u32 flags, @@ -939,7 +926,7 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx) v2 v2_inf = {.x = -F32_INFINITY, .y = F32_INFINITY}; /* TODO(rnp): this can be closable once we have a way of opening new views */ - Variable *result = add_ui_view(ui, parent, &ui->arena, s8("Parameters"), 0, 0); + Variable *result = add_ui_view(ui, parent, &ui->arena, s8("Parameters"), 0, 1, 0); Variable *group = result->view.child = add_variable(ui, result, &ui->arena, s8(""), 0, VT_GROUP, ui->font); @@ -1004,7 +991,7 @@ add_beamformer_frame_view(BeamformerUI *ui, Variable *parent, Arena *arena, BeamformerFrameViewType type, b32 closable) { /* TODO(rnp): this can be always closable once we have a way of opening new views */ - Variable *result = add_ui_view(ui, parent, arena, s8(""), UI_VIEW_CUSTOM_TEXT, closable); + Variable *result = add_ui_view(ui, parent, arena, s8(""), UIViewFlag_CustomText, 1, closable); Variable *var = result->view.child = add_variable(ui, result, arena, s8(""), 0, VT_BEAMFORMER_FRAME_VIEW, ui->small_font); @@ -1084,7 +1071,7 @@ add_compute_progress_bar(Variable *parent, BeamformerCtx *ctx) { BeamformerUI *ui = ctx->ui; /* TODO(rnp): this can be closable once we have a way of opening new views */ - Variable *result = add_ui_view(ui, parent, &ui->arena, s8(""), UI_VIEW_CUSTOM_TEXT, 0); + Variable *result = add_ui_view(ui, parent, &ui->arena, s8(""), UIViewFlag_CustomText, 1, 0); result->view.child = add_variable(ui, result, &ui->arena, s8(""), 0, VT_COMPUTE_PROGRESS_BAR, ui->small_font); ComputeProgressBar *bar = &result->view.child->compute_progress_bar; @@ -1098,7 +1085,7 @@ function Variable * add_compute_stats_view(BeamformerUI *ui, Variable *parent, Arena *arena, VariableType type) { /* TODO(rnp): this can be closable once we have a way of opening new views */ - Variable *result = add_ui_view(ui, parent, arena, s8(""), UI_VIEW_CUSTOM_TEXT, 0); + Variable *result = add_ui_view(ui, parent, arena, s8(""), UIViewFlag_CustomText, 1, 0); result->view.child = add_variable(ui, result, &ui->arena, s8(""), 0, type, ui->small_font); return result; } @@ -1541,7 +1528,7 @@ draw_title_bar(BeamformerUI *ui, Arena arena, Variable *ui_view, Rect r, v2 mous UIView *view = &ui_view->view; s8 title = ui_view->name; - if (view->flags & UI_VIEW_CUSTOM_TEXT) { + if (view->flags & UIViewFlag_CustomText) { Stream buf = arena_stream(arena); push_custom_view_title(&buf, ui_view->group.first); title = arena_stream_commit(&arena, &buf); @@ -2158,13 +2145,13 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex ASSERT(ui_view->type == VT_UI_VIEW); UIView *view = &ui_view->view; - if (view->needed_height - r.size.h < view->offset) - view->offset = view->needed_height - r.size.h; + if (view->desired_size.h - r.size.h < view->offset.h) + view->offset.h = view->desired_size.h - r.size.h; - if (view->needed_height - r.size.h < 0) - view->offset = 0; + if (view->desired_size.h - r.size.h < 0) + view->offset.h = 0; - r.pos.y -= view->offset; + r.pos.y -= view->offset.h; v2 size = {0}; @@ -2189,7 +2176,7 @@ draw_ui_view(BeamformerUI *ui, Variable *ui_view, Rect r, v2 mouse, TextSpec tex InvalidDefaultCase; } - view->needed_height = size.y; + view->desired_size.h = size.y; } function Rect @@ -2352,7 +2339,7 @@ draw_ui_regions(BeamformerUI *ui, Rect window, v2 mouse) function void draw_floating_widget_container(BeamformerUI *ui, Variable *var, v2 mouse, Rect bounds) { - FloatingWidget *fw = &var->floating_widget; + UIView *fw = &var->view; if (fw->rect.size.x > 0 && fw->rect.size.y > 0) { f32 line_height = ui->small_font.baseSize; @@ -2391,16 +2378,15 @@ draw_floating_widgets(BeamformerUI *ui, Rect window_rect, v2 mouse) var != &ui->floating_widget_sentinal; var = var->parent) { - assert(var->type == VT_UI_FLOATING_WIDGET); - FloatingWidget *fw = &var->floating_widget; draw_floating_widget_container(ui, var, mouse, window_rect); - switch (fw->kind) { - case FloatingWidgetKind_Menu:{ - fw->rect = draw_menu(ui, ui->arena, fw->reference, &ui->small_font, - fw->rect.pos, mouse, window_rect); + UIView *fw = &var->view; + switch (var->type) { + case VT_UI_MENU:{ + fw->rect = draw_menu(ui, ui->arena, fw->child, &ui->small_font, + fw->rect.pos, mouse, window_rect); }break; - case FloatingWidgetKind_TextBox:{ + case VT_UI_TEXT_BOX:{ InputState *is = &ui->text_input_state; f32 cursor_width = (is->cursor == is->count) ? 0.55 * is->font->baseSize : 4; @@ -2421,7 +2407,7 @@ draw_floating_widgets(BeamformerUI *ui, Rect window_rect, v2 mouse) v4 cursor_colour = FOCUSED_COLOUR; cursor_colour.a = CLAMP01(is->cursor_blink_t); - v4 text_colour = lerp_v4(FG_COLOUR, HOVERED_COLOUR, fw->reference->hover_t); + v4 text_colour = lerp_v4(FG_COLOUR, HOVERED_COLOUR, fw->child->hover_t); TextSpec text_spec = {.font = is->font, .colour = text_colour}; draw_text(text, text_position, &text_spec); @@ -2461,8 +2447,8 @@ scroll_interaction(Variable *var, f32 delta) *var->cycler.state %= var->cycler.cycle_length; }break; case VT_UI_VIEW:{ - var->view.offset += UI_SCROLL_SPEED * delta; - var->view.offset = MAX(0, var->view.offset); + var->view.offset.h += UI_SCROLL_SPEED * delta; + var->view.offset.h = MAX(0, var->view.offset.h); }break; InvalidDefaultCase; } @@ -2471,10 +2457,10 @@ scroll_interaction(Variable *var, f32 delta) function void begin_text_input(InputState *is, Rect r, Variable *container, v2 mouse) { - assert(container->type == VT_UI_FLOATING_WIDGET); + assert(container->type == VT_UI_TEXT_BOX); Font *font = is->font = is->hot_font; Stream s = {.cap = countof(is->buf), .data = is->buf}; - stream_append_variable(&s, container->floating_widget.reference); + stream_append_variable(&s, container->view.child); is->count = s.widx; is->container = container; @@ -2638,25 +2624,47 @@ ui_widget_bring_to_front(Variable *sentinal, Variable *widget) } function void -ui_close_widget(BeamformerUI *ui, Variable *widget) -{ - assert(widget->type == VT_UI_FLOATING_WIDGET); - FloatingWidget *fw = &widget->floating_widget; - switch (fw->kind) { - case FloatingWidgetKind_Menu:{ - assert(fw->reference->type == VT_GROUP); - fw->reference->group.expanded = 0; - fw->reference->group.container = 0; +ui_view_close(BeamformerUI *ui, Variable *view) +{ + switch (view->type) { + case VT_UI_MENU: + case VT_UI_TEXT_BOX: + { + UIView *fw = &view->view; + if (view->type == VT_UI_MENU) { + assert(fw->child->type == VT_GROUP); + fw->child->group.expanded = 0; + fw->child->group.container = 0; + } else { + end_text_input(&ui->text_input_state, fw->child); + } + view->parent->next = view->next; + view->next->parent = view->parent; + if (fw->close) SLLPush(fw->close, ui->variable_freelist); + SLLPush(view, ui->variable_freelist); }break; - case FloatingWidgetKind_TextBox:{ - end_text_input(&ui->text_input_state, fw->reference); + case VT_UI_VIEW:{ + assert(view->parent->type == VT_UI_REGION_SPLIT); + Variable *region = view->parent; + + Variable *parent = region->parent; + Variable *remaining = region->region_split.left; + if (remaining == view) remaining = region->region_split.right; + + ui_view_free(ui, view); + + assert(parent->type == VT_UI_REGION_SPLIT); + if (parent->region_split.left == region) { + parent->region_split.left = remaining; + } else { + parent->region_split.right = remaining; + } + remaining->parent = parent; + + SLLPush(region, ui->variable_freelist); }break; InvalidDefaultCase; } - widget->parent->next = widget->next; - widget->next->parent = widget->parent; - if (fw->close) SLLPush(fw->close, ui->variable_freelist); - SLLPush(widget, ui->variable_freelist); } function void @@ -2664,6 +2672,7 @@ ui_button_interaction(BeamformerUI *ui, Variable *button) { assert(button->type == VT_UI_BUTTON); switch (button->button) { + case UI_BID_VIEW_CLOSE:{ ui_view_close(ui, button->parent); }break; case UI_BID_FV_COPY_HORIZONTAL:{ ui_copy_frame(ui, button->parent->parent, RSD_HORIZONTAL); }break; @@ -2676,28 +2685,6 @@ ui_button_interaction(BeamformerUI *ui, Variable *button) case UI_BID_GM_OPEN_LIVE_VIEW_BELOW:{ ui_add_live_frame_view(ui, button->parent->parent, RSD_VERTICAL); }break; - case UI_BID_CLOSE_WIDGET:{ ui_close_widget(ui, button->parent); }break; - case UI_BID_CLOSE_VIEW:{ - Variable *view = button->parent; - Variable *region = view->parent; - assert(view->type == VT_UI_VIEW && region->type == VT_UI_REGION_SPLIT); - - Variable *parent = region->parent; - Variable *remaining = region->region_split.left; - if (remaining == view) remaining = region->region_split.right; - - ui_view_free(ui, view); - - assert(parent->type == VT_UI_REGION_SPLIT); - if (parent->region_split.left == region) { - parent->region_split.left = remaining; - } else { - parent->region_split.right = remaining; - } - remaining->parent = parent; - - SLLPush(region, ui->variable_freelist); - }break; } } @@ -2713,16 +2700,14 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll) case VT_SCALE_BAR:{ hot->kind = InteractionKind_Set; }break; case VT_UI_BUTTON:{ hot->kind = InteractionKind_Button; }break; case VT_GROUP:{ hot->kind = InteractionKind_Set; }break; - case VT_UI_FLOATING_WIDGET:{ - FloatingWidget *fw = &hot->var->floating_widget; - switch (fw->kind) { - case FloatingWidgetKind_Menu:{ + case VT_UI_TEXT_BOX: + case VT_UI_MENU: + { + if (hot->var->type == VT_UI_MENU) { hot->kind = InteractionKind_Drag; - }break; - case FloatingWidgetKind_TextBox:{ + } else { hot->kind = InteractionKind_Text; begin_text_input(&ui->text_input_state, hot->rect, hot->var, input->mouse); - }break; } ui_widget_bring_to_front(&ui->floating_widget_sentinal, hot->var); }break; @@ -2767,10 +2752,9 @@ ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll) hot->kind = InteractionKind_Scroll; } else if (hot->var->flags & V_TEXT) { hot->kind = InteractionKind_Text; - Variable *w = add_floating_widget(ui, &ui->arena, - FloatingWidgetKind_TextBox, - hot->rect.pos, hot->var, 0); - w->floating_widget.rect = hot->rect; + Variable *w = add_floating_view(ui, &ui->arena, VT_UI_TEXT_BOX, + hot->rect.pos, hot->var, 0); + w->view.rect = hot->rect; begin_text_input(&ui->text_input_state, hot->rect, w, input->mouse); } }break; @@ -2810,8 +2794,7 @@ ui_end_interact(BeamformerUI *ui, v2 mouse) if (g->container) { ui_widget_bring_to_front(&ui->floating_widget_sentinal, g->container); } else { - g->container = add_floating_widget(ui, &ui->arena, FloatingWidgetKind_Menu, - mouse, it->var, 1); + g->container = add_floating_view(ui, &ui->arena, VT_UI_MENU, mouse, it->var, 1); } }break; case InteractionKind_Ruler:{ @@ -2820,7 +2803,7 @@ ui_end_interact(BeamformerUI *ui, v2 mouse) }break; case InteractionKind_Button:{ ui_button_interaction(ui, it->var); }break; case InteractionKind_Scroll:{ scroll_interaction(it->var, GetMouseWheelMoveV().y); }break; - case InteractionKind_Text:{ ui_close_widget(ui, ui->text_input_state.container); }break; + case InteractionKind_Text:{ ui_view_close(ui, ui->text_input_state.container); }break; InvalidDefaultCase; } @@ -2900,8 +2883,8 @@ ui_interact(BeamformerUI *ui, BeamformerInput *input, Rect window_rect) v2 dMouse = sub_v2(input->mouse, input->last_mouse); switch (ui->interaction.var->type) { - case VT_UI_FLOATING_WIDGET:{ - v2 *pos = &ui->interaction.var->floating_widget.rect.pos; + case VT_UI_MENU:{ + v2 *pos = &ui->interaction.var->view.rect.pos; *pos = clamp_v2_rect(add_v2(*pos, dMouse), window_rect); }break; case VT_UI_REGION_SPLIT:{