diff --git a/src/ui_signals.cpp b/src/ui_signals.cpp index 32f9e05..86e10d5 100644 --- a/src/ui_signals.cpp +++ b/src/ui_signals.cpp @@ -189,10 +189,22 @@ GtkWidget* make_math_divider(App& app) { return row; } +/// A handler that rebuilds the signal list destroys the widget it was emitted from -- the row it +/// lives in, and that row's menu popover with it. GLib holds a reference for the duration of *one* +/// emission, and a button's own code keeps running after the handler returns, so the last reference +/// has to outlive the emission: hold one here and drop it from an idle. This is the same repair +/// `on_signal_switch()` makes, for the same reason. +static void outlive_the_rebuild(GtkWidget* widget) { + g_object_ref(widget); + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, + +[](gpointer) -> gboolean { return G_SOURCE_REMOVE; }, widget, g_object_unref); +} + /// Create a channel, then open the editor on it: a new channel is a single `Source` node with a /// default name, which is of no use until it is given a graph. -void on_math_add_clicked(GtkButton*, gpointer user) { +void on_math_add_clicked(GtkButton* button, gpointer user) { App* a = static_cast(user); + outlive_the_rebuild(GTK_WIDGET(button)); add_math_channel(*a); if (a->math.empty()) return; // `add_math_channel()` appended, so the new channel is the last one; `apply_math_channels()` @@ -233,6 +245,7 @@ void on_math_delete_clicked(GtkButton* button, gpointer user) { const int k = math_index_for_signal(*a, signal_name_at(*a, index)); if (k < 0) return; dismiss_row_menu(button); + outlive_the_rebuild(GTK_WIDGET(button)); a->math.erase(a->math.begin() + k); // Rebuilds the model's signal list, the plots and this list, and schedules the save. apply_math_channels(*a);