Feature/source plugins #1
No files matched your search
+3
-1
@@ -5172,8 +5172,10 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
gtk_widget_set_valign(app->measures_panel, GTK_ALIGN_START);
|
||||
slideover_add_page(*app, "cursors", "Cursors", app->cursors_panel);
|
||||
slideover_add_page(*app, "measure", "Measurements", app->measures_panel);
|
||||
// The dock is the compact readouts alone (step 4 adds the events to it).
|
||||
// The dock: the compact readouts, and under them the compact events list.
|
||||
gtk_box_append(GTK_BOX(app->dock_page), build_readouts_dock(*app));
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
gtk_box_append(GTK_BOX(app->dock_page), build_events_dock(*app));
|
||||
|
||||
// The cursors start off, so their readout starts insensitive -- stated here by the one place that
|
||||
// knows the default, and the *body* and not the panel, because the panel's heading carries the
|
||||
|
||||
@@ -809,6 +809,11 @@ struct App {
|
||||
/// to. 240 by default: enough for the preview and the numbers beside it, and not a plot's worth.
|
||||
GtkWidget* events_divider = nullptr;
|
||||
int events_pane_width = 240;
|
||||
/// The Events block of the dock: the box, its list (compact rows, one per `EventRecord`, the record
|
||||
/// attached to each row as `udpscope-event`) and the Manage button.
|
||||
GtkWidget* dock_events_box = nullptr;
|
||||
GtkWidget* dock_events_list = nullptr;
|
||||
GtkWidget* dock_events_manage = nullptr;
|
||||
/// The comparison's second plot: its own placeholder, area and `RowSet`, and the paned that holds
|
||||
/// the two side by side. `rows_b` exists only once the second area is built, and the area is hidden
|
||||
/// (so the paned gives it no room and no handle) unless a comparison is up.
|
||||
@@ -1786,6 +1791,20 @@ void dock_rebuild_measures(App& app);
|
||||
void dock_cursor_cell(App& app, std::size_t line, const std::string& a, const std::string& b,
|
||||
const std::string& d);
|
||||
void dock_measure_value(App& app, std::size_t i, const std::string& text);
|
||||
/// The Events block under the readouts (`src/ui_dock.cpp`): compact rows (marker, title, time; no
|
||||
/// thumbnails) from `event_store->list()`, newest first, and a Manage button for the `events` page.
|
||||
/// `dock_rebuild_events()` follows every change of what is *cached*, `dock_mark_events()` every change
|
||||
/// of what is *on screen* (in place, no rebuild), `dock_events_set_sensitive()` is the one writer of the
|
||||
/// block's sensitivity. All three are no-ops before the block is built.
|
||||
GtkWidget* build_events_dock(App& app);
|
||||
void dock_rebuild_events(App& app);
|
||||
void dock_mark_events(App& app);
|
||||
void dock_events_set_sensitive(App& app, bool available);
|
||||
/// A row's first line, and the two questions its mark is made of (`ui_events.cpp`): the one function
|
||||
/// for the pane and the dock, so the two cannot disagree about which event is on screen.
|
||||
std::string event_row_title(App& app, const EventRecord& r);
|
||||
bool event_on_screen(const App& app, const EventRecord& r);
|
||||
bool event_in_group(const App& app, const EventRecord& r);
|
||||
/// The signal chip of a measurement's row, in the colour every other view names that signal with.
|
||||
std::string measure_chip_markup(App& app, const std::string& signal);
|
||||
std::string row_unit(App& app, int row);
|
||||
|
||||
+163
@@ -8,13 +8,20 @@
|
||||
// once and hand the same variable to the page's label and to `dock_cursor_cell()` / `dock_measure_value()`,
|
||||
// so the two views cannot disagree. Every label is ellipsized at a fixed `width-chars`, so the dock's
|
||||
// layout does not depend on what a value reads; the full text is the label's tooltip.
|
||||
//
|
||||
// Below them, the Events block: one compact row per cached event (marker, title, time; no thumbnails),
|
||||
// a Manage button for the full list, and one click on a row to replay it. Its rows are built from the
|
||||
// same `event_store->list()` and the same `event_row_title()` as the events pane, so the two cannot
|
||||
// disagree.
|
||||
|
||||
#include "ui_app.hpp"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace udpscope {
|
||||
namespace ui_impl {
|
||||
@@ -226,5 +233,161 @@ void dock_measure_value(App& app, std::size_t i, const std::string& text) {
|
||||
set_value_text(app.dock_measure_labels[i], text);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// The widths of an event row's three cells, in characters. Fixed, so the Events block is as wide with
|
||||
/// one event as with thirty (the marker is one cell wide even when it is empty).
|
||||
constexpr int kEventTitleChars = 18;
|
||||
constexpr int kEventTimeChars = 8;
|
||||
|
||||
/// The one-character mark of a row: filled for the event on screen, hollow for a member of the group on
|
||||
/// screen, blank for the rest. Both questions are the ones `event_row_title()` asks (`ui_events.cpp`).
|
||||
const char* dock_event_marker(const App& app, const EventRecord& r) {
|
||||
if (event_on_screen(app, r)) return "\xE2\x97\x8F"; // U+25CF black circle
|
||||
if (event_in_group(app, r)) return "\xE2\x97\x8B"; // U+25CB white circle
|
||||
return " ";
|
||||
}
|
||||
|
||||
void on_manage_events(GtkButton*, gpointer user) {
|
||||
slideover_open(*static_cast<App*>(user), "events");
|
||||
}
|
||||
|
||||
/// One click on a row replays it, as the pane's activation does: `replay_event()` and then the in-place
|
||||
/// re-mark. The record is copied first, because nothing it triggers may be allowed to free the row it
|
||||
/// was read from.
|
||||
void on_dock_event_activated(GtkListBox*, GtkListBoxRow* row, gpointer user) {
|
||||
App& app = *static_cast<App*>(user);
|
||||
if (row == nullptr) return;
|
||||
const auto* record =
|
||||
static_cast<const EventRecord*>(g_object_get_data(G_OBJECT(row), "udpscope-event"));
|
||||
if (record == nullptr) return;
|
||||
const EventRecord copy = *record;
|
||||
replay_event(app, copy);
|
||||
mark_events_on_screen(app);
|
||||
}
|
||||
|
||||
std::string event_clock(const EventRecord& r) {
|
||||
const std::time_t t = static_cast<std::time_t>(r.wall_time);
|
||||
std::tm tm{};
|
||||
localtime_r(&t, &tm);
|
||||
char clock[32];
|
||||
std::strftime(clock, sizeof(clock), "%H:%M:%S", &tm);
|
||||
return clock;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GtkWidget* build_events_dock(App& app) {
|
||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_widget_set_margin_top(box, 6);
|
||||
gtk_widget_set_margin_bottom(box, 6);
|
||||
gtk_widget_set_margin_start(box, 6);
|
||||
gtk_widget_set_margin_end(box, 6);
|
||||
|
||||
GtkWidget* head = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
GtkWidget* title = gtk_label_new("Events");
|
||||
gtk_widget_set_halign(title, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand(title, true);
|
||||
gtk_widget_add_css_class(title, "heading");
|
||||
gtk_box_append(GTK_BOX(head), title);
|
||||
GtkWidget* manage = gtk_button_new_with_label("Manage");
|
||||
gtk_widget_add_css_class(manage, "flat");
|
||||
gtk_widget_set_tooltip_text(manage, "the events' full list and actions");
|
||||
g_signal_connect(manage, "clicked", G_CALLBACK(on_manage_events), &app);
|
||||
gtk_box_append(GTK_BOX(head), manage);
|
||||
gtk_box_append(GTK_BOX(box), head);
|
||||
|
||||
GtkWidget* list = gtk_list_box_new();
|
||||
gtk_list_box_set_selection_mode(GTK_LIST_BOX(list), GTK_SELECTION_NONE);
|
||||
gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(list), true);
|
||||
gtk_widget_add_css_class(list, "boxed-list");
|
||||
g_signal_connect(list, "row-activated", G_CALLBACK(on_dock_event_activated), &app);
|
||||
|
||||
// A fixed height (about four rows), so the block is as tall with no events as with thirty and the
|
||||
// readouts above it do not move when one is latched.
|
||||
GtkWidget* scroller = gtk_scrolled_window_new();
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroller), 112);
|
||||
gtk_scrolled_window_set_max_content_height(GTK_SCROLLED_WINDOW(scroller), 112);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroller), list);
|
||||
gtk_box_append(GTK_BOX(box), scroller);
|
||||
|
||||
app.dock_events_box = box;
|
||||
app.dock_events_list = list;
|
||||
app.dock_events_manage = manage;
|
||||
// Insensitive until the first availability sync says otherwise: on screen and greyed, never hidden.
|
||||
gtk_widget_set_sensitive(box, FALSE);
|
||||
dock_rebuild_events(app);
|
||||
return box;
|
||||
}
|
||||
|
||||
void dock_rebuild_events(App& app) {
|
||||
if (app.dock_events_list == nullptr || app.event_store == nullptr) return;
|
||||
GtkListBox* list = GTK_LIST_BOX(app.dock_events_list);
|
||||
gtk_list_box_remove_all(list);
|
||||
const std::vector<EventRecord> records = app.event_store->list();
|
||||
if (records.empty()) {
|
||||
GtkWidget* none = gtk_label_new("no events yet");
|
||||
gtk_widget_set_halign(none, GTK_ALIGN_START);
|
||||
gtk_widget_add_css_class(none, "caption");
|
||||
gtk_widget_add_css_class(none, "dim-label");
|
||||
gtk_widget_set_margin_start(none, 6);
|
||||
gtk_widget_set_margin_top(none, 4);
|
||||
gtk_widget_set_margin_bottom(none, 4);
|
||||
GtkWidget* row = gtk_list_box_row_new();
|
||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), none);
|
||||
gtk_list_box_row_set_activatable(GTK_LIST_BOX_ROW(row), false);
|
||||
gtk_list_box_append(list, row);
|
||||
return;
|
||||
}
|
||||
for (const EventRecord& r : records) {
|
||||
GtkWidget* row = gtk_list_box_row_new();
|
||||
GtkWidget* line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_margin_start(line, 6);
|
||||
gtk_widget_set_margin_end(line, 6);
|
||||
gtk_widget_set_margin_top(line, 3);
|
||||
gtk_widget_set_margin_bottom(line, 3);
|
||||
|
||||
GtkWidget* marker = fixed_label(dock_event_marker(app, r), 1, GTK_ALIGN_CENTER);
|
||||
gtk_box_append(GTK_BOX(line), marker);
|
||||
const std::string text = event_row_title(app, r);
|
||||
GtkWidget* name = fixed_label(text.c_str(), kEventTitleChars, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand(name, true);
|
||||
gtk_widget_set_tooltip_text(name, text.c_str());
|
||||
gtk_box_append(GTK_BOX(line), name);
|
||||
const std::string clock = event_clock(r);
|
||||
GtkWidget* when = fixed_label(clock.c_str(), kEventTimeChars, GTK_ALIGN_END);
|
||||
gtk_widget_add_css_class(when, "dim-label");
|
||||
gtk_box_append(GTK_BOX(line), when);
|
||||
|
||||
g_object_set_data(G_OBJECT(row), "udpscope-event-marker", marker);
|
||||
g_object_set_data(G_OBJECT(row), "udpscope-event-title", name);
|
||||
g_object_set_data_full(G_OBJECT(row), "udpscope-event", new EventRecord(r),
|
||||
[](gpointer p) { delete static_cast<EventRecord*>(p); });
|
||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), line);
|
||||
gtk_list_box_append(list, row);
|
||||
}
|
||||
}
|
||||
|
||||
void dock_mark_events(App& app) {
|
||||
if (app.dock_events_list == nullptr) return;
|
||||
for (GtkWidget* row = gtk_widget_get_first_child(app.dock_events_list); row != nullptr;
|
||||
row = gtk_widget_get_next_sibling(row)) {
|
||||
const auto* record =
|
||||
static_cast<const EventRecord*>(g_object_get_data(G_OBJECT(row), "udpscope-event"));
|
||||
GtkWidget* marker = GTK_WIDGET(g_object_get_data(G_OBJECT(row), "udpscope-event-marker"));
|
||||
GtkWidget* name = GTK_WIDGET(g_object_get_data(G_OBJECT(row), "udpscope-event-title"));
|
||||
// The "no events yet" placeholder carries none of the three: skipped.
|
||||
if (record == nullptr || marker == nullptr || name == nullptr) continue;
|
||||
gtk_label_set_text(GTK_LABEL(marker), dock_event_marker(app, *record));
|
||||
set_value_text(name, event_row_title(app, *record));
|
||||
}
|
||||
}
|
||||
|
||||
void dock_events_set_sensitive(App& app, bool available) {
|
||||
if (app.dock_events_box == nullptr) return;
|
||||
gtk_widget_set_sensitive(app.dock_events_box, available);
|
||||
}
|
||||
|
||||
} // namespace ui_impl
|
||||
} // namespace udpscope
|
||||
+28
-4
@@ -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.
|
||||
|
||||
@@ -5576,6 +5576,49 @@ void report_live_restored(App& a, const char* what) {
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
/// The dock's Events block against the pane's list. `same_titles` compares each dock row's title label
|
||||
/// with the pane row's, in order (both are rebuilt from the same list by `refresh_events_pane()`, and both
|
||||
/// are marked by `mark_events_on_screen()`); `on_screen` counts the dock rows with the filled marker, and
|
||||
/// `marked` is the index of the first one (-1 for none).
|
||||
static void report_dock_events(App& a, const char* what) {
|
||||
std::vector<std::string> dock_titles;
|
||||
std::vector<std::string> pane_titles;
|
||||
int on_screen = 0;
|
||||
int marked = -1;
|
||||
int members = 0;
|
||||
const auto titles = [&members](GtkWidget* list, std::vector<std::string>* out, int* filled, int* first) {
|
||||
if (list == nullptr) return;
|
||||
int i = 0;
|
||||
for (GtkWidget* row = gtk_widget_get_first_child(list); row != nullptr;
|
||||
row = gtk_widget_get_next_sibling(row), ++i) {
|
||||
if (g_object_get_data(G_OBJECT(row), "udpscope-event") == nullptr) continue;
|
||||
GtkWidget* name = GTK_WIDGET(g_object_get_data(G_OBJECT(row), "udpscope-event-title"));
|
||||
if (name == nullptr) continue;
|
||||
out->push_back(gtk_label_get_text(GTK_LABEL(name)));
|
||||
GtkWidget* marker = GTK_WIDGET(g_object_get_data(G_OBJECT(row), "udpscope-event-marker"));
|
||||
if (filled != nullptr && marker != nullptr &&
|
||||
std::strcmp(gtk_label_get_text(GTK_LABEL(marker)), "\xE2\x97\x8B") == 0) {
|
||||
++members;
|
||||
}
|
||||
if (filled != nullptr && marker != nullptr &&
|
||||
std::strcmp(gtk_label_get_text(GTK_LABEL(marker)), "\xE2\x97\x8F") == 0) {
|
||||
++*filled;
|
||||
if (first != nullptr && *first < 0) *first = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
titles(a.dock_events_list, &dock_titles, &on_screen, &marked);
|
||||
titles(a.events_list, &pane_titles, nullptr, nullptr);
|
||||
std::fprintf(stderr,
|
||||
"[events] dock events: rows=%zu pane_rows=%zu same_titles=%d sensitive=%d dock_w=%d "
|
||||
"on_screen=%d marked=%d members=%d at=%s\n",
|
||||
dock_titles.size(), pane_titles.size(), dock_titles == pane_titles ? 1 : 0,
|
||||
a.dock_events_box != nullptr && gtk_widget_is_sensitive(a.dock_events_box) ? 1 : 0,
|
||||
a.dock_events_box != nullptr ? gtk_widget_get_width(a.dock_events_box) : -1, on_screen,
|
||||
marked, members, what);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
void events_probe_next(App& a, int step, guint delay_ms) {
|
||||
a.dbg_events_step = step;
|
||||
g_timeout_add(delay_ms, events_probe_step, &a);
|
||||
@@ -5762,6 +5805,24 @@ gboolean events_probe_step(gpointer user) {
|
||||
a->events_thumb != nullptr && gtk_widget_get_mapped(a->events_thumb) ? 1 : 0,
|
||||
rows_kept ? 1 : 0, selected_now, a->dbg_events_selected);
|
||||
std::fflush(stderr);
|
||||
report_dock_events(*a, "after pane replay");
|
||||
// One click on a dock row, the gesture: a *second* event when there is one, so the marker has to
|
||||
// move. Through `row-activated`, which is what a single click emits.
|
||||
{
|
||||
GtkListBox* dock = GTK_LIST_BOX(a->dock_events_list);
|
||||
GtkListBoxRow* row = gtk_list_box_get_row_at_index(dock, 1);
|
||||
if (row == nullptr) row = gtk_list_box_get_row_at_index(dock, 0);
|
||||
const bool real = row != nullptr && g_object_get_data(G_OBJECT(row), "udpscope-event") != nullptr;
|
||||
const std::string before = a->waveform_name;
|
||||
if (real) g_signal_emit_by_name(dock, "row-activated", row);
|
||||
// Already replaying (the pane's click above), so `replaying` alone cannot tell the click from the
|
||||
// state it found: `waveform_changed` says the click opened the row's own event (1 when it was a
|
||||
// second event, 0 when there was only the one to click again).
|
||||
std::fprintf(stderr, "[events] dock replay: clicked=%d replaying=%d waveform_changed=%d\n",
|
||||
real ? 1 : 0, a->replaying ? 1 : 0, before != a->waveform_name ? 1 : 0);
|
||||
std::fflush(stderr);
|
||||
report_dock_events(*a, "after dock click");
|
||||
}
|
||||
events_probe_next(*a, 2, 1200);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -5834,6 +5895,7 @@ gboolean events_group_probe_step(gpointer user) {
|
||||
// The live settings this leg will be read against, taken *here* rather than inherited from the
|
||||
// replay leg: a group is a second, independent way into a file view, and its way back has to put
|
||||
// back what was on screen when the button was pressed.
|
||||
report_dock_events(*a, "two events latched");
|
||||
capture_live_expectation(*a);
|
||||
if (a->events_compare != nullptr) g_signal_emit_by_name(a->events_compare, "clicked");
|
||||
// Read one step later: a rebuilt row's traces are bound by the next *frame*, so a reading taken in
|
||||
@@ -5853,6 +5915,7 @@ gboolean events_group_probe_step(gpointer user) {
|
||||
group_visible_traces(*a, 0), group_visible_traces(*a, 1), a->rows->time_window(),
|
||||
a->event_recording ? 1 : 0,
|
||||
a->events_revealer != nullptr && gtk_widget_get_visible(a->events_revealer) ? 1 : 0);
|
||||
report_dock_events(*a, "comparison up");
|
||||
events_group_probe_next(*a, 46, 400);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -5985,6 +6048,17 @@ gboolean events_group_probe_step(gpointer user) {
|
||||
|
||||
if (a->dbg_events_step == 61) {
|
||||
report_live_restored(*a, "after a group");
|
||||
// A single click on the *second* dock row now that the app is live again: a different event than the
|
||||
// last (there are several by now), so the filled marker has to move to that row.
|
||||
{
|
||||
GtkListBox* dock = GTK_LIST_BOX(a->dock_events_list);
|
||||
GtkListBoxRow* row = gtk_list_box_get_row_at_index(dock, 1);
|
||||
const bool real = row != nullptr && g_object_get_data(G_OBJECT(row), "udpscope-event") != nullptr;
|
||||
if (real) g_signal_emit_by_name(dock, "row-activated", row);
|
||||
std::fprintf(stderr, "[events] dock replay: clicked=%d replaying=%d (second click, live before)\n",
|
||||
real ? 1 : 0, a->replaying ? 1 : 0);
|
||||
report_dock_events(*a, "second dock click");
|
||||
}
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user