diff --git a/src/ui_app.hpp b/src/ui_app.hpp index e68c63c..511bedb 100644 --- a/src/ui_app.hpp +++ b/src/ui_app.hpp @@ -618,6 +618,9 @@ struct App { GtkWidget* strip_box = nullptr; GtkWidget* strip_cursor_box = nullptr; ///< the cursor chips; insensitive with the cursors off GtkWidget* strip_measure_box = nullptr; ///< the measurement chips + GtkWidget* strip_cursor_group = nullptr; ///< the Cursors button and its chips; shown with the cursors on + GtkWidget* strip_measure_group = nullptr; ///< the Measures button and its chips; shown with measurements + GtkWidget* strip_sep = nullptr; ///< between the groups; shown when both are std::vector strip_cursor_who; std::vector strip_cursor_deltas; std::vector strip_measure_labels; diff --git a/src/ui_cursors.cpp b/src/ui_cursors.cpp index be9bf33..a18b064 100644 --- a/src/ui_cursors.cpp +++ b/src/ui_cursors.cpp @@ -154,6 +154,7 @@ void on_cursors_toggled(GtkToggleButton* button, gpointer user) { // The dock's copy and the strip's chips follow: switched off they stay on screen, insensitive, reading // `--`. One function, so the two cannot disagree. dock_set_cursors_sensitive(*app, on); + sync_readouts_strip(*app); // the strip's Cursors group (and the strip itself) follows at once if (!on) update_cursors_pane(*app); if (on) { app->rows->reset_cursors(); diff --git a/src/ui_dock.cpp b/src/ui_dock.cpp index 41f2963..ae1bbb4 100644 --- a/src/ui_dock.cpp +++ b/src/ui_dock.cpp @@ -154,17 +154,22 @@ void build_readouts_strip(App& app) { clear_box(app.strip_box); app.strip_cursor_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); app.strip_measure_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - gtk_box_append(GTK_BOX(app.strip_box), + // Each section is a group of its own so the strip can show only the one that has something to say. + app.strip_cursor_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + gtk_box_append(GTK_BOX(app.strip_cursor_group), strip_button("Cursors", G_CALLBACK(on_edit_cursors), &app, "the cursors' full panel")); - gtk_box_append(GTK_BOX(app.strip_box), app.strip_cursor_box); - GtkWidget* sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL); - gtk_widget_set_margin_top(sep, 5); - gtk_widget_set_margin_bottom(sep, 5); - gtk_box_append(GTK_BOX(app.strip_box), sep); - gtk_box_append(GTK_BOX(app.strip_box), + gtk_box_append(GTK_BOX(app.strip_cursor_group), app.strip_cursor_box); + app.strip_sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL); + gtk_widget_set_margin_top(app.strip_sep, 5); + gtk_widget_set_margin_bottom(app.strip_sep, 5); + app.strip_measure_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + gtk_box_append(GTK_BOX(app.strip_measure_group), strip_button("Measures", G_CALLBACK(on_edit_measures), &app, "the measurements' full panel")); - gtk_box_append(GTK_BOX(app.strip_box), app.strip_measure_box); + gtk_box_append(GTK_BOX(app.strip_measure_group), app.strip_measure_box); + gtk_box_append(GTK_BOX(app.strip_box), app.strip_cursor_group); + gtk_box_append(GTK_BOX(app.strip_box), app.strip_sep); + gtk_box_append(GTK_BOX(app.strip_box), app.strip_measure_group); } } // namespace @@ -264,12 +269,7 @@ void dock_rebuild_measures(App& app) { gtk_widget_add_css_class(none, "caption"); gtk_widget_add_css_class(none, "dim-label"); gtk_box_append(GTK_BOX(app.dock_measures_box), none); - if (app.strip_measure_box != nullptr) { - GtkWidget* strip_none = gtk_label_new("no measurements"); - gtk_widget_add_css_class(strip_none, "caption"); - gtk_widget_add_css_class(strip_none, "dim-label"); - gtk_box_append(GTK_BOX(app.strip_measure_box), strip_none); - } + // The strip has no placeholder: its Measures group is hidden while there are no measurements. return; } for (std::size_t i = 0; i < app.measures.size(); ++i) { @@ -351,12 +351,25 @@ void sync_readouts_strip(App& app) { // open -- and is not a readout you can keep an eye on while looking at the trace. `collapsed` is the // split view's value from the previous frame (the tick calls this before `set_collapsed()`), so the // strip follows a collapse or a widening one tick late; `show-sidebar` is the wish just re-asserted. + // + // And only with something to read: a section is there when its feature is (cursors switched on, + // at least one measurement), and with neither the strip is gone -- it is a readout, not a control + // strip, and an empty line under the plot is 26 px of nothing. (The one place "hide" beats "disable": + // the dock keeps both blocks on screen, so the empty state is never invisible.) AdwOverlaySplitView* split = ADW_OVERLAY_SPLIT_VIEW(app.side_view); - const bool want = !adw_overlay_split_view_get_show_sidebar(split) || - adw_overlay_split_view_get_collapsed(split); - if ((gtk_widget_get_visible(app.readouts_strip) != FALSE) != want) { - gtk_widget_set_visible(app.readouts_strip, want); - } + const bool cursors = app.cursors_toggle != nullptr && + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(app.cursors_toggle)); + const bool measures = !app.measures.empty(); + const bool want = (!adw_overlay_split_view_get_show_sidebar(split) || + adw_overlay_split_view_get_collapsed(split)) && + (cursors || measures); + auto show = [](GtkWidget* w, bool v) { + if (w != nullptr && (gtk_widget_get_visible(w) != FALSE) != v) gtk_widget_set_visible(w, v); + }; + show(app.strip_cursor_group, cursors); + show(app.strip_measure_group, measures); + show(app.strip_sep, cursors && measures); + show(app.readouts_strip, want); } namespace { diff --git a/src/ui_measure.cpp b/src/ui_measure.cpp index faa76eb..432e3f0 100644 --- a/src/ui_measure.cpp +++ b/src/ui_measure.cpp @@ -112,6 +112,7 @@ void rebuild_measures_pane(App& app) { app.measure_text.emplace_back(); } dock_rebuild_measures(app); + sync_readouts_strip(app); // the strip's Measures group (and the strip itself) follows at once, not a tick later update_measures(app, /*force=*/true, /*accumulate=*/true); } diff --git a/src/ui_probe.cpp b/src/ui_probe.cpp index f77cd92..561a853 100644 --- a/src/ui_probe.cpp +++ b/src/ui_probe.cpp @@ -4339,9 +4339,10 @@ static int dock_width_fits(App& a, int* dock_w, int* side_w) { /// The readouts strip against the dock it mirrors: `same_text=1` when every strip label's text equals the /// dock's (the cursors' `who` and delta cells, the measurements' values) and the counts agree. The strip is -/// shown whenever the dock is not beside the plot, so `expected` is `!dock_shown || collapsed` (`dock_shown` -/// is `show-sidebar`, `collapsed` the split view's, both read in the same frame as `visible`), `visible_ok` -/// is `visible == expected`, and `h` is the strip's fixed 26 whenever it is visible. +/// shown whenever the dock is not beside the plot and there is something to read, so `expected` is +/// `(!dock_shown || collapsed) && (cursors on || a measurement)` (`dock_shown` is `show-sidebar`, `collapsed` +/// the split view's, both read in the same frame as `visible`), `visible_ok` is `visible == expected`, and +/// `h` is the strip's fixed 26 whenever it is visible. /// `sens_same` is the strip's cursor chips sharing the dock grid's sensitivity. static void strip_report(App& a, const char* tag, const char* what) { const auto text = [](GtkWidget* w) { return std::string(gtk_label_get_text(GTK_LABEL(w))); }; @@ -4365,19 +4366,35 @@ static void strip_report(App& a, const char* tag, const char* what) { const bool collapsed = a.side_view != nullptr && adw_overlay_split_view_get_collapsed(ADW_OVERLAY_SPLIT_VIEW(a.side_view)) != FALSE; - const bool expected = !dock_shown || collapsed; + // And with something to read: the cursors on, or a measurement (`sync_readouts_strip()`). + const bool has_content = + (a.cursors_toggle != nullptr && + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(a.cursors_toggle))) || + !a.measures.empty(); + const bool expected = (!dock_shown || collapsed) && has_content; const bool sens_same = a.strip_cursor_box != nullptr && a.dock_cursor_grid != nullptr && gtk_widget_get_sensitive(a.strip_cursor_box) == gtk_widget_get_sensitive(a.dock_cursor_grid); + // `groups=`: which sections of the strip are shown, each against its own + // feature (cursors on, a measurement present, both); `groups_ok=1` when all three agree. + const bool cur_on = a.cursors_toggle != nullptr && + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(a.cursors_toggle)); + const bool mea_on = !a.measures.empty(); + const bool g_cur = a.strip_cursor_group != nullptr && gtk_widget_get_visible(a.strip_cursor_group); + const bool g_mea = a.strip_measure_group != nullptr && gtk_widget_get_visible(a.strip_measure_group); + const bool g_sep = a.strip_sep != nullptr && gtk_widget_get_visible(a.strip_sep); + const bool groups_ok = g_cur == cur_on && g_mea == mea_on && g_sep == (cur_on && mea_on); std::fprintf(stderr, "[%s-probe] %-14s strip: visible=%d expected=%d visible_ok=%d dock_shown=%d cursors=%zu " - "measures=%zu same_text=%d h=%d sens_same=%d collapsed=%d window=%d\n", + "measures=%zu same_text=%d h=%d sens_same=%d collapsed=%d window=%d groups=%d%d%d " + "groups_ok=%d\n", tag, what, visible ? 1 : 0, expected ? 1 : 0, visible == expected ? 1 : 0, dock_shown ? 1 : 0, a.strip_cursor_deltas.size(), a.strip_measure_labels.size(), same, a.readouts_strip != nullptr ? gtk_widget_get_height(a.readouts_strip) : -1, sens_same ? 1 : 0, collapsed ? 1 : 0, - a.window != nullptr ? gtk_widget_get_width(a.window) : -1); + a.window != nullptr ? gtk_widget_get_width(a.window) : -1, + g_cur ? 1 : 0, g_mea ? 1 : 0, g_sep ? 1 : 0, groups_ok ? 1 : 0); } /// Puts the dock away through the wish the tick re-asserts and reads the strip with it hidden @@ -4761,6 +4778,9 @@ gboolean measures_probe_step(gpointer user) { std::fprintf(stderr, "[measures-probe] no acquired signal to measure\n"); return G_SOURCE_REMOVE; } + // The "neither" state: no measurement yet and (from a fresh config) the cursors off, so the strip is + // hidden -- `visible=0 expected=0 groups=000`. (The other readings need a state with something in it.) + strip_report(*a, "measures", "start"); for (int i = 0; i < measure_type_count(); ++i) { Measure m; m.signal = signal;