ui: the sidebar's tabs become one dock; Measure and Signals by keyboard section; session keeps the dock's state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-11-02 20:37:55 +01:00
1 parent 31ff778e07
commit 4b65197f56
19 files changed
+421 -419

No files matched your search

+22 -34
View File
@@ -241,13 +241,11 @@ struct App {
GtkWidget* trigger_panel = nullptr;
GtkWidget* signal_panel = nullptr;
GtkWidget* side_box = nullptr;
/// The sidebar's tab stack and its switcher. The stack is what chooses which panels are on
/// screen; a feature's own on/off never does (it sets its panel's sensitivity instead).
GtkWidget* side_stack = nullptr;
GtkWidget* side_switcher = nullptr;
/// The page name the stack is showing, and the one value the session file persists about the
/// sidebar. "signals" on a first run.
std::string side_tab = "signals";
/// 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
/// removes a section (it sets its panel's sensitivity instead).
GtkWidget* dock_page = nullptr;
GtkWidget* dock_scroller = nullptr;
GtkWidget* arm_button = nullptr;
/// The Force button, beside the Arm/Disarm one: one acquisition now, with the configured pre/post
/// window. It carries no state of its own -- `sync_arm_buttons()` states the one it has
@@ -477,18 +475,6 @@ struct App {
/// works, and every other line of the dump still passes.
std::string dbg_toolbar_icons;
std::string dbg_app_icons;
/// What the switcher is: how many pills it holds, and how many labels it is *drawing*. The tabs
/// are words (`wide` policy, a title per page), so the label count must equal the page count.
int dbg_switcher_pills = -1;
int dbg_switcher_labels = -1;
/// How many `GtkImage`s the switcher has *mapped*: a tab is its title and nothing else, so this
/// must be 0 -- an icon, or the missing-icon placeholder GTK draws for a page with no `icon-name`.
int dbg_switcher_images = -1;
/// How many pages the sidebar stack holds: `pills` and `labels` must both equal it.
int dbg_switcher_pages = -1;
/// The switcher's own size, beside it: what the tab bar costs the column.
int dbg_switcher_w = -1;
int dbg_switcher_h = -1;
/// The panes' own switches and the header bar's buttons, read together by the exit probe: the two
/// must agree, and `dbg_panel_drove_toolbar` is the pane's switch having moved the button.
int dbg_panel_switch_trigger = -1;
@@ -504,7 +490,6 @@ struct App {
/// The Arm button, read with the trigger off: it is the pane's one runtime control, so it is the one
/// thing in the body that *is* disabled with the mode.
int dbg_arm_sensitive = -1;
int dbg_switcher_policy = -1;
/// The header bar, so the dump can report the order the controls are in: a header bar is a
/// *titlebar*, not a child of the window, so walking down from the window does not reach it.
GtkWidget* toolbar = nullptr;
@@ -881,7 +866,7 @@ struct App {
std::string source_kind = "udps";
SourceConfig source_opts;
std::string source_title;
/// The panels the source kind added to `side_stack`, in tab order. Empty for UDPS and in file mode.
/// The panels the source kind added to the slide-over, in page order. Empty for UDPS and in file mode.
std::vector<SourcePanelPage> source_panels;
bool source_has_datagrams = true; ///< the kind reports raw datagrams; false leaves the capture button disabled
guint layout_save_source = 0;
@@ -1139,7 +1124,14 @@ struct App {
int dbg_signal_row_h = 0;
int dbg_trigger_w = 0;
int dbg_trigger_h = 0;
/// The signal list has rows (the dock has no tab to be "current" on any more).
int dbg_signal_page_current = 0;
/// The dock as the exit probe found it: the split view showing the sidebar, the column sensitive (it is
/// not until the stream has signals), its width, and how many `GtkScrolledWindow`s it holds directly.
int dbg_dock_shown = -1;
int dbg_dock_sensitive = -1;
int dbg_dock_w = -1;
int dbg_dock_scrollers = -1;
int dbg_trigger_sensitive = 0;
int dbg_trigger_items = 0;
int dbg_trigger_selected = -1;
@@ -1161,7 +1153,6 @@ struct App {
double dbg_trigger_time = 0.0;
int dbg_trigger_time_valid = -1;
std::string dbg_mode_live;
int dbg_switcher_visible = 0;
int dbg_trigger_sensitive_default = 0;
/// The detail string the popover and the exit dump read. The dump used to read the label,
@@ -1572,9 +1563,9 @@ constexpr int kEventThumbH = 46;
constexpr int kEventThumbMaxH = 104;
inline int g_stats_phase = 0;
inline std::vector<std::uint64_t> g_stats_n_before;
/// The same for the tab-switch probe: the accumulator counts recorded while the Measure page was
/// still current, so the report can say whether any of them advanced afterwards.
inline std::vector<std::uint64_t> g_side_tab_n_before;
/// The same for the dock-section probe: the accumulator counts recorded while the Measure section was
/// still scrolled into view, so the report can say whether any of them advanced after it scrolled out.
inline std::vector<std::uint64_t> g_dock_section_n_before;
inline double g_trigrenum_edge = 0.0;
inline std::uint64_t g_trigrenum_latches = 0;
inline ConditionProbe g_condition;
@@ -1600,11 +1591,6 @@ void config_dialog_refresh(ConfigDialog& d);
void open_configurations(App& app);
void save_layout_now(App& app);
/// The sidebar's tabs are words: hide the icon every `AdwViewSwitcherButton` draws (the
/// missing-icon placeholder, for a page with a title and no `icon-name`), for the buttons that exist
/// and for any page added later. Call once, before `build_source_panels()`.
void keep_tabs_icon_free(App& app);
/// Capability panels (`src/ui_source.cpp`). `build_source_panels` adds the source kind's panels as
/// pages of the slide-over (`source-<id>`), once, before the first tick. `apply_source_panels` sets every panel's
/// sensitivity from `connected` -- disabled, never hidden.
@@ -1803,8 +1789,9 @@ void row_menu_fill(RowMenuState& st);
GtkWidget* open_row_menu(App& app, int row, double x, double y, GtkWidget* parent, bool autohide = true);
void on_right_click(GtkGestureClick* gesture, int, double x, double y, gpointer user);
gboolean on_drop_channel(GtkDropTarget*, const GValue* value, double x, double y, gpointer user);
void on_side_tab_changed(GObject*, GParamSpec*, gpointer user);
void select_side_tab(App& app, const char* name);
/// Scrolls the dock to a section: "signals" (the top) or "measure" (the cursors panel, which the
/// measurements follow). An unknown name is a no-op.
void dock_show_section(App& app, const char* section);
/// Reads each pane's own switch back off the header bar's button, and the other way round: the two are
/// one control in two places, and `panel_switch_sync` keeps the write from looking like a click.
void sync_panel_switches(App& app);
@@ -1907,8 +1894,9 @@ gboolean measurepin_live_wait(gpointer user);
gboolean measure_trace(gpointer user);
gboolean stats_probe_report(gpointer user);
gboolean stats_probe(gpointer user);
gboolean side_tab_probe_report(gpointer user);
gboolean side_tab_probe(gpointer user);
gboolean dock_section_probe_report(gpointer user);
gboolean dock_section_probe(gpointer user);
gboolean dock_session_probe(gpointer user); // UDPSCOPE_PROBE_DOCK_SESSION_MS
/// The slide-over panel (`src/ui_slideover.cpp`): one panel at a time over the plot's right edge, a page
/// per setup surface, each page in a scroller of its own.
void build_slideover(App& app); // once, after the plot_overlay exists