feat: hide control annotations by default, demote auto-crop notice to a toast
Control events are zero-duration log markers about the run rather than signals on the air. A busy capture carries thousands, and they clutter the overlay without saying anything about what was transmitted, so the GUI now starts with that kind hidden; the per-kind checkbox brings it back. The headless --render path still enables every kind — an export should render what was asked for, not a GUI preference. The auto-crop notice was a full modal: it dimmed the window, took focus, and demanded a click before the user could look at the file they had just opened. Auto-crop is a helpful default, not a decision worth blocking on. It is now a bottom-right toast offering Undo / Dismiss, with a progress strip showing the remaining time so it doesn't just vanish mid-read, and it is out of UiModalOpen() so it no longer swallows keys or blocks the spectrogram underneath. The countdown only advances while the window is focused, so a crop applied during a background load is still readable when the user returns. The per-frame step is clamped: auto-crop fires on the frame right after the blocking STFT compute, and GetFrameTime() there reports the entire compute, which drained the whole 5 s budget at once and made the toast flash by in an instant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8ZWfr5XZyyDttvkhJUgHN
This commit is contained in:
@@ -826,6 +826,13 @@ void DrawAboutDialog(void)
|
||||
// [OK] — dismiss; keep the cropped view
|
||||
// Esc closes (= OK). Clicks outside the panel do nothing (avoids losing the
|
||||
// crop by missing a button by a few px).
|
||||
// Auto-crop notice, as a bottom-right toast rather than a modal.
|
||||
//
|
||||
// Auto-crop is a helpful default, not a decision worth blocking on: a modal
|
||||
// stole focus and demanded a click before the user could look at the file they
|
||||
// had just opened. The toast states what happened, offers Undo, and expires on
|
||||
// its own — but only counts down while the window is focused, so a crop applied
|
||||
// during a background load is still there to read when the user comes back.
|
||||
void DrawAutocropNotice(void)
|
||||
{
|
||||
if (!app.autocropNoticeActive) return;
|
||||
@@ -833,52 +840,52 @@ void DrawAutocropNotice(void)
|
||||
int sw = GetScreenWidth();
|
||||
int sh = GetScreenHeight();
|
||||
|
||||
DrawRectangle(0, 0, sw, sh, Fade(BLACK, 0.55f));
|
||||
const char* title = "Auto-crop applied";
|
||||
float titleW = MeasureTextScaled(title, 13);
|
||||
float msgW = MeasureTextScaled(app.autocropNoticeMsg, 11);
|
||||
float btnW = 76 * scale, btnH = 24 * scale;
|
||||
|
||||
float pw = 460 * scale;
|
||||
float ph = 170 * scale;
|
||||
Rectangle panel = { (sw - pw) * 0.5f, (sh - ph) * 0.5f, pw, ph };
|
||||
DrawRectangleRec(panel, (Color){ 30, 30, 40, 255 });
|
||||
DrawRectangleLinesEx(panel, 2, (Color){ 160, 130, 200, 255 });
|
||||
float pw = fmaxf(fmaxf(titleW, msgW) + 28 * scale, btnW * 2 + 44 * scale);
|
||||
float ph = 92 * scale;
|
||||
float margin = 16 * scale;
|
||||
Rectangle panel = { sw - pw - margin, sh - ph - margin, pw, ph };
|
||||
|
||||
DrawTextScaled("Auto-crop applied",
|
||||
panel.x + 20 * scale, panel.y + 16 * scale, 16,
|
||||
DrawRectangleRec(panel, (Color){ 28, 28, 36, 240 });
|
||||
DrawRectangleLinesEx(panel, 1, (Color){ 150, 120, 190, 255 });
|
||||
|
||||
DrawTextScaled(title, panel.x + 12 * scale, panel.y + 10 * scale, 13,
|
||||
(Color){ 200, 170, 240, 255 });
|
||||
DrawTextScaled(app.autocropNoticeMsg, panel.x + 12 * scale, panel.y + 30 * scale,
|
||||
11, LIGHTGRAY);
|
||||
|
||||
// Body: word-wrap not needed for the short message ApplyAutoCrop builds.
|
||||
DrawTextScaled(app.autocropNoticeMsg,
|
||||
panel.x + 20 * scale, panel.y + 50 * scale, 13, LIGHTGRAY);
|
||||
float btnY = panel.y + ph - btnH - 10 * scale;
|
||||
Rectangle undoBtn = { panel.x + 12 * scale, btnY, btnW, btnH };
|
||||
Rectangle okBtn = { panel.x + pw - btnW - 12 * scale, btnY, btnW, btnH };
|
||||
|
||||
float btnW = 110 * scale, btnH = 32 * scale;
|
||||
float btnY = panel.y + ph - btnH - 16 * scale;
|
||||
Rectangle uncropBtn = { panel.x + 20 * scale, btnY, btnW, btnH };
|
||||
Rectangle okBtn = { panel.x + pw - btnW - 20*scale, btnY, btnW, btnH };
|
||||
bool undoHover = CheckCollisionPointRec(GetMousePosition(), undoBtn);
|
||||
bool okHover = CheckCollisionPointRec(GetMousePosition(), okBtn);
|
||||
|
||||
bool uncropHover = CheckCollisionPointRec(GetMousePosition(), uncropBtn);
|
||||
bool okHover = CheckCollisionPointRec(GetMousePosition(), okBtn);
|
||||
|
||||
DrawPanelBox(uncropBtn,
|
||||
uncropHover ? (Color){ 100, 60, 60, 255 } : (Color){ 70, 40, 40, 255 },
|
||||
DrawPanelBox(undoBtn,
|
||||
undoHover ? (Color){ 100, 60, 60, 255 } : (Color){ 70, 40, 40, 255 },
|
||||
(Color){ 230, 160, 160, 255 });
|
||||
DrawTextScaled("Uncrop",
|
||||
uncropBtn.x + btnW * 0.5f - MeasureTextScaled("Uncrop", 14) * 0.5f,
|
||||
uncropBtn.y + 8 * scale, 14, WHITE);
|
||||
DrawTextScaled("Undo", undoBtn.x + btnW * 0.5f - MeasureTextScaled("Undo", 12) * 0.5f,
|
||||
undoBtn.y + 5 * scale, 12, WHITE);
|
||||
|
||||
DrawPanelBox(okBtn,
|
||||
okHover ? (Color){ 60, 90, 60, 255 } : (Color){ 40, 70, 40, 255 },
|
||||
(Color){ 160, 220, 160, 255 });
|
||||
DrawTextScaled("OK",
|
||||
okBtn.x + btnW * 0.5f - MeasureTextScaled("OK", 14) * 0.5f,
|
||||
okBtn.y + 8 * scale, 14, WHITE);
|
||||
okHover ? (Color){ 60, 90, 60, 255 } : (Color){ 45, 60, 45, 255 },
|
||||
(Color){ 160, 200, 160, 255 });
|
||||
DrawTextScaled("Dismiss", okBtn.x + btnW * 0.5f - MeasureTextScaled("Dismiss", 12) * 0.5f,
|
||||
okBtn.y + 5 * scale, 12, WHITE);
|
||||
|
||||
DrawTextScaled("Esc or Enter = OK", panel.x + pw - 156 * scale,
|
||||
panel.y + ph - 14 * scale, 10, GRAY);
|
||||
// Remaining-time strip along the bottom edge, so the auto-expiry is visible
|
||||
// rather than the toast just vanishing mid-read.
|
||||
float frac = app.autocropNoticeTimer / AUTOCROP_NOTICE_SECONDS;
|
||||
if (frac < 0.0f) frac = 0.0f;
|
||||
if (frac > 1.0f) frac = 1.0f;
|
||||
DrawRectangle((int)panel.x, (int)(panel.y + ph - 2 * scale),
|
||||
(int)(pw * frac), (int)(2 * scale), (Color){ 150, 120, 190, 200 });
|
||||
|
||||
// Hit handling. Esc/Enter dismiss (keep crop). Uncrop restores full view.
|
||||
if (IsKeyPressed(KEY_ESCAPE) || IsKeyPressed(KEY_ENTER)) {
|
||||
app.autocropNoticeActive = false;
|
||||
}
|
||||
if (uncropHover && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||
if (undoHover && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||
app.displayMaxFreqHz = 0.0f;
|
||||
app.view.start = 0.0f; app.view.end = 1.0f;
|
||||
app.view.freqStart = 0.0f; app.view.freqEnd = 1.0f;
|
||||
@@ -887,4 +894,18 @@ void DrawAutocropNotice(void)
|
||||
} else if (okHover && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||
app.autocropNoticeActive = false;
|
||||
}
|
||||
|
||||
// Count down only while focused: a toast that expired behind another window
|
||||
// would be gone before it was ever seen.
|
||||
//
|
||||
// The step is clamped because auto-crop fires on the frame right after the
|
||||
// blocking STFT compute, and GetFrameTime() on that frame reports the whole
|
||||
// compute — seconds on a long capture. Unclamped, that single frame drained
|
||||
// the entire 5 s budget and the toast flashed by in an instant.
|
||||
if (IsWindowFocused()) {
|
||||
float dt = GetFrameTime();
|
||||
if (dt > 0.1f) dt = 0.1f; // ignore load hitches / debugger stalls
|
||||
app.autocropNoticeTimer -= dt;
|
||||
if (app.autocropNoticeTimer <= 0.0f) app.autocropNoticeActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user