From 35b7290e3be2e61477de6c12ad954675cd743ff7 Mon Sep 17 00:00:00 2001 From: Musa Mahmood Date: Sun, 18 Oct 2026 17:29:31 -0400 Subject: [PATCH] [0.1.37] Settings and Debug Panel buttons in the main panel's top right; window buttons on their own row --- check_targets.jai | 2 +- first.jai | 2 +- src/app/viewer.jai | 35 ++++++++++++++++++++++++++++++++- src/ui/imgui.jai | 49 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/check_targets.jai b/check_targets.jai index cc23f12..b7c4c00 100644 --- a/check_targets.jai +++ b/check_targets.jai @@ -52,7 +52,7 @@ APP_NAME :: "mcap"; APP_FOLDER_NAME :: "mcap"; GIT_BRANCH :: "check"; GIT_HASH :: "-"; -VERSION :: "0.1.35"; +VERSION :: "0.1.37"; BUILD_DATE :: "-"; DEBUG :: true; COMPILE_TESTS :: true; diff --git a/first.jai b/first.jai index 7e35dd1..b118657 100644 --- a/first.jai +++ b/first.jai @@ -8,7 +8,7 @@ APP_NAME :: "mcap"; APP_FOLDER_NAME :: "mcap"; APP_BUNDLE_IDENTIFIER :: "com.example.mcap"; -VERSION :: "0.1.35"; +VERSION :: "0.1.37"; // Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated // builds never write over the ones you run by hand. build_directory := "bin"; diff --git a/src/app/viewer.jai b/src/app/viewer.jai index 9ca7ed4..9741763 100644 --- a/src/app/viewer.jai +++ b/src/app/viewer.jai @@ -1097,6 +1097,10 @@ viewer_ui :: () { using ImGui; viewer_watch_frame(); // files added or deleted outside the app viewer_frame_gpu_work(); + start := GetCursorPos(); + viewer_corner_buttons(start); + SetCursorPos(start); + if viewer.scan_error { TextColored(app_theme.palette.warning, "%", viewer.scan_error); SameLine(); @@ -1141,7 +1145,7 @@ viewer_ui :: () { using ImGui; SameLine(); SetNextItemWidth(160); SliderFloat("Size", *viewer.thumb_size, 64, MCAP_PREVIEW_MAX_SIDE, "%.0f px"); - SameLine(); + // The window buttons, on the row under the search. // The OCR window (app/ocr_ui.jai): the model, GPU or CPU, and the library scan. if Button("OCR") ocr_ui.window_open = !ocr_ui.window_open; SameLine(); @@ -1186,6 +1190,35 @@ viewer_ui :: () { using ImGui; viewer_full_window(); } +// The settings and Debug Panel buttons in the top right corner, on the search's row, sized to its text. +viewer_corner_buttons :: (start: Vector2) { using ImGui; + size := floor(GetTextLineHeight()); + gap := floor(imgui_scale(4.0)); + x := GetWindowContentRegionMax().x - TOP_RIGHT_PADDING - 2 * size - gap; + y := start.y + GetStyle().FramePadding.y; // level with the text in the row's frames + draw := GetWindowDrawList(); + text := GetColorU32(.Text); + + SetCursorPos(.{x, y}); + settings_clicked, settings_min := imgui_icon_button("##mcap_open_settings", size); + settings_path := app_user_settings_path(); + ButtonTooltip(tprint("Opens %", settings_path)); + imgui_draw_gear(draw, settings_min, size, text); + if settings_clicked { + if !file_exists(settings_path) app_save_user_settings_file(); + app_open_config_file(settings_path); + } + + SetCursorPos(.{x + size + gap, y}); + debug_clicked, debug_min := imgui_icon_button("##mcap_toggle_debug_panel", size); + hotkey := to_string(toggle_debug_panel_hotkey); + tooltip := ifx app.show_debug_panel then "Hides the Debug Panel" else "Shows the Debug Panel"; + ButtonTooltip(ifx hotkey.count then tprint("% (%)", tooltip, hotkey) else tooltip); + // In the accent while the panel shows. + imgui_draw_bug(draw, debug_min, size, ifx app.show_debug_panel then GetColorU32(app_theme.palette.accent) else text); + if debug_clicked app.show_debug_panel = !app.show_debug_panel; +} + // The trash button's square in a thumbnail whose top left is at top_left. viewer_trash_rect :: (top_left: ImGui.ImVec2, cell: float) -> ImGui.ImVec2, ImGui.ImVec2 { size := clamp(cell * 0.16, 18, 30); diff --git a/src/ui/imgui.jai b/src/ui/imgui.jai index 0194cd4..7d1e366 100644 --- a/src/ui/imgui.jai +++ b/src/ui/imgui.jai @@ -180,8 +180,6 @@ imgui_text_top_right :: (text: string, text_color: Vector4) { original_cursor_pos := GetCursorPos(); windowRegionMax := GetWindowContentRegionMax(); text_size := CalcTextSize(text); - - TOP_RIGHT_PADDING : float32 : 10; cursorX: float32 = windowRegionMax.x - text_size.x - TOP_RIGHT_PADDING; cursorY: float32 = 0 + TOP_RIGHT_PADDING; @@ -242,13 +240,13 @@ CenteredButton :: (label: string) -> bool { using ImGui; return Button(label_c); } +TOP_RIGHT_PADDING : float32 : 10; + RightAlignedText :: (text: string, text_color: Vector4) { using ImGui; text_size := CalcTextSize(text); available := GetWindowContentRegionMax().x; - - TOP_RIGHT_PADDING : float32 : 10; // cursorX: float32 = available.x - text_size.x - TOP_RIGHT_PADDING; // cursorY: float32 = 0 + TOP_RIGHT_PADDING; @@ -261,6 +259,49 @@ RightAlignedText :: (text: string, text_color: Vector4) { TextColored(text_color, "%", text); } +// A small square button with a glyph drawn in it, at the cursor: the item's frame color behind it +// while hovered or held. The glyph is the caller's to draw, from the returned rect. +imgui_icon_button :: (id: *u8, size: float) -> clicked: bool, min: Vector2 { + using ImGui; + clicked := InvisibleButton(id, .{size, size}); + min := GetItemRectMin(); + if IsItemHovered() { + draw := GetWindowDrawList(); + background := ifx IsItemActive() then Col.ButtonActive else Col.ButtonHovered; + draw.AddRectFilled(draw, min, .{min.x + size, min.y + size}, GetColorU32(background), GetStyle().FrameRounding); + } + return clicked, min; +} + +// A gear in the size × size square at min: eight teeth round a ring. +imgui_draw_gear :: (draw: *ImGui.ImDrawList, min: Vector2, size: float, color: u32) { + r := size * 0.5; + c := Vector2.{min.x + r, min.y + r}; + tooth := max(r * 0.28, 1.0); + for 0..7 { + angle := it * TAU / 8; + d := Vector2.{cos(angle), sin(angle)}; + draw.AddLine(draw, .{c.x + d.x * r * 0.45, c.y + d.y * r * 0.45}, .{c.x + d.x * r * 0.9, c.y + d.y * r * 0.9}, color, tooth); + } + draw.AddCircle(draw, c, r * 0.5, color, 0, max(r * 0.26, 1.0)); +} + +// A bug in the size × size square at min: a body, a head, antennae and three legs a side. +imgui_draw_bug :: (draw: *ImGui.ImDrawList, min: Vector2, size: float, color: u32) { + r := size * 0.5; + c := Vector2.{min.x + r, min.y + r}; + stroke := max(r * 0.13, 1.0); + draw.AddEllipseFilled(draw, .{c.x, c.y + r * 0.18}, .{r * 0.4, r * 0.55}, color); + draw.AddCircleFilled(draw, .{c.x, c.y - r * 0.5}, r * 0.24, color); + for side: float.[-1, 1] { + draw.AddLine(draw, .{c.x + side * r * 0.12, c.y - r * 0.68}, .{c.x + side * r * 0.38, c.y - r * 0.95}, color, stroke); + for leg: 0..2 { + y := c.y + r * (-0.1 + 0.3 * leg); + draw.AddLine(draw, .{c.x + side * r * 0.3, y}, .{c.x + side * r * 0.85, y + r * (-0.2 + 0.2 * leg)}, color, stroke); + } + } +} + RightAlignedButton :: (label: string) -> bool #expand { using ImGui; // start := GetCursorPos(); label_c := to_c_string(label,, temp);