Skip to content

GroupWidget: add getTabNames() / hasTab() / getActiveTab() tab introspection #280

Description

@HanSur94

Problem / motivation

GroupWidget is the dashboard's nested-layout container (panel / collapsible / tabbed — the project's named "tabs, collapsible groups" core value). In tabbed mode it already navigates by nameswitchTab(tabName) (GroupWidget.m:382) and addChild(widget, tabName) (:80) both key off the tab name via the private findTab.

But there is no public read side:

  1. No way to enumerate tabs. To list tab names a caller must reach into the internal Tabs cell-of-struct and know its struct('name',…,'widgets',{{…}}) shape (:41, :84). grep -rnE "getTabNames|getActiveTab|listTabs" libs/ examples/ returns nothing.
  2. No way to validate a tab before switching. switchTab silently no-ops on an unknown tab (:386–389), unlike its siblings removeTab(tabName) and removeChild(idx, tabName) which throw GroupWidget:unknownTab. A caller has no non-throwing pre-check.
  3. Cross-class asymmetry. DashboardEngine pages just received getPageNames() / getActivePageName() introspection (DashboardEngine: name-based page navigation (switchPage by name) + page-name introspection #279). Tabbed GroupWidget is the directly-analogous nested-layout primitive with the same gap — the read side of name-based navigation is missing.

Proposed feature

Add three small read-only introspection methods to GroupWidget, mirroring the page-introspection API from #279:

  • getTabNames() → cellstr of the tabbed group's tab names ({} when not tabbed / no tabs).
  • hasTab(name) → logical; true if a tab with that name exists.
  • getActiveTab() → returns the active tab name (ActiveTab).

Rough sketch

Single file — libs/Dashboard/GroupWidget.m, three methods over the existing private findTab:

function names = getTabNames(obj)
    %GETTABNAMES Cellstr of tab names (tabbed mode); {} otherwise.
    if ~strcmp(obj.Mode, 'tabbed') || isempty(obj.Tabs)
        names = {};
        return;
    end
    names = cellfun(@(t) t.name, obj.Tabs, 'UniformOutput', false);
end

function tf = hasTab(obj, name)
    %HASTAB True if a tab with NAME exists.
    tf = obj.findTab(name) ~= 0;
end

function name = getActiveTab(obj)
    %GETACTIVETAB Name of the active tab ('' when none).
    name = obj.ActiveTab;
end

Lets a "jump to tab" menu, a FastSenseCompanion control, or a structural test assert/enumerate tabs without touching Tabs internals — e.g. for n = g.getTabNames(); ...; end then if g.hasTab(n), g.switchTab(n); end.

Value

Medium. Tabbed groups are an explicitly named core value ("organize … into navigable sections … tabs, collapsible groups"). Enumeration is the primitive that name-based navigation needs on the read side; #279 established the same value for the page analog one cycle ago, so the shape and worth are concrete and consistent.

Constraints check

  • Toolbox-free: yes — plain cellfun / strcmp via existing findTab.
  • Backward-compatible: yes — strictly additive. No existing method changes; switchTab keeps its current silent-no-op behavior (deliberately not touched here). No serialization change (Tabs already round-trips).
  • Pure MATLAB/Octave: yes.
  • Widget contract: yes — read-only methods on the existing GroupWidget (a DashboardWidget); no contract change.

Effort estimate

S — one file, three small read-only methods.

Related note (NOT part of this additive change)

switchTab(tabName) silently no-ops on an unknown tab while its removeTab / removeChild siblings throw GroupWidget:unknownTab. Tightening that to throw would be a behavior change (callers may rely on the silent path), so it is intentionally excluded here — hasTab() gives callers a non-throwing pre-check instead. Route the switchTab consistency question separately as a small bug/decision if desired.


AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions