ui: compact events block in the dock

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Sonnet 4.6 committed 2026-11-03 06:48:35 +01:00
1 parent b08494dd36
commit baf1c73b21
5 files changed
+287 -5

No files matched your search

+28 -4
View File
@@ -52,6 +52,7 @@ void sync_events_availability(App& app) {
// the button that shows it must not go with the switch. `events_cached` is refreshed once a second
// from the tick -- the store's list is a directory read, not a per-frame question.
const bool available = app.event_recording || app.replaying || app.events_cached;
dock_events_set_sensitive(app, available);
gtk_widget_set_visible(app.events_toggle, available);
if (!available) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app.events_toggle), FALSE);
@@ -132,11 +133,14 @@ bool record_event(App& app) {
set_notice(app, "event cached: " + std::to_string(table.columns.size()) + " signals, " +
std::to_string(table.rows()) + " samples",
3);
// Only while the pane is on screen: rebuilding every row per latch for a column nobody opened is
// work the tick owes nothing, and the toggle's own handler refreshes when it is asked for.
// The dock follows every latch; the pane's rows (with pictures) only while the pane is on screen:
// rebuilding them per latch for a column nobody opened is work the tick owes nothing, and the toggle's
// own handler refreshes when it is asked for.
if (app.events_revealer != nullptr &&
gtk_revealer_get_child_revealed(GTK_REVEALER(app.events_revealer))) {
refresh_events_pane(app);
} else {
dock_rebuild_events(app);
}
return true;
}
@@ -580,6 +584,7 @@ bool build_event_group(App& app, const std::vector<EventRecord>& records, EventG
// Fit on open, once: the group's own span, framed.
// As above: the tick pins the axis to the group's own span and marks the trigger.
sync_events_availability(app);
mark_events_on_screen(app); // the dock marks the group's members
return true;
}
@@ -781,6 +786,17 @@ void attach_preview_zoom(GtkWidget* frame, const std::string& path) {
/// screen. One function because two callers write it -- the row builder and the in-place
/// re-mark -- and a mark that reads differently depending on which of them last ran would be
/// worse than no mark.
bool event_on_screen(const App& app, const EventRecord& r) {
return std::string(app.waveform_name) == r.file;
}
/// Whether the event is one of the members of the comparison or accumulation on screen.
bool event_in_group(const App& app, const EventRecord& r) {
if (app.group_kind == EventGroupKind::None) return false;
return std::any_of(app.group.begin(), app.group.end(),
[&](const EventRecord& g) { return g.file == r.file; });
}
std::string event_row_title(App& app, const EventRecord& r) {
const std::time_t t = static_cast<std::time_t>(r.wall_time);
std::tm tm{};
@@ -789,11 +805,12 @@ std::string event_row_title(App& app, const EventRecord& r) {
std::strftime(clock, sizeof(clock), "%H:%M:%S", &tm);
char title[160];
std::snprintf(title, sizeof(title), "%s %.3f s%s", clock, r.duration_s,
std::string(app.waveform_name) == r.file ? " (on screen)" : "");
event_on_screen(app, r) ? " (on screen)" : "");
return title;
}
void mark_events_on_screen(App& app) {
dock_mark_events(app);
if (app.events_list == nullptr) return;
for (GtkWidget* row = gtk_widget_get_first_child(app.events_list); row != nullptr;
row = gtk_widget_get_next_sibling(row)) {
@@ -811,7 +828,7 @@ void mark_events_on_screen(App& app) {
/// the whole pane describes whichever event happens to be newest rather than the one being read. The
/// numbers are one line each: a list that has to load the samples to describe an event is a list that
/// cannot show thirty of them.
void refresh_events_pane(App& app) {
static void rebuild_events_pane_list(App& app) {
if (app.events_list == nullptr) return;
while (GtkWidget* child = gtk_widget_get_first_child(app.events_list)) {
gtk_list_box_remove(GTK_LIST_BOX(app.events_list), child);
@@ -955,6 +972,13 @@ void refresh_events_pane(App& app) {
}
}
/// Both views of the cache, rebuilt from the same list: the pane's rows (with their pictures) and the
/// dock's compact ones. The dock follows even when the pane's list does not exist.
void refresh_events_pane(App& app) {
rebuild_events_pane_list(app);
dock_rebuild_events(app);
}
/// Clicking a row replays it: the same loader `--open` uses, on the event's file. The acquisition is
/// stopped by `replay_event()` -- a live producer would overwrite the samples within a frame -- so the
/// pane's own *Live* button is the way back.