badges: source badge shows the name only; drop the signals lock; the signals page proposes what can be added
The link state is the status bar's, so the Source badge loses its state text. The signals have no lock (the badges and signal pages are direct manipulation), and the signals page lists only unacquired signals and uncomputed math channels (plus the acquired ones while the badge bar has overflowed). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
dad424f1de
commit
b98a3478ff
10 files changed
+164
-148
No files matched your search
+46
-28
@@ -167,7 +167,7 @@ int math_index_for_signal(App& app, const std::string& name) {
|
||||
/// The `MATH` boundary in the one channel list, carrying the `+` that creates a channel. Not
|
||||
/// selectable and not activatable: it is a heading, and a heading that highlights under the
|
||||
/// pointer reads as a row you can plot.
|
||||
GtkWidget* make_math_divider(App& app, bool with_add) {
|
||||
GtkWidget* make_math_divider(App& app) {
|
||||
GtkWidget* row = gtk_list_box_row_new();
|
||||
gtk_list_box_row_set_selectable(GTK_LIST_BOX_ROW(row), FALSE);
|
||||
gtk_list_box_row_set_activatable(GTK_LIST_BOX_ROW(row), FALSE);
|
||||
@@ -181,16 +181,12 @@ GtkWidget* make_math_divider(App& app, bool with_add) {
|
||||
gtk_widget_set_halign(label, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand(label, TRUE);
|
||||
gtk_box_append(GTK_BOX(box), label);
|
||||
// Locked: the heading stays, because the list it divides is the one being read, but the `+` that
|
||||
// creates a channel does not. A `+` on a locked list is a button that opens the editor the lock
|
||||
// exists to keep shut.
|
||||
if (with_add) {
|
||||
GtkWidget* add = gtk_button_new_from_icon_name("list-add-symbolic");
|
||||
gtk_widget_add_css_class(add, "flat");
|
||||
gtk_widget_set_tooltip_text(add, "add a math channel");
|
||||
g_signal_connect(add, "clicked", G_CALLBACK(on_math_add_clicked), &app);
|
||||
gtk_box_append(GTK_BOX(box), add);
|
||||
}
|
||||
// The `+` that creates a channel: the "new math channel" entry of a page that proposes what can be added.
|
||||
GtkWidget* add = gtk_button_new_from_icon_name("list-add-symbolic");
|
||||
gtk_widget_add_css_class(add, "flat");
|
||||
gtk_widget_set_tooltip_text(add, "add a math channel");
|
||||
g_signal_connect(add, "clicked", G_CALLBACK(on_math_add_clicked), &app);
|
||||
gtk_box_append(GTK_BOX(box), add);
|
||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), box);
|
||||
return row;
|
||||
}
|
||||
@@ -257,6 +253,14 @@ void on_math_delete_clicked(GtkButton* button, gpointer user) {
|
||||
apply_math_channels(*a);
|
||||
}
|
||||
|
||||
bool signal_badges_overflowed(App& app, const std::vector<SignalInfo>& signals) {
|
||||
std::size_t enabled = 0;
|
||||
for (std::size_t i : listed_signals(signals)) {
|
||||
if (app.model->signal_enabled(i)) ++enabled;
|
||||
}
|
||||
return enabled > kMaxSignalBadges;
|
||||
}
|
||||
|
||||
std::vector<std::size_t> listed_signals(const std::vector<SignalInfo>& signals) {
|
||||
std::vector<std::size_t> out;
|
||||
for (std::size_t i = 0; i < signals.size(); ++i) {
|
||||
@@ -310,12 +314,20 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
bool math_divider_done = false;
|
||||
// Streamed signals, counted as they are drawn: the dump reads `rows == signals + 1 + math`.
|
||||
std::size_t plain = 0;
|
||||
std::size_t math_rows = 0;
|
||||
// The page proposes what can be *added*: an acquired signal already has a badge, so it is not listed --
|
||||
// unless the badges have overflowed into one "N signals" badge, when the rows are the only way to reach
|
||||
// each signal's page.
|
||||
const bool overflowed = signal_badges_overflowed(app, signals);
|
||||
for (std::size_t i : listed_signals(signals)) {
|
||||
// Locked: a row per signal that is *on*, which is the list a user reads rather than the list they
|
||||
// configure. Turning one on is what the pane's [edit] button is for.
|
||||
if (app.signals_locked && !app.model->signal_enabled(i)) continue;
|
||||
if (!overflowed && app.model->signal_enabled(i)) continue;
|
||||
rows.push_back(i);
|
||||
}
|
||||
// Nothing addable: say so rather than leave the math entry alone under the hint. Not before the stream
|
||||
// has declared any signal -- "every signal is acquired" would be untrue of no signals at all.
|
||||
if (app.signals_empty != nullptr) {
|
||||
gtk_widget_set_visible(app.signals_empty, rows.empty() && !listed_signals(signals).empty());
|
||||
}
|
||||
|
||||
for (std::size_t i : rows) {
|
||||
const SignalInfo& sig = signals[i];
|
||||
@@ -323,9 +335,13 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
// `+` that creates the first one.
|
||||
if (sig.math && !math_divider_done) {
|
||||
math_divider_done = true;
|
||||
gtk_list_box_append(GTK_LIST_BOX(list), make_math_divider(app, !app.signals_locked));
|
||||
gtk_list_box_append(GTK_LIST_BOX(list), make_math_divider(app));
|
||||
}
|
||||
if (sig.math) {
|
||||
++math_rows;
|
||||
} else {
|
||||
++plain;
|
||||
}
|
||||
if (!sig.math) ++plain;
|
||||
GtkWidget* row = gtk_list_box_row_new();
|
||||
gtk_widget_add_css_class(row, "udpscope-compact");
|
||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
@@ -339,7 +355,7 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
chip, !app.model->signal_enabled(i) ? "not acquired"
|
||||
: plotted ? "the colour this signal is plotted in -- click to change it"
|
||||
: "on no plot, so there is no trace to colour");
|
||||
if (plotted && !app.signals_locked) {
|
||||
if (plotted) {
|
||||
// A gesture on the label rather than a `GtkButton` around it: a button brings its own
|
||||
// padding, and padding on this row is what made the list twice as tall once already (see
|
||||
// this function's note). The chip also has to stay exactly one disc wide, or the names
|
||||
@@ -373,9 +389,7 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
gtk_widget_set_visible(enable, sig.math || app.waveform_name.empty());
|
||||
g_object_set_data(G_OBJECT(enable), "udpscope-signal", GSIZE_TO_POINTER(i));
|
||||
g_signal_connect(enable, "notify::active", G_CALLBACK(on_signal_switch), &app);
|
||||
// Locked: the switch decides what is acquired, so it is a configuration control and it goes with
|
||||
// the rest of them.
|
||||
if (!app.signals_locked) gtk_box_append(GTK_BOX(box), enable);
|
||||
gtk_box_append(GTK_BOX(box), enable);
|
||||
|
||||
GtkWidget* name = gtk_label_new(sig.name.c_str());
|
||||
gtk_widget_set_hexpand(name, true);
|
||||
@@ -459,9 +473,7 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
gtk_menu_button_set_popover(GTK_MENU_BUTTON(more), popover);
|
||||
gtk_widget_add_css_class(more, "flat");
|
||||
gtk_widget_set_tooltip_text(more, "information and calibration");
|
||||
// Locked: the menu holds calibration, deletion and a rename, which are exactly what this mode is
|
||||
// not for. The popover is left unattached, so nothing can reach it.
|
||||
if (!app.signals_locked) gtk_box_append(GTK_BOX(box), more);
|
||||
gtk_box_append(GTK_BOX(box), more);
|
||||
|
||||
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), box);
|
||||
// Activating the row opens the signal's page; the row resolves its identity the way its widgets do.
|
||||
@@ -473,9 +485,18 @@ void rebuild_signal_list(App& app, const std::vector<SignalInfo>& signals) {
|
||||
// Appended when there are no math channels at all: the `+` is the only way to create the first
|
||||
// one, so it cannot be conditional on one already existing.
|
||||
if (!math_divider_done) {
|
||||
gtk_list_box_append(GTK_LIST_BOX(list), make_math_divider(app, !app.signals_locked));
|
||||
gtk_list_box_append(GTK_LIST_BOX(list), make_math_divider(app));
|
||||
}
|
||||
app.dbg_signal_plain = plain;
|
||||
app.dbg_signal_math_rows = math_rows;
|
||||
// The proposal as the model states it, to compare with the rows built: the signals not acquired, or every
|
||||
// listed signal while the badges are overflowed.
|
||||
app.dbg_signal_addable = 0;
|
||||
for (std::size_t i : listed_signals(signals)) {
|
||||
if (overflowed || !app.model->signal_enabled(i)) ++app.dbg_signal_addable;
|
||||
}
|
||||
app.dbg_signal_empty_label =
|
||||
app.signals_empty != nullptr && gtk_widget_get_visible(app.signals_empty) != FALSE ? 1 : 0;
|
||||
app.dbg_signal_rows = 0;
|
||||
for (GtkWidget* c = gtk_widget_get_first_child(list); c != nullptr;
|
||||
c = gtk_widget_get_next_sibling(c)) {
|
||||
@@ -608,10 +629,7 @@ GtkWidget* build_signal_panel(App& app) {
|
||||
// The hint line under it, which is where the "signals_config" hint joins: the heading already
|
||||
// carries one, and the rule the layer follows is a hint beside the thing it explains.
|
||||
app.signals_note = GTK_WIDGET(gtk_builder_get_object(ui, "signals_note"));
|
||||
app.signals_edit = GTK_WIDGET(gtk_builder_get_object(ui, "signals_edit"));
|
||||
if (app.signals_edit != nullptr) {
|
||||
g_signal_connect(app.signals_edit, "clicked", G_CALLBACK(on_pane_edit_clicked), &app);
|
||||
}
|
||||
app.signals_empty = GTK_WIDGET(gtk_builder_get_object(ui, "signals_empty"));
|
||||
if (box == nullptr || app.signal_list == nullptr) {
|
||||
// A missing id is a mistake in the `.ui`: the panel comes up empty rather than taking the window with
|
||||
// it, and `[udpscope] signals panel:` reads the difference.
|
||||
|
||||
Reference in new issue
Block a user