ui: cap and cheapen the dock's events rebuild
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
baf1c73b21
commit
cb098eff75
4 files changed
+133
-34
No files matched your search
@@ -1797,7 +1797,12 @@ void dock_measure_value(App& app, std::size_t i, const std::string& text);
|
|||||||
/// of what is *on screen* (in place, no rebuild), `dock_events_set_sensitive()` is the one writer of the
|
/// 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.
|
/// block's sensitivity. All three are no-ops before the block is built.
|
||||||
GtkWidget* build_events_dock(App& app);
|
GtkWidget* build_events_dock(App& app);
|
||||||
|
/// The dock shows at most this many events (the newest), then one dim "... N more, see Manage" row.
|
||||||
|
inline constexpr std::size_t kDockEventsMax = 24;
|
||||||
|
/// The first lists the store itself; the second takes a list the caller already holds (so one refresh
|
||||||
|
/// reads the cache directory once, not twice). Both keep the list's vertical scroll across the rebuild.
|
||||||
void dock_rebuild_events(App& app);
|
void dock_rebuild_events(App& app);
|
||||||
|
void dock_rebuild_events(App& app, const std::vector<EventRecord>& records);
|
||||||
void dock_mark_events(App& app);
|
void dock_mark_events(App& app);
|
||||||
void dock_events_set_sensitive(App& app, bool available);
|
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
|
/// A row's first line, and the two questions its mark is made of (`ui_events.cpp`): the one function
|
||||||
|
|||||||
+74
-16
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -238,7 +239,7 @@ namespace {
|
|||||||
/// The widths of an event row's three cells, in characters. Fixed, so the Events block is as wide with
|
/// 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).
|
/// one event as with thirty (the marker is one cell wide even when it is empty).
|
||||||
constexpr int kEventTitleChars = 18;
|
constexpr int kEventTitleChars = 18;
|
||||||
constexpr int kEventTimeChars = 8;
|
constexpr int kEventSeqChars = 8;
|
||||||
|
|
||||||
/// The one-character mark of a row: filled for the event on screen, hollow for a member of the group on
|
/// 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`).
|
/// screen, blank for the rest. Both questions are the ones `event_row_title()` asks (`ui_events.cpp`).
|
||||||
@@ -255,6 +256,11 @@ void on_manage_events(GtkButton*, gpointer user) {
|
|||||||
/// One click on a row replays it, as the pane's activation does: `replay_event()` and then the in-place
|
/// 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
|
/// re-mark. The record is copied first, because nothing it triggers may be allowed to free the row it
|
||||||
/// was read from.
|
/// was read from.
|
||||||
|
///
|
||||||
|
/// Safety depends on `replay_event()` never rebuilding the dock's list (it must go through
|
||||||
|
/// `dock_mark_events()`, which rewrites labels in place): a rebuild here would free this very row, and
|
||||||
|
/// `row`, `record` and the list box's own emission of `row-activated` would all be dangling. The copy
|
||||||
|
/// guards the record only, not the row. If a replay ever has to refresh the cache's view, defer it.
|
||||||
void on_dock_event_activated(GtkListBox*, GtkListBoxRow* row, gpointer user) {
|
void on_dock_event_activated(GtkListBox*, GtkListBoxRow* row, gpointer user) {
|
||||||
App& app = *static_cast<App*>(user);
|
App& app = *static_cast<App*>(user);
|
||||||
if (row == nullptr) return;
|
if (row == nullptr) return;
|
||||||
@@ -266,15 +272,6 @@ void on_dock_event_activated(GtkListBox*, GtkListBoxRow* row, gpointer user) {
|
|||||||
mark_events_on_screen(app);
|
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
|
} // namespace
|
||||||
|
|
||||||
GtkWidget* build_events_dock(App& app) {
|
GtkWidget* build_events_dock(App& app) {
|
||||||
@@ -323,9 +320,47 @@ GtkWidget* build_events_dock(App& app) {
|
|||||||
|
|
||||||
void dock_rebuild_events(App& app) {
|
void dock_rebuild_events(App& app) {
|
||||||
if (app.dock_events_list == nullptr || app.event_store == nullptr) return;
|
if (app.dock_events_list == nullptr || app.event_store == nullptr) return;
|
||||||
|
dock_rebuild_events(app, app.event_store->list());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// Puts the list's vertical scroll back after a refill. The adjustment's bounds are only updated at the
|
||||||
|
/// next allocation, so the value set at once is clamped against the old ones; an idle (which runs after the
|
||||||
|
/// frame that laid the new rows out) sets it again against the new bounds, where the clamp is exact.
|
||||||
|
void restore_dock_scroll(GtkWidget* list, double value) {
|
||||||
|
GtkWidget* scroller = gtk_widget_get_ancestor(list, GTK_TYPE_SCROLLED_WINDOW);
|
||||||
|
if (scroller == nullptr) return;
|
||||||
|
GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroller));
|
||||||
|
gtk_adjustment_set_value(adj, value);
|
||||||
|
struct Pending {
|
||||||
|
GtkAdjustment* adj;
|
||||||
|
double value;
|
||||||
|
};
|
||||||
|
auto* p = new Pending{GTK_ADJUSTMENT(g_object_ref(adj)), value};
|
||||||
|
g_idle_add_full(
|
||||||
|
G_PRIORITY_DEFAULT_IDLE,
|
||||||
|
[](gpointer d) -> gboolean {
|
||||||
|
auto* q = static_cast<Pending*>(d);
|
||||||
|
gtk_adjustment_set_value(q->adj, q->value);
|
||||||
|
g_object_unref(q->adj);
|
||||||
|
delete q;
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
},
|
||||||
|
p, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void dock_rebuild_events(App& app, const std::vector<EventRecord>& records) {
|
||||||
|
if (app.dock_events_list == nullptr) return;
|
||||||
GtkListBox* list = GTK_LIST_BOX(app.dock_events_list);
|
GtkListBox* list = GTK_LIST_BOX(app.dock_events_list);
|
||||||
|
GtkWidget* scroller = gtk_widget_get_ancestor(app.dock_events_list, GTK_TYPE_SCROLLED_WINDOW);
|
||||||
|
const double scroll =
|
||||||
|
scroller != nullptr
|
||||||
|
? gtk_adjustment_get_value(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroller)))
|
||||||
|
: 0.0;
|
||||||
gtk_list_box_remove_all(list);
|
gtk_list_box_remove_all(list);
|
||||||
const std::vector<EventRecord> records = app.event_store->list();
|
|
||||||
if (records.empty()) {
|
if (records.empty()) {
|
||||||
GtkWidget* none = gtk_label_new("no events yet");
|
GtkWidget* none = gtk_label_new("no events yet");
|
||||||
gtk_widget_set_halign(none, GTK_ALIGN_START);
|
gtk_widget_set_halign(none, GTK_ALIGN_START);
|
||||||
@@ -340,7 +375,11 @@ void dock_rebuild_events(App& app) {
|
|||||||
gtk_list_box_append(list, row);
|
gtk_list_box_append(list, row);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const EventRecord& r : records) {
|
// The newest `kDockEventsMax` only: the list is newest first, and a block that builds four widgets per
|
||||||
|
// cached event is a cost that grows with a cache the user may have set to unbounded.
|
||||||
|
const std::size_t shown = std::min(records.size(), kDockEventsMax);
|
||||||
|
for (std::size_t i = 0; i < shown; ++i) {
|
||||||
|
const EventRecord& r = records[i];
|
||||||
GtkWidget* row = gtk_list_box_row_new();
|
GtkWidget* row = gtk_list_box_row_new();
|
||||||
GtkWidget* line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
GtkWidget* line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||||
gtk_widget_set_margin_start(line, 6);
|
gtk_widget_set_margin_start(line, 6);
|
||||||
@@ -355,10 +394,13 @@ void dock_rebuild_events(App& app) {
|
|||||||
gtk_widget_set_hexpand(name, true);
|
gtk_widget_set_hexpand(name, true);
|
||||||
gtk_widget_set_tooltip_text(name, text.c_str());
|
gtk_widget_set_tooltip_text(name, text.c_str());
|
||||||
gtk_box_append(GTK_BOX(line), name);
|
gtk_box_append(GTK_BOX(line), name);
|
||||||
const std::string clock = event_clock(r);
|
// The latch's sequence number, as the pane's third line ends with it: the title already carries the
|
||||||
GtkWidget* when = fixed_label(clock.c_str(), kEventTimeChars, GTK_ALIGN_END);
|
// clock, so this is the part of the row that is not in it.
|
||||||
gtk_widget_add_css_class(when, "dim-label");
|
const std::string seq = "#" + std::to_string(r.seq);
|
||||||
gtk_box_append(GTK_BOX(line), when);
|
GtkWidget* num = fixed_label(seq.c_str(), kEventSeqChars, GTK_ALIGN_END);
|
||||||
|
gtk_widget_set_tooltip_text(num, ("latch " + seq).c_str());
|
||||||
|
gtk_widget_add_css_class(num, "dim-label");
|
||||||
|
gtk_box_append(GTK_BOX(line), num);
|
||||||
|
|
||||||
g_object_set_data(G_OBJECT(row), "udpscope-event-marker", marker);
|
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(G_OBJECT(row), "udpscope-event-title", name);
|
||||||
@@ -367,6 +409,22 @@ void dock_rebuild_events(App& app) {
|
|||||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), line);
|
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), line);
|
||||||
gtk_list_box_append(list, row);
|
gtk_list_box_append(list, row);
|
||||||
}
|
}
|
||||||
|
if (records.size() > shown) {
|
||||||
|
// A last row with no record (like the placeholder: `dock_mark_events()` skips it, and it does nothing
|
||||||
|
// when activated). Fixed text and the same ellipsis as the rest, so it cannot widen the dock.
|
||||||
|
const std::string more = "\xE2\x80\xA6 " + std::to_string(records.size() - shown) + " more, see Manage";
|
||||||
|
GtkWidget* label = fixed_label(more.c_str(), kEventTitleChars + kEventSeqChars, GTK_ALIGN_START);
|
||||||
|
gtk_widget_add_css_class(label, "dim-label");
|
||||||
|
gtk_widget_set_tooltip_text(label, more.c_str());
|
||||||
|
gtk_widget_set_margin_start(label, 6);
|
||||||
|
gtk_widget_set_margin_top(label, 4);
|
||||||
|
gtk_widget_set_margin_bottom(label, 4);
|
||||||
|
GtkWidget* row = gtk_list_box_row_new();
|
||||||
|
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), label);
|
||||||
|
gtk_list_box_row_set_activatable(GTK_LIST_BOX_ROW(row), false);
|
||||||
|
gtk_list_box_append(list, row);
|
||||||
|
}
|
||||||
|
restore_dock_scroll(app.dock_events_list, scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dock_mark_events(App& app) {
|
void dock_mark_events(App& app) {
|
||||||
|
|||||||
+15
-9
@@ -782,10 +782,8 @@ void attach_preview_zoom(GtkWidget* frame, const std::string& path) {
|
|||||||
GConnectFlags(0));
|
GConnectFlags(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A row's first line: when the event was latched, how long it is, and whether it is the one on
|
/// Whether the event is the one on screen: the one question the title's "(on screen)" and the dock's
|
||||||
/// screen. One function because two callers write it -- the row builder and the in-place
|
/// filled mark both ask.
|
||||||
/// 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) {
|
bool event_on_screen(const App& app, const EventRecord& r) {
|
||||||
return std::string(app.waveform_name) == r.file;
|
return std::string(app.waveform_name) == r.file;
|
||||||
}
|
}
|
||||||
@@ -797,6 +795,10 @@ bool event_in_group(const App& app, const EventRecord& r) {
|
|||||||
[&](const EventRecord& g) { return g.file == r.file; });
|
[&](const EventRecord& g) { return g.file == r.file; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A row's first line: when the event was latched, how long it is, and whether it is the one on
|
||||||
|
/// 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.
|
||||||
std::string event_row_title(App& app, const EventRecord& r) {
|
std::string event_row_title(App& app, const EventRecord& r) {
|
||||||
const std::time_t t = static_cast<std::time_t>(r.wall_time);
|
const std::time_t t = static_cast<std::time_t>(r.wall_time);
|
||||||
std::tm tm{};
|
std::tm tm{};
|
||||||
@@ -828,7 +830,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
|
/// 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
|
/// 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.
|
/// cannot show thirty of them.
|
||||||
static void rebuild_events_pane_list(App& app) {
|
static void rebuild_events_pane_list(App& app, std::vector<EventRecord>& records) {
|
||||||
if (app.events_list == nullptr) return;
|
if (app.events_list == nullptr) return;
|
||||||
while (GtkWidget* child = gtk_widget_get_first_child(app.events_list)) {
|
while (GtkWidget* child = gtk_widget_get_first_child(app.events_list)) {
|
||||||
gtk_list_box_remove(GTK_LIST_BOX(app.events_list), child);
|
gtk_list_box_remove(GTK_LIST_BOX(app.events_list), child);
|
||||||
@@ -838,8 +840,8 @@ static void rebuild_events_pane_list(App& app) {
|
|||||||
// the one that finds out.
|
// the one that finds out.
|
||||||
app.events_thumb = nullptr;
|
app.events_thumb = nullptr;
|
||||||
if (app.event_store == nullptr) return;
|
if (app.event_store == nullptr) return;
|
||||||
// Not const: a record whose preview had to be redrawn learns its thumbnail's path here.
|
// `records` is the caller's list, not const: a record whose preview had to be redrawn learns its
|
||||||
std::vector<EventRecord> records = app.event_store->list();
|
// thumbnail's path here.
|
||||||
if (records.empty()) {
|
if (records.empty()) {
|
||||||
GtkWidget* empty = gtk_label_new("no events yet -- arm the trigger");
|
GtkWidget* empty = gtk_label_new("no events yet -- arm the trigger");
|
||||||
gtk_widget_add_css_class(empty, "dim-label");
|
gtk_widget_add_css_class(empty, "dim-label");
|
||||||
@@ -975,8 +977,12 @@ static void rebuild_events_pane_list(App& app) {
|
|||||||
/// Both views of the cache, rebuilt from the same list: the pane's rows (with their pictures) and the
|
/// 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.
|
/// dock's compact ones. The dock follows even when the pane's list does not exist.
|
||||||
void refresh_events_pane(App& app) {
|
void refresh_events_pane(App& app) {
|
||||||
rebuild_events_pane_list(app);
|
// One read of the cache directory for both views (none without a store, which the pane's rebuild
|
||||||
dock_rebuild_events(app);
|
// and the dock's both treat as nothing to list).
|
||||||
|
std::vector<EventRecord> records;
|
||||||
|
if (app.event_store != nullptr) records = app.event_store->list();
|
||||||
|
rebuild_events_pane_list(app, records);
|
||||||
|
if (app.event_store != nullptr) dock_rebuild_events(app, records);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clicking a row replays it: the same loader `--open` uses, on the event's file. The acquisition is
|
/// Clicking a row replays it: the same loader `--open` uses, on the event's file. The acquisition is
|
||||||
|
|||||||
+39
-9
@@ -5579,14 +5579,19 @@ void report_live_restored(App& a, const char* what) {
|
|||||||
/// The dock's Events block against the pane's list. `same_titles` compares each dock row's title label
|
/// 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
|
/// 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
|
/// 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).
|
/// `marked` is the index of the first one (-1 for none). `max_ok` is the cap: no more than
|
||||||
static void report_dock_events(App& a, const char* what) {
|
/// `kDockEventsMax` event rows and one "more" row. `expected_marked` is read after a replay of a record
|
||||||
|
/// the caller names by file (`expected_file`): 1 when exactly one dock row is filled and its record is that
|
||||||
|
/// one (record identity, not a count), 0 otherwise, -1 when the caller names none.
|
||||||
|
static void report_dock_events(App& a, const char* what, const std::string& expected_file = std::string()) {
|
||||||
std::vector<std::string> dock_titles;
|
std::vector<std::string> dock_titles;
|
||||||
|
std::string filled_file;
|
||||||
|
int dock_children = 0;
|
||||||
std::vector<std::string> pane_titles;
|
std::vector<std::string> pane_titles;
|
||||||
int on_screen = 0;
|
int on_screen = 0;
|
||||||
int marked = -1;
|
int marked = -1;
|
||||||
int members = 0;
|
int members = 0;
|
||||||
const auto titles = [&members](GtkWidget* list, std::vector<std::string>* out, int* filled, int* first) {
|
const auto titles = [&members, &filled_file](GtkWidget* list, std::vector<std::string>* out, int* filled, int* first) {
|
||||||
if (list == nullptr) return;
|
if (list == nullptr) return;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (GtkWidget* row = gtk_widget_get_first_child(list); row != nullptr;
|
for (GtkWidget* row = gtk_widget_get_first_child(list); row != nullptr;
|
||||||
@@ -5603,22 +5608,41 @@ static void report_dock_events(App& a, const char* what) {
|
|||||||
if (filled != nullptr && marker != nullptr &&
|
if (filled != nullptr && marker != nullptr &&
|
||||||
std::strcmp(gtk_label_get_text(GTK_LABEL(marker)), "\xE2\x97\x8F") == 0) {
|
std::strcmp(gtk_label_get_text(GTK_LABEL(marker)), "\xE2\x97\x8F") == 0) {
|
||||||
++*filled;
|
++*filled;
|
||||||
if (first != nullptr && *first < 0) *first = i;
|
if (first != nullptr && *first < 0) {
|
||||||
|
*first = i;
|
||||||
|
filled_file = static_cast<const EventRecord*>(g_object_get_data(G_OBJECT(row), "udpscope-event"))->file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
for (GtkWidget* row = a.dock_events_list != nullptr ? gtk_widget_get_first_child(a.dock_events_list) : nullptr;
|
||||||
|
row != nullptr; row = gtk_widget_get_next_sibling(row)) {
|
||||||
|
++dock_children;
|
||||||
|
}
|
||||||
titles(a.dock_events_list, &dock_titles, &on_screen, &marked);
|
titles(a.dock_events_list, &dock_titles, &on_screen, &marked);
|
||||||
titles(a.events_list, &pane_titles, nullptr, nullptr);
|
titles(a.events_list, &pane_titles, nullptr, nullptr);
|
||||||
|
// The dock shows the newest `kDockEventsMax` only: its titles are the pane's first ones, in order.
|
||||||
|
if (pane_titles.size() > kDockEventsMax) pane_titles.resize(kDockEventsMax);
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"[events] dock events: rows=%zu pane_rows=%zu same_titles=%d sensitive=%d dock_w=%d "
|
"[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",
|
"on_screen=%d marked=%d members=%d max_ok=%d expected_marked=%d at=%s\n",
|
||||||
dock_titles.size(), pane_titles.size(), dock_titles == pane_titles ? 1 : 0,
|
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_is_sensitive(a.dock_events_box) ? 1 : 0,
|
||||||
a.dock_events_box != nullptr ? gtk_widget_get_width(a.dock_events_box) : -1, on_screen,
|
a.dock_events_box != nullptr ? gtk_widget_get_width(a.dock_events_box) : -1, on_screen,
|
||||||
marked, members, what);
|
marked, members, dock_children <= static_cast<int>(kDockEventsMax) + 1 ? 1 : 0,
|
||||||
|
expected_file.empty() ? -1 : (on_screen == 1 && filled_file == expected_file ? 1 : 0), what);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The file of the record the Nth row of a list carries ("" for a row without one).
|
||||||
|
static std::string listed_file(GtkWidget* list, int index) {
|
||||||
|
if (list == nullptr) return std::string();
|
||||||
|
GtkListBoxRow* row = gtk_list_box_get_row_at_index(GTK_LIST_BOX(list), index);
|
||||||
|
if (row == nullptr) return std::string();
|
||||||
|
const auto* r = static_cast<const EventRecord*>(g_object_get_data(G_OBJECT(row), "udpscope-event"));
|
||||||
|
return r != nullptr ? r->file : std::string();
|
||||||
|
}
|
||||||
|
|
||||||
void events_probe_next(App& a, int step, guint delay_ms) {
|
void events_probe_next(App& a, int step, guint delay_ms) {
|
||||||
a.dbg_events_step = step;
|
a.dbg_events_step = step;
|
||||||
g_timeout_add(delay_ms, events_probe_step, &a);
|
g_timeout_add(delay_ms, events_probe_step, &a);
|
||||||
@@ -5805,7 +5829,9 @@ gboolean events_probe_step(gpointer user) {
|
|||||||
a->events_thumb != nullptr && gtk_widget_get_mapped(a->events_thumb) ? 1 : 0,
|
a->events_thumb != nullptr && gtk_widget_get_mapped(a->events_thumb) ? 1 : 0,
|
||||||
rows_kept ? 1 : 0, selected_now, a->dbg_events_selected);
|
rows_kept ? 1 : 0, selected_now, a->dbg_events_selected);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
report_dock_events(*a, "after pane replay");
|
// The pane replayed its own row 0; the dock's list is the same list, so the dock's filled marker has to
|
||||||
|
// be on the row whose record is that one.
|
||||||
|
report_dock_events(*a, "after pane replay", listed_file(a->events_list, 0));
|
||||||
// One click on a dock row, the gesture: a *second* event when there is one, so the marker has to
|
// 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.
|
// move. Through `row-activated`, which is what a single click emits.
|
||||||
{
|
{
|
||||||
@@ -5813,6 +5839,9 @@ gboolean events_probe_step(gpointer user) {
|
|||||||
GtkListBoxRow* row = gtk_list_box_get_row_at_index(dock, 1);
|
GtkListBoxRow* row = gtk_list_box_get_row_at_index(dock, 1);
|
||||||
if (row == nullptr) row = gtk_list_box_get_row_at_index(dock, 0);
|
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 bool real = row != nullptr && g_object_get_data(G_OBJECT(row), "udpscope-event") != nullptr;
|
||||||
|
const std::string clicked_file =
|
||||||
|
real ? static_cast<const EventRecord*>(g_object_get_data(G_OBJECT(row), "udpscope-event"))->file
|
||||||
|
: std::string();
|
||||||
const std::string before = a->waveform_name;
|
const std::string before = a->waveform_name;
|
||||||
if (real) g_signal_emit_by_name(dock, "row-activated", row);
|
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
|
// Already replaying (the pane's click above), so `replaying` alone cannot tell the click from the
|
||||||
@@ -5821,7 +5850,7 @@ gboolean events_probe_step(gpointer user) {
|
|||||||
std::fprintf(stderr, "[events] dock replay: clicked=%d replaying=%d waveform_changed=%d\n",
|
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);
|
real ? 1 : 0, a->replaying ? 1 : 0, before != a->waveform_name ? 1 : 0);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
report_dock_events(*a, "after dock click");
|
report_dock_events(*a, "after dock click", clicked_file);
|
||||||
}
|
}
|
||||||
events_probe_next(*a, 2, 1200);
|
events_probe_next(*a, 2, 1200);
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
@@ -6054,10 +6083,11 @@ gboolean events_group_probe_step(gpointer user) {
|
|||||||
GtkListBox* dock = GTK_LIST_BOX(a->dock_events_list);
|
GtkListBox* dock = GTK_LIST_BOX(a->dock_events_list);
|
||||||
GtkListBoxRow* row = gtk_list_box_get_row_at_index(dock, 1);
|
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;
|
const bool real = row != nullptr && g_object_get_data(G_OBJECT(row), "udpscope-event") != nullptr;
|
||||||
|
const std::string clicked_file = real ? listed_file(a->dock_events_list, 1) : std::string();
|
||||||
if (real) g_signal_emit_by_name(dock, "row-activated", row);
|
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",
|
std::fprintf(stderr, "[events] dock replay: clicked=%d replaying=%d (second click, live before)\n",
|
||||||
real ? 1 : 0, a->replaying ? 1 : 0);
|
real ? 1 : 0, a->replaying ? 1 : 0);
|
||||||
report_dock_events(*a, "second dock click");
|
report_dock_events(*a, "second dock click", clicked_file);
|
||||||
}
|
}
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user