scope windows take drops, and the signal page lists them
The second windows were read-only by accident: nothing accepted a drop on them, and the
signal page's *Plot on* buttons knew only the main window's rows.
- **Drop target per scope window**: the same string payload a badge drags (the signal index),
handled by `on_scope_drop_channel()` -- the target carries its own window, the row is that
window's cell under the pointer, and `assign_signal_to_scope()` lands the signal in the
window's own row set (enabling an unacquired signal first, exactly as the main drop does).
The rebuild and the area's re-point are the window's next frame in the tick: a drop arrives
from inside the very widget it rearranges, and a synchronous rebuild would reset the drag
controller under it.
- **Plot on, per window**: the signal page carries one named group per scope window ("In
\"plot 2\""), one button per plot of that window, sensitive on that window's own rows. A
window closing refreshes the pages in its destroy handler, before the deferred delete could
dangle a button's scope pointer.
- Probe: `[scope-probe]` drops through the wired handler (`drop=1 slot1='square'`) and counts
the page's groups (`page_groups=2`); the section documents that it must not run with
`UDPSCOPE_ENABLE_ALL_MS` -- the enable-all probe's second leg clears the front plot, which
is the slot this probe copies. README, help and the layout invariant follow.
This commit is contained in:
1 parent
706d247b58
commit
51b4fad386
9 files changed
+231
-21
No files matched your search
+20
@@ -611,6 +611,16 @@ void open_scope_window(App& app, int figure_row) {
|
||||
gtk_widget_set_hexpand(scope->widget, true);
|
||||
gtk_widget_set_vexpand(scope->widget, true);
|
||||
gtk_widget_set_size_request(scope->widget, 320, 160);
|
||||
// The window's short name, what the signal page's *Plot on* groups carry: "plot N" for a copy
|
||||
// (of the main window's figure N), "scope N" -- numbered app-wide -- for an empty one.
|
||||
scope->name = figure_row >= 0 ? "plot " + std::to_string(figure_row + 1)
|
||||
: "scope " + std::to_string(++app.scope_seq);
|
||||
// A drop target like the main area's: a signal badge dragged onto this window's plot lands in
|
||||
// that window's own row set, on the cell the pointer is over.
|
||||
scope->drop = gtk_drop_target_new(G_TYPE_STRING, GDK_ACTION_COPY);
|
||||
g_object_set_data(G_OBJECT(scope->drop), "udpscope-scope", scope.get());
|
||||
g_signal_connect(scope->drop, "drop", G_CALLBACK(on_scope_drop_channel), &app);
|
||||
gtk_widget_add_controller(scope->widget, GTK_EVENT_CONTROLLER(scope->drop));
|
||||
scope->window = GTK_WINDOW(gtk_window_new());
|
||||
const std::string title = figure_row >= 0
|
||||
? "udpscope \u2014 plot " + std::to_string(figure_row + 1)
|
||||
@@ -640,6 +650,10 @@ void open_scope_window(App& app, int figure_row) {
|
||||
victim, nullptr);
|
||||
break;
|
||||
}
|
||||
// The signal page's *Plot on* groups carry a button per window: with one gone,
|
||||
// the pages are rebuilt before anything can read a dangling scope pointer
|
||||
// (the struct itself is deleted on the idle above).
|
||||
signal_pages_sync(*a);
|
||||
}),
|
||||
&app);
|
||||
gtk_window_present(scope->window);
|
||||
@@ -4235,6 +4249,12 @@ gboolean tick(gpointer user) {
|
||||
} else if (std::fabs(scope->rows->time_window() - window_s) > 1.0e-12) {
|
||||
scope->rows->set_time_window(window_s);
|
||||
}
|
||||
// An arrangement change (a drop, a *Plot on* button) rebuilt the row set: point the area at
|
||||
// the new figure here, in the tick -- the change arrives from inside this very widget (a drop
|
||||
// signal), and a synchronous rebuild would reset the drag controller under it.
|
||||
if (scope->rows->rebuild_if_dirty() && scope->area != nullptr) {
|
||||
scope->area->set_figure(&scope->rows->figure());
|
||||
}
|
||||
if (scope->widget != nullptr) gtk_gl_area_queue_render(GTK_GL_AREA(scope->widget));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user