diff --git a/src/df/core/df_core.mdesk b/src/df/core/df_core.mdesk index 3845b61c..a0920e71 100644 --- a/src/df/core/df_core.mdesk +++ b/src/df/core/df_core.mdesk @@ -80,22 +80,23 @@ DF_EntityKindTable: @table(name, name_lower, c_type) DF_CmdParamSlotTable: { - {Window, window, `DF_Handle`} - {Panel, panel, `DF_Handle`} - {DestPanel, dest_panel, `DF_Handle`} - {PrevView, prev_view, `DF_Handle`} - {View, view, `DF_Handle`} - {Entity, entity, `DF_Handle`} - {EntityList, entity_list, `DF_HandleList`} - {String, string, `String8`} - {FilePath, file_path, `String8`} - {TextPoint, text_point, `TxtPt`} - {CmdSpec, cmd_spec, `struct DF_CmdSpec *`} - {VirtualAddr, vaddr, `U64`} - {VirtualOff, voff, `U64`} - {Index, index, `U64`} - {ID, id, `U64`} - {PreferDisassembly, prefer_dasm, `B32`} + {Window, window, `DF_Handle`} + {Panel, panel, `DF_Handle`} + {DestPanel, dest_panel, `DF_Handle`} + {PrevView, prev_view, `DF_Handle`} + {View, view, `DF_Handle`} + {Entity, entity, `DF_Handle`} + {EntityList, entity_list, `DF_HandleList`} + {String, string, `String8`} + {FilePath, file_path, `String8`} + {TextPoint, text_point, `TxtPt`} + {CmdSpec, cmd_spec, `struct DF_CmdSpec *`} + {VirtualAddr, vaddr, `U64`} + {VirtualOff, voff, `U64`} + {Index, index, `U64`} + {ID, id, `U64`} + {PreferDisassembly, prefer_dasm, `B32`} + {ForceConfirm, force_confirm,`B32`} } @table(name, display_string, args_desc) @@ -185,6 +186,10 @@ DF_CoreCmdTable: {CloseWindow 0 0 0 0 0 Window Null Null Null Null 0 0 Window "close_window" "Close Window" "Closes an opened window." "" } {ToggleFullscreen 0 0 0 0 0 Window Null Null Null Null 0 0 Window "toggle_fullscreen" "Toggle Fullscreen" "Toggles fullscreen view on the active window." "" } + //- rjf: confirmations + {ConfirmAccept 1 0 0 0 0 Null Null Null Null Null 0 0 Null "confirm_accept" "Confirm Accept" "Accepts the active confirmation prompt." "" } + {ConfirmCancel 1 0 0 0 0 Null Null Null Null Null 0 0 Null "confirm_cancel" "Confirm Cancel" "Cancels the active confirmation prompt." "" } + //- rjf: panel splitting {ResetToDefaultPanels 0 0 0 0 0 Window Null Null Null Null 0 0 Window "reset_to_default_panels" "Reset To Default Panel Layout" "Resets the window to the default panel layout." "panel" } {NewPanelRight 0 0 0 0 0 Window Panel Null Null Null 0 0 XSplit "new_panel_right" "Split Panel Vertically" "Creates a new panel to the right of the active panel." "panel" } diff --git a/src/df/core/generated/df_core.meta.c b/src/df/core/generated/df_core.meta.c index 87d0345d..d75f14bc 100644 --- a/src/df/core/generated/df_core.meta.c +++ b/src/df/core/generated/df_core.meta.c @@ -54,6 +54,8 @@ DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[] = { str8_lit_comp("open_window"), str8_lit_comp("Opens a new window."), str8_lit_comp(""), str8_lit_comp("Open New Window"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}}, { str8_lit_comp("close_window"), str8_lit_comp("Closes an opened window."), str8_lit_comp(""), str8_lit_comp("Close Window"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}}, { str8_lit_comp("toggle_fullscreen"), str8_lit_comp("Toggles fullscreen view on the active window."), str8_lit_comp(""), str8_lit_comp("Toggle Fullscreen"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}}, +{ str8_lit_comp("confirm_accept"), str8_lit_comp("Accepts the active confirmation prompt."), str8_lit_comp(""), str8_lit_comp("Confirm Accept"), (DF_CmdSpecFlag_OmitFromLists*1) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Null, {0, 0}}, +{ str8_lit_comp("confirm_cancel"), str8_lit_comp("Cancels the active confirmation prompt."), str8_lit_comp(""), str8_lit_comp("Confirm Cancel"), (DF_CmdSpecFlag_OmitFromLists*1) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Null, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Null, {0, 0}}, { str8_lit_comp("reset_to_default_panels"), str8_lit_comp("Resets the window to the default panel layout."), str8_lit_comp("panel"), str8_lit_comp("Reset To Default Panel Layout"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Null, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_Window, {0, 0}}, { str8_lit_comp("new_panel_right"), str8_lit_comp("Creates a new panel to the right of the active panel."), str8_lit_comp("panel"), str8_lit_comp("Split Panel Vertically"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Panel, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_XSplit, {0, 0}}, { str8_lit_comp("new_panel_down"), str8_lit_comp("Creates a new panel at the bottom of the active panel."), str8_lit_comp("panel"), str8_lit_comp("Split Panel Horizontally"), (DF_CmdSpecFlag_OmitFromLists*0) | (DF_CmdSpecFlag_RunKeepsQuery*0) | (DF_CmdSpecFlag_QueryUsesOldInput*0) | (DF_CmdSpecFlag_AppliesToView*0) | (DF_CmdSpecFlag_QueryIsCode*0), {DF_CmdParamSlot_Window, DF_CmdParamSlot_Panel, DF_CmdParamSlot_Null}, DF_CmdQueryRule_Null, DF_IconKind_YSplit, {0, 0}}, diff --git a/src/df/core/generated/df_core.meta.h b/src/df/core/generated/df_core.meta.h index 9c6d63bf..c8cfd51c 100644 --- a/src/df/core/generated/df_core.meta.h +++ b/src/df/core/generated/df_core.meta.h @@ -104,6 +104,8 @@ DF_CoreCmdKind_DecCodeFontScale, DF_CoreCmdKind_OpenWindow, DF_CoreCmdKind_CloseWindow, DF_CoreCmdKind_ToggleFullscreen, +DF_CoreCmdKind_ConfirmAccept, +DF_CoreCmdKind_ConfirmCancel, DF_CoreCmdKind_ResetToDefaultPanels, DF_CoreCmdKind_NewPanelRight, DF_CoreCmdKind_NewPanelDown, @@ -368,6 +370,7 @@ DF_CmdParamSlot_VirtualOff, DF_CmdParamSlot_Index, DF_CmdParamSlot_ID, DF_CmdParamSlot_PreferDisassembly, +DF_CmdParamSlot_ForceConfirm, DF_CmdParamSlot_COUNT } DF_CmdParamSlot; @@ -407,6 +410,7 @@ U64 voff; U64 index; U64 id; B32 prefer_dasm; +B32 force_confirm; }; DF_CORE_VIEW_RULE_EVAL_RESOLUTION_FUNCTION_DEF(array); diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index 24b1846c..93d922cf 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -1145,6 +1145,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D { ProfBeginFunction(); B32 window_is_focused = os_window_is_focused(ws->os); + B32 confirm_open = df_gfx_state->confirm_active; B32 hover_eval_is_open = (ws->hover_eval_string.size != 0 && ws->hover_eval_first_frame_idx+20 < ws->hover_eval_last_frame_idx && df_frame_index()-ws->hover_eval_last_frame_idx < 20); B32 any_query_is_focused = !df_panel_is_nil(ws->focused_panel) && !df_view_is_nil(df_query_view_from_panel(ws->focused_panel)); if(!window_is_focused) @@ -3861,6 +3862,55 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D scratch_end(scratch); } + //- rjf: confirmation popup + { + if(df_gfx_state->confirm_t > 0.005f) UI_Focus(1) UI_TextAlignment(UI_TextAlign_Center) + { + Vec2F32 window_dim = dim_2f32(window_rect); + UI_Box *bg_box = &ui_g_nil_box; + UI_Rect(window_rect) UI_ChildLayoutAxis(Axis2_X) + { + Vec4F32 bg_color = ui_top_background_color(); + bg_color.w *= df_gfx_state->confirm_t; + ui_set_next_blur_size(10*df_gfx_state->confirm_t); + ui_set_next_background_color(bg_color); + bg_box = ui_build_box_from_stringf(UI_BoxFlag_FixedSize|UI_BoxFlag_Floating|UI_BoxFlag_Clickable|UI_BoxFlag_Scroll|UI_BoxFlag_DefaultFocusNav|UI_BoxFlag_DrawBackgroundBlur|UI_BoxFlag_DrawBackground, "###confirm_popup_%p", ws); + } + if(df_gfx_state->confirm_active) UI_Parent(bg_box) + { + ui_ctx_menu_close(); + UI_WidthFill UI_PrefHeight(ui_children_sum(1.f)) UI_Column UI_Padding(ui_pct(1, 0)) + { + UI_FontSize(ui_top_font_size()*2.f) UI_PrefHeight(ui_em(3.f, 1.f)) ui_label(df_gfx_state->confirm_title); + UI_PrefHeight(ui_em(3.f, 1.f)) UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_WeakText)) ui_label(df_gfx_state->confirm_msg); + ui_spacer(ui_em(1.5f, 1.f)); + UI_Row UI_Padding(ui_pct(1.f, 0.f)) UI_WidthFill UI_PrefHeight(ui_em(5.f, 1.f)) + { + UI_CornerRadius00(ui_top_font_size()*0.25f) + UI_CornerRadius01(ui_top_font_size()*0.25f) + if(ui_buttonf("Cancel").clicked || os_key_press(ui_events(), ui_window(), 0, OS_Key_Esc)) + { + DF_CmdParams p = df_cmd_params_zero(); + df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ConfirmCancel)); + } + UI_CornerRadius10(ui_top_font_size()*0.25f) + UI_CornerRadius11(ui_top_font_size()*0.25f) + UI_BackgroundColor(df_rgba_from_theme_color(DF_ThemeColor_ActionBackground)) + UI_TextColor(df_rgba_from_theme_color(DF_ThemeColor_ActionText)) + UI_BorderColor(df_rgba_from_theme_color(DF_ThemeColor_ActionBorder)) + if(ui_buttonf("OK").clicked) + { + DF_CmdParams p = df_cmd_params_zero(); + df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ConfirmAccept)); + } + } + ui_spacer(ui_em(3.f, 1.f)); + } + } + ui_signal_from_box(bg_box); + } + } + //- rjf: build auto-complete lister ProfScope("build autocomplete lister") if(!ws->autocomp_force_closed && !ui_key_match(ws->autocomp_root_key, ui_key_zero()) && ws->autocomp_last_frame_idx+1 >= df_frame_index()) @@ -10898,6 +10948,7 @@ df_gfx_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, DF_StateDe df_gfx_state->num_frames_requested = 2; df_gfx_state->hist = hist; df_gfx_state->key_map_arena = arena_alloc(); + df_gfx_state->confirm_arena = arena_alloc(); df_gfx_state->view_spec_table_size = 256; df_gfx_state->view_spec_table = push_array(arena, DF_ViewSpec *, df_gfx_state->view_spec_table_size); df_gfx_state->view_rule_spec_table_size = 1024; @@ -10942,6 +10993,17 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds) arena_clear(df_gfx_state->frame_arena); df_gfx_state->hover_line_set_this_frame = 0; + //- rjf: animate confirmation + { + F32 rate = 1 - pow_f32(2, (-10.f * df_dt())); + B32 confirm_open = df_gfx_state->confirm_active; + df_gfx_state->confirm_t += rate * ((F32)!!confirm_open-df_gfx_state->confirm_t); + if(abs_f32(df_gfx_state->confirm_t - (F32)!!confirm_open) > 0.005f) + { + df_gfx_request_frame(); + } + } + //- rjf: capture is active? -> keep rendering if(ProfIsCapturing()) { @@ -11002,10 +11064,32 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds) DF_Window *ws = df_window_from_handle(params.window); if(ws != 0) { + DF_EntityList running_processes = df_query_cached_entity_list_with_kind(DF_EntityKind_Process); + + // NOTE(rjf): if this is the last window, and targets are running, but + // this command is not force-confirmed, then we should query the user + // to ensure they want to close the debugger before exiting + UI_Key key = ui_key_from_string(ui_key_zero(), str8_lit("lossy_exit_confirmation")); + if(!ui_key_match(key, df_gfx_state->confirm_key) && running_processes.count != 0 && ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && !params.force_confirm) + { + df_gfx_state->confirm_key = key; + df_gfx_state->confirm_active = 1; + arena_clear(df_gfx_state->confirm_arena); + MemoryZeroStruct(&df_gfx_state->confirm_cmds); + df_gfx_state->confirm_title = push_str8f(df_gfx_state->confirm_arena, "Are you sure you want to exit?"); + df_gfx_state->confirm_msg = push_str8f(df_gfx_state->confirm_arena, "The debugger is still attached to %slive process%s.", + running_processes.count == 1 ? "a " : "", + running_processes.count == 1 ? "" : "es"); + DF_CmdParams p = df_cmd_params_from_window(ws); + p.force_confirm = 1; + df_cmd_params_mark_slot(&p, DF_CmdParamSlot_ForceConfirm); + df_cmd_list_push(df_gfx_state->confirm_arena, &df_gfx_state->confirm_cmds, &p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CloseWindow)); + } + // NOTE(rjf): if this is the last window, and it is being closed, then // we need to auto-save, and provide one last chance to process saving // commands. after doing so, we can retry. - if(ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && df_gfx_state->last_window_queued_save == 0) + else if(ws == df_gfx_state->first_window && ws == df_gfx_state->last_window && df_gfx_state->last_window_queued_save == 0) { df_gfx_state->last_window_queued_save = 1; { @@ -11047,6 +11131,22 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds) } }break; + //- rjf: confirmations + case DF_CoreCmdKind_ConfirmAccept: + { + df_gfx_state->confirm_active = 0; + df_gfx_state->confirm_key = ui_key_zero(); + for(DF_CmdNode *n = df_gfx_state->confirm_cmds.first; n != 0; n = n->next) + { + df_push_cmd__root(&n->cmd.params, n->cmd.spec); + } + }break; + case DF_CoreCmdKind_ConfirmCancel: + { + df_gfx_state->confirm_active = 0; + df_gfx_state->confirm_key = ui_key_zero(); + }break; + //- rjf: commands with implications for graphical systems, but generated // without context needed - pick selected window & dispatch case DF_CoreCmdKind_SelectThread: diff --git a/src/df/gfx/df_gfx.h b/src/df/gfx/df_gfx.h index 194fd0c6..59979d0d 100644 --- a/src/df/gfx/df_gfx.h +++ b/src/df/gfx/df_gfx.h @@ -681,6 +681,15 @@ struct DF_GfxState DF_CmdSpec *bind_change_cmd_spec; DF_Binding bind_change_binding; + // rjf: confirmation popup state + UI_Key confirm_key; + B32 confirm_active; + F32 confirm_t; + Arena *confirm_arena; + DF_CmdList confirm_cmds; + String8 confirm_title; + String8 confirm_msg; + // rjf: string search state Arena *string_search_arena; String8 string_search_string; diff --git a/src/raddbg/raddbg.c b/src/raddbg/raddbg.c index 5a6e2cfc..b70ac1c9 100644 --- a/src/raddbg/raddbg.c +++ b/src/raddbg/raddbg.c @@ -66,7 +66,7 @@ update_and_render(OS_Handle repaint_window_handle, void *user_data) U64 begin_time_us = os_now_microseconds(); //- rjf: bind change - if(df_gfx_state->bind_change_active) + if(!df_gfx_state->confirm_active && df_gfx_state->bind_change_active) { if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc)) { diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index ac79e8be..0c3ee3ee 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -2568,8 +2568,10 @@ ui_signal_from_box(UI_Box *box) } //- rjf: set hovering status - result.hovering = mouse_is_over && (ui_key_match(ui_state->active_box_key[Side_Min], ui_key_zero()) || - ui_key_match(ui_state->active_box_key[Side_Min], box->key)); + result.hovering = mouse_is_over && ((ui_key_match(ui_state->hot_box_key, ui_key_zero()) || + ui_key_match(ui_state->hot_box_key, box->key)) && + (ui_key_match(ui_state->active_box_key[Side_Min], ui_key_zero()) || + ui_key_match(ui_state->active_box_key[Side_Min], box->key))); result.mouse_over = mouse_is_over; //- rjf: clicking in default nav -> set navigation state to this box