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:
2026-08-12 14:17:05 -07:00
parent 4c3c6a955a
commit 82294844dd
3 changed files with 72 additions and 36 deletions
+9
View File
@@ -147,6 +147,7 @@ static bool IsAppActive(void)
if (app.sel.isDragging || app.sel.isTimeSelecting || app.sel.isFreqSelecting) return true;
if (app.marker.dragging) return true;
if (app.exportMessageTimer > 0.0f) return true; // notification countdown
if (app.autocropNoticeActive) return true; // toast countdown + progress strip
return false;
}
@@ -526,6 +527,7 @@ void ApplyAutoCrop(void)
timePart,
srcPart);
app.autocropNoticeActive = true;
app.autocropNoticeTimer = AUTOCROP_NOTICE_SECONDS;
TraceLog(LOG_INFO, "Auto-crop: %s", app.autocropNoticeMsg);
}
@@ -968,6 +970,13 @@ int main(int argc, char* argv[])
app.hoverStackCount = 0;
app.currentCollision = -1;
for (int i = 0; i < MLNL_KIND_MAX; i++) app.annotationKindEnabled[i] = true;
// Control events are zero-duration log markers about the run, not signals on
// the air. On a busy capture there are thousands of them and they clutter
// the overlay without saying anything about what was transmitted, so they
// start hidden; the per-kind checkbox turns them back on. (Headless
// --render keeps every kind enabled — an export should show what was asked
// for, not a GUI default.)
app.annotationKindEnabled[MLNL_KIND_CONTROL] = false;
app.showScope = true;
app.dividerY = 0.6f; // Start with 60% spectro, 40% scope
app.isDividing = false;