ui: draw the sidebar tabs without the placeholder icon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
5bb23e6a0a
commit
0c6e69957d
6 files changed
+75
-8
No files matched your search
@@ -2909,7 +2909,7 @@ now — they are listed so they are not mistaken for anything else:
|
||||
unlocked, because configuring the host is what a first run is for. Each pane's heading also carries its **own switch**, the same control as the
|
||||
header bar's button — clicking either moves both — because a pane that is *disabled* while its
|
||||
feature is off is a pane whose controls you would otherwise have to leave to turn it back on. The
|
||||
switcher's policy is `wide` and each page has a title. With the math
|
||||
switcher's policy is `wide` and each page has a title and no icon (the switcher's own placeholder icon is hidden). With the math
|
||||
list folded into the signal list under a `MATH` divider whose `+` creates a channel — a derived channel already *is* a channel in
|
||||
the model, so the two lists were the same list drawn twice. A tab is one-of-*N*, so no
|
||||
toggle hides a pane any more: cursors-off **disables** its readout and leaves it in place, and
|
||||
|
||||
@@ -252,7 +252,12 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
session file. An icon-only version existed (policy `narrow`, empty titles, the pills named in code by
|
||||
`name_side_tabs()`, because `narrow` still draws a label beside an icon); it was reversed because the
|
||||
glyphs were not understandable. A page added with an empty title is a blank pill, and
|
||||
`switcher: … pills=N labels=N pages=N` reads it as `labels` below `pills`.
|
||||
`switcher: … pills=N labels=N images=0 pages=N` reads it as `labels` below `pills`. **No icon at all**
|
||||
is not what "no `icon-name`" gives you: `AdwViewSwitcherButton` always builds a `GtkImage` and falls
|
||||
back to `image-missing` (the `[x]` placeholder, which also squeezed the title to "Meas..."), and no
|
||||
property turns it off. `keep_tabs_icon_free()` (`src/ui_source.cpp`) hides every image in the switcher,
|
||||
re-hides one that is shown again, and re-runs on the stack's `items-changed` (connected *after* the
|
||||
switcher's own, so the new button exists) and on the switcher's `map`; `images=0` is the reading.
|
||||
- **An *overlaid* sidebar covers the plot, so a drag out of it has nowhere to land.** `collapsed` in an
|
||||
`AdwOverlaySplitView` means the sidebar sits *on* the plot rather than beside it: the pointer is over the
|
||||
list, never over a drop target, and a signal can be dragged but not dropped. `row_drag_sidebar()` hides
|
||||
|
||||
@@ -479,9 +479,15 @@ Two lines of the self-check output are assertions in disguise:
|
||||
substitutes a missing-image placeholder, the button still works, and every other line of the
|
||||
dump still passes. Changing an icon name is a one-line change in `build_toolbar()` and this
|
||||
is the only check that it names something that exists.
|
||||
- `switcher: policy=wide pills=4 labels=4 pages=4 size=308x34` (in the exit dump, beside `panels:`) — the
|
||||
- `switcher: policy=wide pills=4 labels=4 images=0 pages=4 size=308x34` (in the exit dump, beside `panels:`) — the
|
||||
sidebar tabs as the switcher actually draws them, and they are **words**: `labels` counts *mapped labels
|
||||
with text*, so `pills=labels=pages` says every page has a visible title. `pages` is read from the stack
|
||||
with text*, so `pills=labels=pages` says every page has a visible title. **`images` must be `0`**: it
|
||||
counts *mapped* `GtkImage`s inside the switcher. `AdwViewSwitcherButton` builds an icon in every tab, and
|
||||
a page with a title and no `icon-name` gets `image-missing` -- the `[x]` placeholder; the tabs hide it
|
||||
(`keep_tabs_icon_free()`, which also covers pages `build_source_panels()` adds). A nonzero `images`
|
||||
names a tab still drawing an icon or its placeholder (`images=pages` is the unfixed state), and
|
||||
`pills`/`labels` never saw it. Read it on a source with extra pages (`--source probe` under
|
||||
`UDPSCOPE_PROBE_SOURCE_KINDS`) as well as on UDPS. `pages` is read from the stack
|
||||
(2 for UDPS, 3 for the probe source, 4 for the E-1608 with its Analog out and Digital I/O tabs). A
|
||||
`labels` below `pills` is a page added with an empty title; `pills=0` means nothing is mapped, which
|
||||
makes the rest of the line meaningless rather than a pass. `policy=wide` is read rather than assumed.
|
||||
|
||||
+10
-4
@@ -3060,8 +3060,11 @@ void note_panel_controls(App& app) {
|
||||
// pill count reads 0 and the label count means nothing, rather than passing vacuously.
|
||||
int pills = 0;
|
||||
int drawn_labels = 0;
|
||||
int drawn_images = 0;
|
||||
const std::function<void(GtkWidget*)> count_drawn = [&](GtkWidget* w) {
|
||||
if (GTK_IS_TOGGLE_BUTTON(w) && gtk_widget_get_mapped(w)) ++pills;
|
||||
// A *mapped* image is an icon (or its missing-icon placeholder) on a tab: tabs are words only.
|
||||
if (GTK_IS_IMAGE(w) && gtk_widget_get_mapped(w)) ++drawn_images;
|
||||
// A *mapped* label with text: an empty label would be counted by "mapped" alone.
|
||||
if (GTK_IS_LABEL(w) && gtk_widget_get_mapped(w) && gtk_label_get_text(GTK_LABEL(w))[0] != '\0') {
|
||||
++drawn_labels;
|
||||
@@ -3074,6 +3077,7 @@ void note_panel_controls(App& app) {
|
||||
if (app.side_switcher != nullptr) count_drawn(app.side_switcher);
|
||||
app.dbg_switcher_pills = pills;
|
||||
app.dbg_switcher_labels = drawn_labels;
|
||||
app.dbg_switcher_images = drawn_images;
|
||||
// Walked through `get_pages()`, not the widget tree: an `AdwViewStackPage` is a plain `GObject`.
|
||||
app.dbg_switcher_pages =
|
||||
app.side_stack != nullptr
|
||||
@@ -5203,7 +5207,9 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
G_CALLBACK(on_side_tab_changed), app);
|
||||
}
|
||||
// The source kind's own tabs (a generator, digital I/O), added before the first tick so the
|
||||
// pages-versus-pills reading below counts them too.
|
||||
// pages-versus-pills reading below counts them too. The tabs are words, so the switcher's icons
|
||||
// are hidden first and kept hidden for the pages this call adds.
|
||||
keep_tabs_icon_free(*app);
|
||||
build_source_panels(*app);
|
||||
|
||||
// The left side is the events pane *and* the plot, in one row: the pane is a column of its own
|
||||
@@ -6240,10 +6246,10 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
app.dbg_panel_switch_trigger, app.dbg_toolbar_switch_trigger,
|
||||
app.dbg_panel_switch_cursors, app.dbg_toolbar_switch_cursors,
|
||||
app.dbg_panel_drove_toolbar);
|
||||
std::printf("[udpscope] switcher: policy=%s pills=%d labels=%d pages=%d size=%dx%d\n",
|
||||
std::printf("[udpscope] switcher: policy=%s pills=%d labels=%d images=%d pages=%d size=%dx%d\n",
|
||||
app.dbg_switcher_policy == 0 ? "narrow" : (app.dbg_switcher_policy == 1 ? "wide" : "?"),
|
||||
app.dbg_switcher_pills, app.dbg_switcher_labels, app.dbg_switcher_pages,
|
||||
app.dbg_switcher_w, app.dbg_switcher_h);
|
||||
app.dbg_switcher_pills, app.dbg_switcher_labels, app.dbg_switcher_images,
|
||||
app.dbg_switcher_pages, app.dbg_switcher_w, app.dbg_switcher_h);
|
||||
// The help affordance layer: what the contextual-help toggle left on the surfaces, and where the
|
||||
// guided tour stood when the last capture ran. Both are captured on the tick (`capture_help_state`),
|
||||
// so neither asks a destroyed widget anything.
|
||||
|
||||
@@ -444,6 +444,9 @@ struct App {
|
||||
/// 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.
|
||||
@@ -1560,6 +1563,11 @@ 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 `side_stack`, once, before the first tick. `apply_source_panels` sets every panel's
|
||||
/// sensitivity from `connected` -- disabled, never hidden: the tab switcher owns visibility.
|
||||
|
||||
@@ -11,6 +11,48 @@
|
||||
namespace udpscope {
|
||||
namespace ui_impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Hide every `GtkImage` inside `w`, and keep it hidden. `AdwViewSwitcherButton` always builds an
|
||||
/// icon -- in both of its layouts -- and a page with no `icon-name` gets `image-missing`, which is
|
||||
/// the placeholder the sidebar's text tabs used to draw. There is no property or policy that turns
|
||||
/// the icon off, so the image is hidden (a hidden child takes no space: the label gets the pill) and
|
||||
/// a `notify::visible` handler puts it back to hidden if the button ever shows it again.
|
||||
void hide_tab_icons(GtkWidget* w) {
|
||||
if (GTK_IS_IMAGE(w)) {
|
||||
if (g_object_get_data(G_OBJECT(w), "udpscope-icon-hidden") == nullptr) {
|
||||
g_object_set_data(G_OBJECT(w), "udpscope-icon-hidden", GINT_TO_POINTER(1));
|
||||
g_signal_connect(w, "notify::visible", G_CALLBACK(+[](GObject* o, GParamSpec*, gpointer) {
|
||||
GtkWidget* img = GTK_WIDGET(o);
|
||||
if (gtk_widget_get_visible(img)) gtk_widget_set_visible(img, FALSE);
|
||||
}),
|
||||
nullptr);
|
||||
}
|
||||
gtk_widget_set_visible(w, FALSE);
|
||||
return;
|
||||
}
|
||||
for (GtkWidget* c = gtk_widget_get_first_child(w); c != nullptr; c = gtk_widget_get_next_sibling(c)) {
|
||||
hide_tab_icons(c);
|
||||
}
|
||||
}
|
||||
|
||||
void on_tab_pages_changed(GListModel*, guint, guint, guint, gpointer sw) {
|
||||
hide_tab_icons(GTK_WIDGET(sw));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void keep_tabs_icon_free(App& app) {
|
||||
if (app.side_switcher == nullptr || app.side_stack == nullptr) return;
|
||||
hide_tab_icons(app.side_switcher);
|
||||
// A page added later gets a button when the model reports it; this handler is connected *after*
|
||||
// the switcher's own, so the button already exists when it runs.
|
||||
g_signal_connect_after(adw_view_stack_get_pages(ADW_VIEW_STACK(app.side_stack)), "items-changed",
|
||||
G_CALLBACK(on_tab_pages_changed), app.side_switcher);
|
||||
// And once more when the switcher is (re)mapped, which also covers a rebuild of the buttons.
|
||||
g_signal_connect_swapped(app.side_switcher, "map", G_CALLBACK(hide_tab_icons), app.side_switcher);
|
||||
}
|
||||
|
||||
void build_source_panels(App& app) {
|
||||
if (app.side_stack == nullptr || app.acq == nullptr || app.acq->source() == nullptr) return;
|
||||
SourceKind* kind = find_kind(app.source_kind);
|
||||
|
||||
Reference in new issue
Block a user