fix ctx menu closing escape consumption rules; fix top bar prioritization rules

This commit is contained in:
Ryan Fleury
2025-03-19 16:42:14 -07:00
parent b9ed7b3f0f
commit 8df16b8508
2 changed files with 443 additions and 442 deletions
+437 -436
View File
@@ -6342,7 +6342,6 @@ rd_window_frame(void)
U64 hover_eval_open_delay_us = 400000;
B32 hover_eval_is_open = (!popup_is_open &&
!query_is_open &&
!ws->menu_bar_focused &&
ws->hover_eval_string.size != 0 &&
ws->hover_eval_firstt_us+hover_eval_open_delay_us < ws->hover_eval_lastt_us &&
rd_state->time_in_us - ws->hover_eval_lastt_us < hover_eval_open_delay_us);
@@ -7434,440 +7433,6 @@ rd_window_frame(void)
}
}
////////////////////////////
//- rjf: @window_ui_part gather all tasks to build floating views
//
typedef struct FloatingViewTask FloatingViewTask;
struct FloatingViewTask
{
FloatingViewTask *next;
RD_Cfg *view;
F32 row_height_px;
Rng2F32 rect;
String8 view_name;
String8 expr;
B32 is_focused;
B32 is_anchored;
UI_Signal signal; // NOTE(rjf): output, from build
};
FloatingViewTask *hover_eval_floating_view_task = 0;
FloatingViewTask *query_floating_view_task = 0;
FloatingViewTask *first_floating_view_task = 0;
FloatingViewTask *last_floating_view_task = 0;
RD_Font(RD_FontSlot_Code)
UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
{
//- rjf: try to add hover eval first
{
B32 build_hover_eval = (hover_eval_is_open && (!rd_drag_is_active() || rd_state->drag_drop_regs_slot == RD_RegSlot_View));
// rjf: disable hover eval if hovered view is actively scrolling
if(hover_eval_is_open)
{
for(RD_PanelNode *panel = panel_tree.root;
panel != &rd_nil_panel_node;
panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
{
if(panel->first != &rd_nil_panel_node) { continue; }
RD_Cfg *tab = panel->selected_tab;
if(tab != &rd_nil_cfg)
{
RD_ViewState *vs = rd_view_state_from_cfg(tab);
Rng2F32 panel_rect = rd_target_rect_from_panel_node(content_rect, panel_tree.root, panel);
if(contains_2f32(panel_rect, ui_mouse()) &&
(abs_f32(vs->scroll_pos.x.off) > 0.01f ||
abs_f32(vs->scroll_pos.y.off) > 0.01f))
{
build_hover_eval = 0;
ws->hover_eval_firstt_us = rd_state->time_in_us;
}
}
}
}
// rjf: choose hover evaluation expression
String8 hover_eval_expr = push_str8f(scratch.arena, "%S%s%S", ws->hover_eval_string, ws->hover_eval_view_rules.size != 0 ? " => " : "", ws->hover_eval_view_rules);
// rjf: evaluate hover evaluation expression, & determine if it evaluates
// such that we want to build a hover eval.
E_Eval hover_eval = e_eval_from_string(scratch.arena, hover_eval_expr);
{
if(hover_eval.msgs.max_kind > E_MsgKind_Null)
{
build_hover_eval = 0;
}
else if(hover_eval.space.kind == RD_EvalSpaceKind_MetaCfg &&
rd_cfg_from_eval_space(hover_eval.space) == &rd_nil_cfg)
{
build_hover_eval = 0;
}
else if((hover_eval.space.kind == RD_EvalSpaceKind_MetaCtrlEntity ||
hover_eval.space.kind == RD_EvalSpaceKind_CtrlEntity) &&
rd_ctrl_entity_from_eval_space(hover_eval.space) == &ctrl_entity_nil)
{
build_hover_eval = 0;
}
}
// rjf: determine if we have a top-level visualizer
EV_ExpandRuleTagPair expand_rule_tag = ev_expand_rule_tag_pair_from_expr_irtree(hover_eval.exprs.last, &hover_eval.irtree);
RD_ViewUIRule *view_ui_rule = rd_view_ui_rule_from_string(expand_rule_tag.rule->string);
// rjf: determine view name
String8 view_name = str8_lit("watch");
if(view_ui_rule != &rd_nil_view_ui_rule)
{
view_name = view_ui_rule->name;
}
// rjf: build view
RD_Cfg *root = rd_immediate_cfg_from_keyf("hover_eval_view");
RD_Cfg *view = rd_cfg_child_from_string_or_alloc(root, view_name);
RD_Cfg *explicit_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("explicit_root"));
rd_cfg_new(explicit_root, str8_lit("1"));
// rjf: request frames if we're waiting to open
if(ws->hover_eval_string.size != 0 &&
!hover_eval_is_open &&
ws->hover_eval_lastt_us < ws->hover_eval_firstt_us+hover_eval_open_delay_us &&
rd_state->time_in_us - ws->hover_eval_lastt_us < hover_eval_open_delay_us*2)
{
rd_request_frame();
}
// rjf: determine size of hover evaluation container
EV_BlockTree predicted_block_tree = {0};
RD_RegsScope(.view = view->id)
{
predicted_block_tree = ev_block_tree_from_exprs(scratch.arena, rd_view_eval_view(), str8_zero(), hover_eval.exprs);
}
F32 row_height_px = floor_f32(ui_top_font_size()*2.5f);
U64 max_row_count = (U64)floor_f32(ui_top_font_size()*10.f / row_height_px);
if(ws->hover_eval_focused)
{
max_row_count *= 3;
}
U64 needed_row_count = Min(max_row_count, predicted_block_tree.total_row_count);
F32 num_rows_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "hover_eval_num_rows_t"), (F32)needed_row_count);
F32 width_px = 60.f*ui_top_font_size();
F32 height_px = num_rows_t*row_height_px;
// rjf: if arbitrary visualizer, pick catchall size
if(view_ui_rule != &rd_nil_view_ui_rule)
{
height_px = 40.f*ui_top_font_size();
}
// rjf: determine hover eval top-level rect
Rng2F32 rect = r2f32p(ws->hover_eval_spawn_pos.x,
ws->hover_eval_spawn_pos.y,
ws->hover_eval_spawn_pos.x + width_px,
ws->hover_eval_spawn_pos.y + height_px);
// rjf: push hover eval task
if(build_hover_eval)
{
FloatingViewTask *t = push_array(scratch.arena, FloatingViewTask, 1);
SLLQueuePush(first_floating_view_task, last_floating_view_task, t);
hover_eval_floating_view_task = t;
t->view = view;
t->row_height_px = row_height_px;
t->rect = rect;
t->view_name = view_name;
t->expr = hover_eval_expr;
t->is_focused = ws->hover_eval_focused;
t->is_anchored = 1;
}
// rjf: reset focus state if hover eval is not being built
if(!build_hover_eval || ws->hover_eval_string.size == 0 || !hover_eval_is_open)
{
ws->hover_eval_focused = 0;
}
}
//- rjf: try to add opened query
if(query_is_open)
{
if(ui_slot_press(UI_EventActionSlot_Cancel))
{
rd_cmd(RD_CmdKind_CancelQuery);
}
// rjf: unpack query info
String8 cmd_name = ws->query_cmd_name;
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_name);
B32 size_query_by_expr_eval = (cmd_kind_info->query.expr.size == 0);
B32 query_is_anchored = (!ui_box_is_nil(ui_box_from_key(ws->query_regs->ui_key)));
F32 row_height_px = floor_f32(ui_top_font_size()*2.5f);
// rjf: build view for query
RD_Cfg *root = rd_immediate_cfg_from_keyf("window_query_%p", window);
RD_Cfg *view = rd_cfg_child_from_string_or_alloc(root, str8_lit("watch"));
RD_Cfg *query = rd_cfg_child_from_string_or_alloc(view, str8_lit("query"));
RD_Cfg *cmd = rd_cfg_child_from_string_or_alloc(query, str8_lit("cmd"));
rd_cfg_new_replace(cmd, cmd_name);
rd_cfg_child_from_string_or_alloc(view, str8_lit("lister"));
RD_ViewState *vs = rd_view_state_from_cfg(view);
vs->query_is_selected = 1;
// rjf: compute query expression
String8 query_expression = cmd_kind_info->query.expr;
if(query_expression.size == 0)
{
query_expression = str8(vs->query_buffer, vs->query_string_size);
}
else
{
U64 input_insertion_pos = str8_find_needle(query_expression, 0, str8_lit("$input"), 0);
if(input_insertion_pos < query_expression.size)
{
String8 pre_insertion = str8_prefix(query_expression, input_insertion_pos);
String8 post_insertion = str8_skip(query_expression, input_insertion_pos + 6);
query_expression = push_str8f(scratch.arena, "%S%S%S", pre_insertion, str8(vs->query_buffer, vs->query_string_size), post_insertion);
}
}
// rjf: based on query expression, determine if we have an explicit root
if(query_expression.size == 0)
{
RD_Cfg *explicit_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("explicit_root"));
rd_cfg_new(explicit_root, str8_lit("1"));
}
else
{
rd_cfg_release(rd_cfg_child_from_string(view, str8_lit("explicit_root")));
}
// rjf: evaluate query expression
E_Eval query_eval = e_eval_from_string(scratch.arena, query_expression);
// rjf: compute query view's top-level rectangle
Rng2F32 rect = {0};
RD_RegsScope(.view = view->id)
{
Vec2F32 content_rect_center = center_2f32(content_rect);
Vec2F32 content_rect_dim = dim_2f32(content_rect);
EV_BlockTree predicted_block_tree = ev_block_tree_from_exprs(scratch.arena, rd_view_eval_view(), rd_view_query_input(), query_eval.exprs);
F32 query_width_px = floor_f32(content_rect_dim.x * 0.35f);
F32 max_query_height_px = content_rect_dim.y*0.8f;
F32 query_height_px = max_query_height_px;
if(size_query_by_expr_eval)
{
query_height_px = row_height_px*predicted_block_tree.total_row_count;
query_height_px = Min(query_height_px, max_query_height_px);
}
rect = r2f32p(content_rect_center.x - query_width_px/2,
content_rect_center.y - max_query_height_px/2.f,
content_rect_center.x + query_width_px/2,
content_rect_center.y - max_query_height_px/2.f + query_height_px);
if(!ui_key_match(ui_key_zero(), ws->query_regs->ui_key))
{
UI_Box *anchor_box = ui_box_from_key(ws->query_regs->ui_key);
if(anchor_box != &ui_nil_box)
{
rect.x0 = anchor_box->rect.x0;
rect.y0 = anchor_box->rect.y1;
rect.x1 = rect.x0 + ui_top_font_size()*40.f;
rect.y1 = rect.y0 + query_height_px;
}
}
}
// rjf: push query task
{
FloatingViewTask *t = push_array(scratch.arena, FloatingViewTask, 1);
SLLQueuePush(first_floating_view_task, last_floating_view_task, t);
query_floating_view_task = t;
t->view = view;
t->row_height_px = row_height_px;
t->rect = rect;
t->view_name = str8_lit("watch");
t->expr = query_expression;
t->is_focused = 1;
t->is_anchored = query_is_anchored;
}
}
}
////////////////////////////
//- rjf: @window_ui_part build all floating views
//
ProfScope("build all floating views")
RD_Font(RD_FontSlot_Code)
UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
UI_TagF("floating")
{
F32 fast_open_rate = 1 - pow_f32(2, (-70.f * ui_state->animation_dt));
F32 slow_open_rate = 1 - pow_f32(2, (-40.f * ui_state->animation_dt));
for(FloatingViewTask *t = first_floating_view_task; t != 0; t = t->next)
{
// rjf: unpack
RD_Cfg *view = t->view;
F32 row_height_px = t->row_height_px;
Rng2F32 rect = t->rect;
String8 view_name = t->view_name;
String8 expr = t->expr;
B32 is_focused = t->is_focused;
B32 is_anchored = t->is_anchored;
F32 open_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "floating_view_open_%p", view), 1.f, .rate = is_anchored ? fast_open_rate : slow_open_rate);
// rjf: build cfg tree
RD_Cfg *expr_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("expression"));
rd_cfg_new(view, str8_lit("selected"));
rd_cfg_new_replace(expr_root, expr);
// rjf: push view regs
rd_push_regs(.view = view->id);
{
String8 view_expr = rd_expr_from_cfg(view);
String8 view_file_path = rd_file_path_from_eval_string(rd_frame_arena(), view_expr);
// NOTE(rjf): we want to only fill out this view's file path slot if it
// evaluates one - this way, a view can use the slot to know the selected
// file path (if there is one). this is useful when pushing commandas which
// apply to a cursor, for example.
if(view_file_path.size != 0)
{
rd_regs()->file_path = view_file_path;
}
}
// rjf: build
UI_Focus(is_focused ? UI_FocusKind_On : UI_FocusKind_Off)
UI_PrefHeight(ui_px(row_height_px, 1.f))
{
// rjf: build top-level container box
UI_Box *container = &ui_nil_box;
UI_Rect(rect) UI_ChildLayoutAxis(Axis2_Y) UI_Squish(0.1f-0.1f*open_t) UI_Transparency(1.f-open_t)
{
container = ui_build_box_from_stringf(UI_BoxFlag_Clickable|
UI_BoxFlag_DrawBorder|
UI_BoxFlag_DrawBackground|
UI_BoxFlag_DrawBackgroundBlur|
UI_BoxFlag_DisableFocusOverlay|
UI_BoxFlag_DrawDropShadow|
(UI_BoxFlag_SquishAnchored*!!is_anchored),
"floating_view_container_%p", view);
}
// rjf: peek press events -> focus hover eval if we have a press
if(!ws->hover_eval_focused)
{
for(UI_Event *evt = 0; ui_next_event(&evt);)
{
if(evt->kind == UI_EventKind_Press &&
evt->key == OS_Key_LeftMouseButton &&
contains_2f32(container->rect, evt->pos))
{
ws->hover_eval_focused = 1;
break;
}
}
}
// rjf: build overlay container for loading animation
UI_Box *loading_overlay_container = &ui_nil_box;
UI_Parent(container) UI_WidthFill UI_HeightFill
{
loading_overlay_container = ui_build_box_from_key(UI_BoxFlag_Floating, ui_key_zero());
}
// rjf: build contents
UI_Parent(container) UI_Focus(is_focused ? UI_FocusKind_Null : UI_FocusKind_Off)
{
ui_set_next_pref_width(ui_pct(1, 0));
ui_set_next_pref_height(ui_pct(1, 0));
ui_set_next_child_layout_axis(Axis2_Y);
UI_Box *view_contents_container = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "###view_contents_container");
UI_Parent(view_contents_container) UI_WidthFill
{
rd_view_ui(rect);
}
}
// rjf: build loading overlay
{
RD_ViewState *vs = rd_view_state_from_cfg(view);
F32 loading_t = vs->loading_t;
if(loading_t > 0.01f) UI_Parent(loading_overlay_container)
{
rd_loading_overlay(rect, loading_t, vs->loading_progress_v, vs->loading_progress_v_target);
}
}
// rjf: interact with container
UI_Signal sig = ui_signal_from_box(container);
t->signal = sig;
}
// rjf: pop interaction registers; commit if this is focused
RD_Regs *view_regs = rd_pop_regs();
if(is_focused)
{
MemoryCopyStruct(rd_regs(), view_regs);
}
}
}
////////////////////////////
//- rjf: @window_ui_part do special handling of floating view interactions
//
{
//- rjf: hover eval focus rules
if(hover_eval_floating_view_task)
{
UI_Signal sig = hover_eval_floating_view_task->signal;
if(ui_pressed(sig))
{
ws->hover_eval_focused = 1;
}
if(ui_mouse_over(sig) || ws->hover_eval_focused)
{
ws->hover_eval_lastt_us = rd_state->time_in_us;
}
else if(ws->hover_eval_lastt_us+1000000 < rd_state->time_in_us)
{
rd_request_frame();
}
if(ws->hover_eval_focused)
{
for(UI_Event *evt = 0; ui_next_event(&evt);)
{
if(evt->kind == UI_EventKind_Press &&
evt->key == OS_Key_LeftMouseButton &&
!contains_2f32(hover_eval_floating_view_task->rect, evt->pos))
{
ws->hover_eval_focused = 0;
MemoryZeroStruct(&ws->hover_eval_string);
arena_clear(ws->hover_eval_arena);
rd_request_frame();
break;
}
}
}
}
//- rjf: query interactions
if(query_floating_view_task)
{
String8 cmd_name = ws->query_cmd_name;
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_name);
// rjf: any queries which take a file path mutate the debugger's "current path"
if(cmd_kind_info->query.slot == RD_RegSlot_FilePath)
{
RD_Cfg *view = query_floating_view_task->view;
RD_Cfg *query = rd_cfg_child_from_string(view, str8_lit("query"));
RD_Cfg *input = rd_cfg_child_from_string(query, str8_lit("input"));
if(input != &rd_nil_cfg)
{
String8 path_chopped = str8_chop_last_slash(input->first->string);
rd_cmd(RD_CmdKind_SetCurrentPath, .file_path = path_chopped);
}
}
}
}
////////////////////////////
//- rjf: @window_ui_part top bar
//
@@ -7877,7 +7442,7 @@ rd_window_frame(void)
os_window_push_custom_edges(ws->os, window_edge_px);
os_window_push_custom_title_bar(ws->os, dim_2f32(top_bar_rect).y);
ui_set_next_flags(UI_BoxFlag_DefaultFocusNav|UI_BoxFlag_DisableFocusOverlay);
UI_Focus((ws->menu_bar_focused && window_is_focused && !ui_any_ctx_menu_is_open() && !ws->hover_eval_focused) ? UI_FocusKind_On : UI_FocusKind_Null)
UI_Focus((ws->menu_bar_focused && window_is_focused && !ui_any_ctx_menu_is_open()) ? UI_FocusKind_On : UI_FocusKind_Null)
UI_TagF("menu_bar")
UI_Pane(top_bar_rect, str8_lit("###top_bar"))
UI_WidthFill UI_Row
@@ -8636,6 +8201,442 @@ rd_window_frame(void)
}
}
////////////////////////////
//- rjf: @window_ui_part gather all tasks to build floating views
//
typedef struct FloatingViewTask FloatingViewTask;
struct FloatingViewTask
{
FloatingViewTask *next;
RD_Cfg *view;
F32 row_height_px;
Rng2F32 rect;
String8 view_name;
String8 expr;
B32 is_focused;
B32 is_anchored;
UI_Signal signal; // NOTE(rjf): output, from build
};
FloatingViewTask *hover_eval_floating_view_task = 0;
FloatingViewTask *query_floating_view_task = 0;
FloatingViewTask *first_floating_view_task = 0;
FloatingViewTask *last_floating_view_task = 0;
RD_Font(RD_FontSlot_Code)
UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
{
//- rjf: try to add hover eval first
{
B32 build_hover_eval = (hover_eval_is_open && (!rd_drag_is_active() || rd_state->drag_drop_regs_slot == RD_RegSlot_View));
// rjf: disable hover eval if hovered view is actively scrolling
if(hover_eval_is_open)
{
for(RD_PanelNode *panel = panel_tree.root;
panel != &rd_nil_panel_node;
panel = rd_panel_node_rec__depth_first_pre(panel_tree.root, panel).next)
{
if(panel->first != &rd_nil_panel_node) { continue; }
RD_Cfg *tab = panel->selected_tab;
if(tab != &rd_nil_cfg)
{
RD_ViewState *vs = rd_view_state_from_cfg(tab);
Rng2F32 panel_rect = rd_target_rect_from_panel_node(content_rect, panel_tree.root, panel);
if(contains_2f32(panel_rect, ui_mouse()) &&
(abs_f32(vs->scroll_pos.x.off) > 0.01f ||
abs_f32(vs->scroll_pos.y.off) > 0.01f))
{
build_hover_eval = 0;
ws->hover_eval_firstt_us = rd_state->time_in_us;
}
}
}
}
// rjf: choose hover evaluation expression
String8 hover_eval_expr = push_str8f(scratch.arena, "%S%s%S", ws->hover_eval_string, ws->hover_eval_view_rules.size != 0 ? " => " : "", ws->hover_eval_view_rules);
// rjf: evaluate hover evaluation expression, & determine if it evaluates
// such that we want to build a hover eval.
E_Eval hover_eval = e_eval_from_string(scratch.arena, hover_eval_expr);
{
if(hover_eval.msgs.max_kind > E_MsgKind_Null)
{
build_hover_eval = 0;
}
else if(hover_eval.space.kind == RD_EvalSpaceKind_MetaCfg &&
rd_cfg_from_eval_space(hover_eval.space) == &rd_nil_cfg)
{
build_hover_eval = 0;
}
else if((hover_eval.space.kind == RD_EvalSpaceKind_MetaCtrlEntity ||
hover_eval.space.kind == RD_EvalSpaceKind_CtrlEntity) &&
rd_ctrl_entity_from_eval_space(hover_eval.space) == &ctrl_entity_nil)
{
build_hover_eval = 0;
}
}
// rjf: determine if we have a top-level visualizer
EV_ExpandRuleTagPair expand_rule_tag = ev_expand_rule_tag_pair_from_expr_irtree(hover_eval.exprs.last, &hover_eval.irtree);
RD_ViewUIRule *view_ui_rule = rd_view_ui_rule_from_string(expand_rule_tag.rule->string);
// rjf: determine view name
String8 view_name = str8_lit("watch");
if(view_ui_rule != &rd_nil_view_ui_rule)
{
view_name = view_ui_rule->name;
}
// rjf: build view
RD_Cfg *root = rd_immediate_cfg_from_keyf("hover_eval_view");
RD_Cfg *view = rd_cfg_child_from_string_or_alloc(root, view_name);
RD_Cfg *explicit_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("explicit_root"));
rd_cfg_new(explicit_root, str8_lit("1"));
// rjf: request frames if we're waiting to open
if(ws->hover_eval_string.size != 0 &&
!hover_eval_is_open &&
ws->hover_eval_lastt_us < ws->hover_eval_firstt_us+hover_eval_open_delay_us &&
rd_state->time_in_us - ws->hover_eval_lastt_us < hover_eval_open_delay_us*2)
{
rd_request_frame();
}
// rjf: determine size of hover evaluation container
EV_BlockTree predicted_block_tree = {0};
RD_RegsScope(.view = view->id)
{
predicted_block_tree = ev_block_tree_from_exprs(scratch.arena, rd_view_eval_view(), str8_zero(), hover_eval.exprs);
}
F32 row_height_px = floor_f32(ui_top_font_size()*2.5f);
U64 max_row_count = (U64)floor_f32(ui_top_font_size()*10.f / row_height_px);
if(ws->hover_eval_focused)
{
max_row_count *= 3;
}
U64 needed_row_count = Min(max_row_count, predicted_block_tree.total_row_count);
F32 num_rows_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "hover_eval_num_rows_t"), (F32)needed_row_count);
F32 width_px = 60.f*ui_top_font_size();
F32 height_px = num_rows_t*row_height_px;
// rjf: if arbitrary visualizer, pick catchall size
if(view_ui_rule != &rd_nil_view_ui_rule)
{
height_px = 40.f*ui_top_font_size();
}
// rjf: determine hover eval top-level rect
Rng2F32 rect = r2f32p(ws->hover_eval_spawn_pos.x,
ws->hover_eval_spawn_pos.y,
ws->hover_eval_spawn_pos.x + width_px,
ws->hover_eval_spawn_pos.y + height_px);
// rjf: push hover eval task
if(build_hover_eval)
{
FloatingViewTask *t = push_array(scratch.arena, FloatingViewTask, 1);
SLLQueuePush(first_floating_view_task, last_floating_view_task, t);
hover_eval_floating_view_task = t;
t->view = view;
t->row_height_px = row_height_px;
t->rect = rect;
t->view_name = view_name;
t->expr = hover_eval_expr;
t->is_focused = ws->hover_eval_focused;
t->is_anchored = 1;
}
// rjf: reset focus state if hover eval is not being built
if(!build_hover_eval || ws->hover_eval_string.size == 0 || !hover_eval_is_open)
{
ws->hover_eval_focused = 0;
}
}
//- rjf: try to add opened query
if(query_is_open)
{
// rjf: close queries
if(ui_slot_press(UI_EventActionSlot_Cancel))
{
rd_cmd(RD_CmdKind_CancelQuery);
}
// rjf: unpack query info
String8 cmd_name = ws->query_cmd_name;
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_name);
B32 size_query_by_expr_eval = (cmd_kind_info->query.expr.size == 0);
B32 query_is_anchored = (!ui_box_is_nil(ui_box_from_key(ws->query_regs->ui_key)));
F32 row_height_px = floor_f32(ui_top_font_size()*2.5f);
// rjf: build view for query
RD_Cfg *root = rd_immediate_cfg_from_keyf("window_query_%p", window);
RD_Cfg *view = rd_cfg_child_from_string_or_alloc(root, str8_lit("watch"));
RD_Cfg *query = rd_cfg_child_from_string_or_alloc(view, str8_lit("query"));
RD_Cfg *cmd = rd_cfg_child_from_string_or_alloc(query, str8_lit("cmd"));
rd_cfg_new_replace(cmd, cmd_name);
rd_cfg_child_from_string_or_alloc(view, str8_lit("lister"));
RD_ViewState *vs = rd_view_state_from_cfg(view);
vs->query_is_selected = 1;
// rjf: compute query expression
String8 query_expression = cmd_kind_info->query.expr;
if(query_expression.size == 0)
{
query_expression = str8(vs->query_buffer, vs->query_string_size);
}
else
{
U64 input_insertion_pos = str8_find_needle(query_expression, 0, str8_lit("$input"), 0);
if(input_insertion_pos < query_expression.size)
{
String8 pre_insertion = str8_prefix(query_expression, input_insertion_pos);
String8 post_insertion = str8_skip(query_expression, input_insertion_pos + 6);
query_expression = push_str8f(scratch.arena, "%S%S%S", pre_insertion, str8(vs->query_buffer, vs->query_string_size), post_insertion);
}
}
// rjf: based on query expression, determine if we have an explicit root
if(query_expression.size == 0)
{
RD_Cfg *explicit_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("explicit_root"));
rd_cfg_new(explicit_root, str8_lit("1"));
}
else
{
rd_cfg_release(rd_cfg_child_from_string(view, str8_lit("explicit_root")));
}
// rjf: evaluate query expression
E_Eval query_eval = e_eval_from_string(scratch.arena, query_expression);
// rjf: compute query view's top-level rectangle
Rng2F32 rect = {0};
RD_RegsScope(.view = view->id)
{
Vec2F32 content_rect_center = center_2f32(content_rect);
Vec2F32 content_rect_dim = dim_2f32(content_rect);
EV_BlockTree predicted_block_tree = ev_block_tree_from_exprs(scratch.arena, rd_view_eval_view(), rd_view_query_input(), query_eval.exprs);
F32 query_width_px = floor_f32(content_rect_dim.x * 0.35f);
F32 max_query_height_px = content_rect_dim.y*0.8f;
F32 query_height_px = max_query_height_px;
if(size_query_by_expr_eval)
{
query_height_px = row_height_px*predicted_block_tree.total_row_count;
query_height_px = Min(query_height_px, max_query_height_px);
}
rect = r2f32p(content_rect_center.x - query_width_px/2,
content_rect_center.y - max_query_height_px/2.f,
content_rect_center.x + query_width_px/2,
content_rect_center.y - max_query_height_px/2.f + query_height_px);
if(!ui_key_match(ui_key_zero(), ws->query_regs->ui_key))
{
UI_Box *anchor_box = ui_box_from_key(ws->query_regs->ui_key);
if(anchor_box != &ui_nil_box)
{
rect.x0 = anchor_box->rect.x0;
rect.y0 = anchor_box->rect.y1;
rect.x1 = rect.x0 + ui_top_font_size()*40.f;
rect.y1 = rect.y0 + query_height_px;
}
}
}
// rjf: push query task
{
FloatingViewTask *t = push_array(scratch.arena, FloatingViewTask, 1);
SLLQueuePush(first_floating_view_task, last_floating_view_task, t);
query_floating_view_task = t;
t->view = view;
t->row_height_px = row_height_px;
t->rect = rect;
t->view_name = str8_lit("watch");
t->expr = query_expression;
t->is_focused = 1;
t->is_anchored = query_is_anchored;
}
}
}
////////////////////////////
//- rjf: @window_ui_part build all floating views
//
ProfScope("build all floating views")
RD_Font(RD_FontSlot_Code)
UI_FontSize(rd_font_size_from_slot(RD_FontSlot_Main))
UI_TagF("floating")
UI_Focus(ws->menu_bar_focused ? UI_FocusKind_Off : UI_FocusKind_Null)
{
F32 fast_open_rate = 1 - pow_f32(2, (-70.f * ui_state->animation_dt));
F32 slow_open_rate = 1 - pow_f32(2, (-40.f * ui_state->animation_dt));
for(FloatingViewTask *t = first_floating_view_task; t != 0; t = t->next)
{
// rjf: unpack
RD_Cfg *view = t->view;
F32 row_height_px = t->row_height_px;
Rng2F32 rect = t->rect;
String8 view_name = t->view_name;
String8 expr = t->expr;
B32 is_focused = t->is_focused;
B32 is_anchored = t->is_anchored;
F32 open_t = ui_anim(ui_key_from_stringf(ui_key_zero(), "floating_view_open_%p", view), 1.f, .rate = is_anchored ? fast_open_rate : slow_open_rate);
// rjf: build cfg tree
RD_Cfg *expr_root = rd_cfg_child_from_string_or_alloc(view, str8_lit("expression"));
rd_cfg_new(view, str8_lit("selected"));
rd_cfg_new_replace(expr_root, expr);
// rjf: push view regs
rd_push_regs(.view = view->id);
{
String8 view_expr = rd_expr_from_cfg(view);
String8 view_file_path = rd_file_path_from_eval_string(rd_frame_arena(), view_expr);
// NOTE(rjf): we want to only fill out this view's file path slot if it
// evaluates one - this way, a view can use the slot to know the selected
// file path (if there is one). this is useful when pushing commandas which
// apply to a cursor, for example.
if(view_file_path.size != 0)
{
rd_regs()->file_path = view_file_path;
}
}
// rjf: build
UI_Focus(is_focused ? UI_FocusKind_On : UI_FocusKind_Off)
UI_PrefHeight(ui_px(row_height_px, 1.f))
{
// rjf: build top-level container box
UI_Box *container = &ui_nil_box;
UI_Rect(rect) UI_ChildLayoutAxis(Axis2_Y) UI_Squish(0.1f-0.1f*open_t) UI_Transparency(1.f-open_t)
{
container = ui_build_box_from_stringf(UI_BoxFlag_Clickable|
UI_BoxFlag_DrawBorder|
UI_BoxFlag_DrawBackground|
UI_BoxFlag_DrawBackgroundBlur|
UI_BoxFlag_DisableFocusOverlay|
UI_BoxFlag_DrawDropShadow|
(UI_BoxFlag_SquishAnchored*!!is_anchored),
"floating_view_container_%p", view);
}
// rjf: peek press events -> focus hover eval if we have a press
if(!ws->hover_eval_focused)
{
for(UI_Event *evt = 0; ui_next_event(&evt);)
{
if(evt->kind == UI_EventKind_Press &&
evt->key == OS_Key_LeftMouseButton &&
contains_2f32(container->rect, evt->pos))
{
ws->hover_eval_focused = 1;
break;
}
}
}
// rjf: build overlay container for loading animation
UI_Box *loading_overlay_container = &ui_nil_box;
UI_Parent(container) UI_WidthFill UI_HeightFill
{
loading_overlay_container = ui_build_box_from_key(UI_BoxFlag_Floating, ui_key_zero());
}
// rjf: build contents
UI_Parent(container) UI_Focus(is_focused ? UI_FocusKind_Null : UI_FocusKind_Off)
{
ui_set_next_pref_width(ui_pct(1, 0));
ui_set_next_pref_height(ui_pct(1, 0));
ui_set_next_child_layout_axis(Axis2_Y);
UI_Box *view_contents_container = ui_build_box_from_stringf(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "###view_contents_container");
UI_Parent(view_contents_container) UI_WidthFill
{
rd_view_ui(rect);
}
}
// rjf: build loading overlay
{
RD_ViewState *vs = rd_view_state_from_cfg(view);
F32 loading_t = vs->loading_t;
if(loading_t > 0.01f) UI_Parent(loading_overlay_container)
{
rd_loading_overlay(rect, loading_t, vs->loading_progress_v, vs->loading_progress_v_target);
}
}
// rjf: interact with container
UI_Signal sig = ui_signal_from_box(container);
t->signal = sig;
}
// rjf: pop interaction registers; commit if this is focused
RD_Regs *view_regs = rd_pop_regs();
if(is_focused)
{
MemoryCopyStruct(rd_regs(), view_regs);
}
}
}
////////////////////////////
//- rjf: @window_ui_part do special handling of floating view interactions
//
{
//- rjf: hover eval focus rules
if(hover_eval_floating_view_task)
{
UI_Signal sig = hover_eval_floating_view_task->signal;
if(ui_pressed(sig))
{
ws->hover_eval_focused = 1;
}
if(ui_mouse_over(sig) || ws->hover_eval_focused)
{
ws->hover_eval_lastt_us = rd_state->time_in_us;
}
else if(ws->hover_eval_lastt_us+1000000 < rd_state->time_in_us)
{
rd_request_frame();
}
if(ws->hover_eval_focused)
{
for(UI_Event *evt = 0; ui_next_event(&evt);)
{
if(evt->kind == UI_EventKind_Press &&
evt->key == OS_Key_LeftMouseButton &&
!contains_2f32(hover_eval_floating_view_task->rect, evt->pos))
{
ws->hover_eval_focused = 0;
MemoryZeroStruct(&ws->hover_eval_string);
arena_clear(ws->hover_eval_arena);
rd_request_frame();
break;
}
}
}
}
//- rjf: query interactions
if(query_floating_view_task)
{
String8 cmd_name = ws->query_cmd_name;
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_name);
// rjf: any queries which take a file path mutate the debugger's "current path"
if(cmd_kind_info->query.slot == RD_RegSlot_FilePath)
{
RD_Cfg *view = query_floating_view_task->view;
RD_Cfg *query = rd_cfg_child_from_string(view, str8_lit("query"));
RD_Cfg *input = rd_cfg_child_from_string(query, str8_lit("input"));
if(input != &rd_nil_cfg)
{
String8 path_chopped = str8_chop_last_slash(input->first->string);
rd_cmd(RD_CmdKind_SetCurrentPath, .file_path = path_chopped);
}
}
}
}
////////////////////////////
//- rjf: @window_ui_part bottom bar
//
+6 -6
View File
@@ -1172,6 +1172,12 @@ ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, U
ui_state->active_box_key[k] = ui_key_zero();
}
}
//- rjf: escape -> close context menu
if(ui_any_ctx_menu_is_open() && ui_slot_press(UI_EventActionSlot_Cancel))
{
ui_ctx_menu_close();
}
}
internal void
@@ -1179,12 +1185,6 @@ ui_end_build(void)
{
ProfBeginFunction();
//- rjf: escape -> close context menu
if(ui_state->ctx_menu_open != 0 && ui_slot_press(UI_EventActionSlot_Cancel))
{
ui_ctx_menu_close();
}
//- rjf: prune untouched or transient boxes in the cache
ProfScope("ui prune unused boxes")
{