ui: the signal list is the signals slide-over page, opened by a + badge or g
This commit is contained in:
1 parent
e36f2a202b
commit
0dd235e41c
11 files changed
+98
-75
No files matched your search
+11
-16
@@ -2864,19 +2864,13 @@ void on_right_click(GtkGestureClick* gesture, int, double x, double y, gpointer
|
||||
open_row_menu(*app, row, x, y, widget);
|
||||
}
|
||||
|
||||
/// Scrolls the dock to a section. The dock is one scroller (`App::dock_scroller`) holding the signal list,
|
||||
/// the cursors and the measurements top to bottom, so "measure" is the cursors panel's offset in the
|
||||
/// page, and "signals" the top. Nothing is hidden or removed: both stay in the page. An unknown name is a
|
||||
/// no-op, so a key bound to a section a later version drops cannot blank anything.
|
||||
/// Scrolls the dock to a section. The dock is one scroller (`App::dock_scroller`) holding the cursors and
|
||||
/// the measurements top to bottom, so "measure" is the cursors panel's offset in the page. Nothing is
|
||||
/// hidden or removed: both stay in the page. An unknown name (the signal list is a slide-over page now,
|
||||
/// not a dock section) is a no-op, so a key bound to a section a later version drops cannot blank anything.
|
||||
void dock_show_section(App& app, const char* section) {
|
||||
if (app.dock_scroller == nullptr || app.dock_page == nullptr || section == nullptr) return;
|
||||
GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(app.dock_scroller));
|
||||
if (std::strcmp(section, "signals") == 0) {
|
||||
// The list is the first section: its own offset in the page is the page's margin, and "go to Signals"
|
||||
// means the top of the dock.
|
||||
gtk_adjustment_set_value(adj, 0.0);
|
||||
return;
|
||||
}
|
||||
if (std::strcmp(section, "measure") != 0 || app.cursors_panel == nullptr) return;
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t at;
|
||||
@@ -4679,7 +4673,7 @@ void run_app_action(App& app, AppAction action) {
|
||||
toggle(app.cursors_toggle);
|
||||
break;
|
||||
case AppAction::ShowMeasure: dock_show_section(app, "measure"); break;
|
||||
case AppAction::ShowSignals: dock_show_section(app, "signals"); break;
|
||||
case AppAction::ShowSignals: slideover_toggle(app, "signals"); break;
|
||||
case AppAction::LayoutEditor:
|
||||
toggle(app.layout_toggle);
|
||||
break;
|
||||
@@ -5153,9 +5147,9 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
gtk_popover_set_has_arrow(GTK_POPOVER(app->status_popover), TRUE);
|
||||
}
|
||||
|
||||
// The right-hand side is the dock: one scroller, the signal list first (it is what is dragged onto the
|
||||
// plot), then the cursors and the measurements. Every section is always there -- "go to Measure" scrolls
|
||||
// to it instead of switching a tab, so the measurements are as live as the list above them.
|
||||
// The right-hand side is the dock: one scroller, the cursors then the measurements. Every section is
|
||||
// always there -- "go to Measure" scrolls to it instead of switching a tab, so the measurements are as
|
||||
// live as the cursors above them. The signal list is a slide-over page (reached by the `+` badge).
|
||||
// `side` and `dock_page`/`dock_scroller` are resolved above with the rest of the skeleton and checked
|
||||
// by the null guard; assign the member here.
|
||||
app->side_box = side;
|
||||
@@ -5169,9 +5163,10 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
// would spread its rows (or draw its frame) over empty space under the last one.
|
||||
gtk_widget_set_valign(app->trigger_panel, GTK_ALIGN_START);
|
||||
slideover_add_page(*app, "trigger", "Trigger", app->trigger_panel);
|
||||
// The signal list is the `signals` page, reached by the `+` badge or `g`; the panel is built here, after
|
||||
// `build_slideover`, and registered once.
|
||||
app->signal_panel = build_signal_panel(*app);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), app->signal_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
slideover_add_page(*app, "signals", "Signals", app->signal_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), app->cursors_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
gtk_box_append(GTK_BOX(app->dock_page), measures_panel);
|
||||
|
||||
+3
-2
@@ -156,6 +156,7 @@ struct BadgeBar {
|
||||
GtkWidget* system_slot = nullptr; ///< right: Trigger, Source
|
||||
Badge trigger;
|
||||
Badge source; ///< opens the first source page; a read-out when the source has none
|
||||
Badge plus; ///< the "+" in the signals slot: opens the `signals` page
|
||||
};
|
||||
|
||||
struct App {
|
||||
@@ -253,8 +254,8 @@ struct App {
|
||||
GtkWidget* trigger_panel = nullptr;
|
||||
GtkWidget* signal_panel = nullptr;
|
||||
GtkWidget* side_box = nullptr;
|
||||
/// The dock: the sidebar's one scroller and the box inside it, which holds the signal list, the
|
||||
/// cursors and the measurements (`dock_show_section()` scrolls it). A feature's own on/off never
|
||||
/// The dock: the sidebar's one scroller and the box inside it, which holds the cursors and
|
||||
/// the measurements (`dock_show_section()` scrolls it). A feature's own on/off never
|
||||
/// removes a section (it sets its panel's sensitivity instead).
|
||||
GtkWidget* dock_page = nullptr;
|
||||
GtkWidget* dock_scroller = nullptr;
|
||||
|
||||
@@ -86,6 +86,9 @@ void build_badge_bar(App& app) {
|
||||
gtk_widget_set_can_target(bb.source.button, FALSE);
|
||||
gtk_widget_set_focusable(bb.source.button, FALSE);
|
||||
}
|
||||
// The full signal list. Insensitive until the stream has declared signals ("disable, do not hide").
|
||||
make_badge(app, bb.plus, bb.signals_slot, "+", "signals");
|
||||
gtk_widget_set_tooltip_text(bb.plus.button, "all signals: switch more on, add a math channel");
|
||||
sync_badge_bar(app);
|
||||
}
|
||||
|
||||
@@ -100,6 +103,10 @@ void sync_badge_bar(App& app) {
|
||||
set_text_if_changed(bb.trigger.state, trigger_badge_state(enabled, armed, pending, has_event));
|
||||
set_open(bb.trigger.button, slideover_page(app) == bb.trigger.page);
|
||||
|
||||
if (bb.plus.button != nullptr) {
|
||||
gtk_widget_set_sensitive(bb.plus.button, !app.model->signals().empty());
|
||||
set_open(bb.plus.button, slideover_page(app) == "signals");
|
||||
}
|
||||
if (bb.source.button == nullptr) return;
|
||||
// The source's name and link state. A file has neither a link nor a kind worth naming.
|
||||
const bool file_mode = app.replaying || !app.model->config().open_waveform.empty();
|
||||
|
||||
@@ -92,7 +92,7 @@ constexpr HelpHint kHelpHints[] = {
|
||||
{"signals", "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. Nothing is buffered "
|
||||
"until it is switched on, and only acquired signals appear in the drop targets and the "
|
||||
"measurement editor. A signal that is dragged onto a plot is drawn there.",
|
||||
"measurement editor. A signal's badge, dragged onto a plot, draws it there.",
|
||||
"G", &App::signals_head},
|
||||
{"events", "The event history",
|
||||
"Every triggered acquisition the cache keeps, with a picture of each. Open one to read it as a "
|
||||
@@ -117,8 +117,8 @@ constexpr HelpHint kHelpHints[] = {
|
||||
"", &App::trigger_search},
|
||||
{"signals_config", "Configuring signals",
|
||||
"The list is locked until the pencil above opens the per-signal controls: the calibration that "
|
||||
"maps raw counts to a unit, the colour a trace is drawn in, and which plot a signal is dragged "
|
||||
"onto. A signal nothing measures can be switched off to stop acquiring it.",
|
||||
"maps raw counts to a unit, the colour a trace is drawn in, and which plot a signal is drawn "
|
||||
"on. A signal nothing measures can be switched off to stop acquiring it.",
|
||||
"", &App::signals_note},
|
||||
{"window", "The time window",
|
||||
"How much time is on screen, counted back from the newest sample. Type a span, change its unit, "
|
||||
|
||||
+42
-24
@@ -93,17 +93,14 @@ int on_render_probe(GtkGLArea*, GdkGLContext*, gpointer user) {
|
||||
return FALSE; // same reason the adapter does: never stop the emission
|
||||
}
|
||||
|
||||
gboolean exit_probe_step3(gpointer user) {
|
||||
gboolean exit_probe_step3b(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
a->dbg_mode_live = a->status_detail;
|
||||
GtkWidget* window = a->window;
|
||||
a->window = nullptr; // stop the tick before the widgets go
|
||||
a->dbg_side_w = gtk_widget_get_width(a->side_box);
|
||||
a->dbg_side_h = gtk_widget_get_height(a->side_box);
|
||||
// The signal list is the `signals` page, opened by step 3 300 ms ago: its allocation is only real once
|
||||
// the page is open and the reveal has run.
|
||||
a->dbg_signal_w = gtk_widget_get_width(a->signal_panel);
|
||||
a->dbg_signal_h = gtk_widget_get_height(a->signal_panel);
|
||||
a->dbg_trigger_w = gtk_widget_get_width(a->trigger_panel);
|
||||
a->dbg_trigger_h = gtk_widget_get_height(a->trigger_panel);
|
||||
// How many rows of the signal list carry a drag source: dragging a signal onto a plot is how a plot is
|
||||
// filled, so it has to survive both pane modes -- the locked list drops the tick, the menu and the `+`,
|
||||
// and this is the reading that says it did not drop the drag with them. One row is the `MATH` divider,
|
||||
@@ -211,11 +208,25 @@ gboolean exit_probe_step3(gpointer user) {
|
||||
}
|
||||
// `dbg_window_text` is kept by `sync_window_display()`, the only place that knows the span the
|
||||
// window control is showing: there is no label to read it back from any more.
|
||||
slideover_close(*a); // opened by step 1 for the allocations read above
|
||||
slideover_close(*a); // opened by step 3 for the signal list's allocations read above
|
||||
if (window != nullptr) gtk_window_destroy(GTK_WINDOW(window));
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// Reads the sizes that need the `trigger` page open (step 1 opened it), then swaps it for the `signals`
|
||||
/// page -- the slide-over holds one page -- and leaves step 3b the 300 ms the reveal needs.
|
||||
gboolean exit_probe_step3(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
a->dbg_mode_live = a->status_detail;
|
||||
a->dbg_side_w = gtk_widget_get_width(a->side_box);
|
||||
a->dbg_side_h = gtk_widget_get_height(a->side_box);
|
||||
a->dbg_trigger_w = gtk_widget_get_width(a->trigger_panel);
|
||||
a->dbg_trigger_h = gtk_widget_get_height(a->trigger_panel);
|
||||
slideover_open(*a, "signals");
|
||||
g_timeout_add(300, exit_probe_step3b, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// The frame after the pane was locked: read the compact arm control's *allocation*, then leave the
|
||||
/// configuration mode and trigger mode as step 2 used to, and hand the chain back its 400 ms. The button
|
||||
/// is created hidden and this is the first tick it is shown, so its width in the same tick would always
|
||||
@@ -1794,15 +1805,16 @@ gboolean stats_probe(gpointer user) {
|
||||
}
|
||||
|
||||
/// Which section the dock's scroller is showing at its top: "measure" once it has been scrolled to the
|
||||
/// cursors panel or past it, "signals" above that. Read from the adjustment, so it is the state the user
|
||||
/// would see and not the last call made.
|
||||
/// measurements' heading or past it, "cursors" above that (the cursors panel is the dock's first section;
|
||||
/// the signal list is a slide-over page). Read from the adjustment, so it is the state the user would see
|
||||
/// and not the last call made.
|
||||
static const char* dock_section_in_view(App& a) {
|
||||
if (a.dock_scroller == nullptr || a.cursors_panel == nullptr || a.dock_page == nullptr) return "none";
|
||||
if (a.dock_scroller == nullptr || a.measures_head == nullptr || a.dock_page == nullptr) return "none";
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t at;
|
||||
if (!gtk_widget_compute_point(a.cursors_panel, a.dock_page, &zero, &at)) return "none";
|
||||
if (!gtk_widget_compute_point(a.measures_head, a.dock_page, &zero, &at)) return "none";
|
||||
GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(a.dock_scroller));
|
||||
return gtk_adjustment_get_value(adj) > 0.5 * at.y ? "measure" : "signals";
|
||||
return gtk_adjustment_get_value(adj) > 0.5 * at.y ? "measure" : "cursors";
|
||||
}
|
||||
|
||||
/// Phase two of the dock-section probe: the accumulators must have advanced while the Measure section was
|
||||
@@ -1825,7 +1837,7 @@ gboolean dock_section_probe_report(gpointer user) {
|
||||
}
|
||||
}
|
||||
if (a->dock_page != nullptr) gtk_widget_set_size_request(a->dock_page, -1, -1);
|
||||
if (a->signal_panel != nullptr) gtk_widget_set_size_request(a->signal_panel, -1, -1);
|
||||
if (a->cursors_panel != nullptr) gtk_widget_set_size_request(a->cursors_panel, -1, -1);
|
||||
std::fprintf(stderr,
|
||||
"[side-tab-probe] section=%s rows=%zu accums=%zu advanced=%" G_GUINT64_FORMAT
|
||||
" min_n=%" G_GUINT64_FORMAT "\n",
|
||||
@@ -1847,10 +1859,11 @@ gboolean dock_section_probe(gpointer user) {
|
||||
if (!padded && a->dock_page != nullptr) {
|
||||
padded = true;
|
||||
gtk_widget_set_size_request(a->dock_page, -1, 3000);
|
||||
// ... and the signal list taller than any window too, which is what pushes the Measure section below
|
||||
// the viewport at offset 0 (padding the page's end only makes it scrollable, and on a window taller than
|
||||
// the section's y the section would stay in view). Undone in the report.
|
||||
if (a->signal_panel != nullptr) gtk_widget_set_size_request(a->signal_panel, -1, 3000);
|
||||
// ... and the cursors panel (the dock's first section, above the measurements) taller than any window
|
||||
// too, which is what pushes the measurements below the viewport at offset 0 (padding the page's end only
|
||||
// makes it scrollable, and on a window taller than the section's y the section would stay in view).
|
||||
// Undone in the report.
|
||||
if (a->cursors_panel != nullptr) gtk_widget_set_size_request(a->cursors_panel, -1, 3000);
|
||||
g_timeout_add(400, dock_section_probe, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -1892,16 +1905,16 @@ gboolean dock_section_probe(gpointer user) {
|
||||
gtk_check_button_set_active(GTK_CHECK_BUTTON(a->measures_stats_check), TRUE);
|
||||
}
|
||||
GtkAdjustment* dock_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(a->dock_scroller));
|
||||
// From the top, is the measure section (the cursors panel, where `dock_show_section("measure")` lands)
|
||||
// below the viewport? Only then is scrolling to it a move *into* view and back out a move *away*; a
|
||||
// From the top, is the measure section (the measurements' heading) below the viewport? Only then is scrolling to it a move *into* view and back out a move *away*; a
|
||||
// section already in view at offset 0 would make the whole probe a reading of nothing.
|
||||
gtk_adjustment_set_value(dock_adj, 0.0);
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t measure_at = GRAPHENE_POINT_INIT(-1.f, -1.f);
|
||||
const bool located =
|
||||
a->cursors_panel != nullptr && gtk_widget_compute_point(a->cursors_panel, a->dock_page, &zero, &measure_at);
|
||||
a->measures_head != nullptr && gtk_widget_compute_point(a->measures_head, a->dock_page, &zero, &measure_at);
|
||||
const bool outside_at_top = located && static_cast<double>(measure_at.y) >= gtk_adjustment_get_page_size(dock_adj);
|
||||
dock_show_section(*a, "measure");
|
||||
dock_show_section(*a, "measure"); // lands on the cursors panel, the dock's top: a no-op scroll
|
||||
if (located) gtk_adjustment_set_value(dock_adj, measure_at.y); // ... so go to the measurements themselves
|
||||
// Where the dock went: `scrolled` is its offset and `reach` the most it can scroll. A reach of 0 would
|
||||
// mean the content fits and "away from the section" is not a state this run can reach.
|
||||
const double scrolled = gtk_adjustment_get_value(dock_adj);
|
||||
@@ -1911,7 +1924,7 @@ gboolean dock_section_probe(gpointer user) {
|
||||
for (const MeasureStatsAccum& s : a->measure_stats) g_dock_section_n_before.push_back(s.n);
|
||||
// Away from the section the measurements are in: they are scrolled out of the viewport (the widgets
|
||||
// stay mapped, only clipped), and the tick keeps calling `update_measures()` regardless.
|
||||
dock_show_section(*a, "signals");
|
||||
gtk_adjustment_set_value(dock_adj, 0.0);
|
||||
std::fprintf(stderr,
|
||||
"[side-tab-probe] section=%s accums=%zu baseline recorded (measure at scroll %.0f of %.0f, "
|
||||
"outside_at_top=%d)\n",
|
||||
@@ -6427,12 +6440,17 @@ gboolean keys_probe(gpointer user) {
|
||||
line += buf;
|
||||
}
|
||||
{
|
||||
// `g` is the signal list's slide-over page now: it opens it, the dock does not move, and a second `g`
|
||||
// closes it again.
|
||||
const double before = dock_value();
|
||||
run_app_action(*a, AppAction::ShowSignals);
|
||||
const bool opened = slideover_page(*a) == "signals";
|
||||
const double after = dock_value();
|
||||
run_app_action(*a, AppAction::ShowSignals);
|
||||
const bool closed = slideover_page(*a).empty();
|
||||
char buf[96];
|
||||
std::snprintf(buf, sizeof(buf), " g: dock %.0f->%.0f at_top=%d |", before, after,
|
||||
std::fabs(after) < 1.0 ? 1 : 0);
|
||||
std::snprintf(buf, sizeof(buf), " g: page=%d dock_still=%d closed=%d |", opened ? 1 : 0,
|
||||
std::fabs(after - before) < 1.0 ? 1 : 0, closed ? 1 : 0);
|
||||
line += buf;
|
||||
}
|
||||
before_after("l: layout", a->layout_toggle, AppAction::LayoutEditor);
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||
//
|
||||
// The signal list: one row per signal with its colour chip and acquire tick, the panel around it,\n// and the drop that puts a signal on a row.
|
||||
// The signal list: one row per signal with its colour chip and acquire tick, the panel around it,
|
||||
// and the drop that puts a signal on a row.
|
||||
|
||||
#include "ui_app.hpp"
|
||||
|
||||
|
||||
+5
-5
@@ -454,12 +454,12 @@ constexpr TourStep kTourSteps[] = {
|
||||
"the view then freezes on the first event that matches, however rare it is.",
|
||||
&App::trigger_body},
|
||||
{"side", nullptr, "The dock",
|
||||
"The dock beside the plot: the list of signals, then the cursors and the measurements. The "
|
||||
"buttons in the bar above show and hide it.",
|
||||
"The dock beside the plot: the cursors, then the measurements. The buttons in the bar above "
|
||||
"show and hide it.",
|
||||
&App::side_box},
|
||||
{"signals", nullptr, "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. A signal has to be "
|
||||
"switched on before it can be plotted, measured or searched.",
|
||||
{"signals", "signals", "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. The + badge opens "
|
||||
"this list; a signal has to be switched on before it can be plotted, measured or searched.",
|
||||
&App::signal_list},
|
||||
{"status", nullptr, "The status line",
|
||||
"The stream's own report, once a second: the display mode, the rate arriving, the window on "
|
||||
|
||||
Reference in new issue
Block a user