Skip to content

Commit 73a3bf1

Browse files
HanSur94claude
andcommitted
Merge origin/main into quick-260519-bs4 (Tag Status Table)
Conflict resolution in libs/FastSenseCompanion/FastSenseCompanion.m: - Properties: kept both new private fields (TagStatusTableWindow_ + hTagStatusBtn_ from this branch; OpenedFigures_ + hTileBtn_ + hCloseAllBtn_ from main PR #143) - Inner toolbar grid: combined 1x5 (ours: Events|Live|Tags|spacer|gear) and 1x6 (main: Events|Live|Tile|Close all|spacer|gear) into a 1x7: Events | Live | Tags | Tile | Close all | spacer | gear. Column widths {110, 110, 110, 70, 90, '1x', 36}. - Button blocks: re-numbered main's Tile/Close all/gear columns 3->4, 4->5, 6->7. Tags ↗ stays at col 3 next to Live. - Test seams: kept all four hidden test getters (tagStatusTableWindowForTest_, scanLiveTagUpdatesForTest_, getOpenedFiguresForTest_, trackOpenedFigureForTest_). Verified post-merge: - test_companion_tag_status_table 24/24 PASS - TestTagStatusTableWindow 16/16 PASS - test_companion_tile_close_buttons 9/9 PASS - TestFastSenseCompanion 64/64 PASS Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 parents 10df740 + bd01d63 commit 73a3bf1

10 files changed

Lines changed: 879 additions & 39 deletions

File tree

.planning/STATE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ milestone: v3.0
44
milestone_name: FastSense Companion
55
status: shipped
66
last_updated: "2026-05-12T09:20:00.000Z"
7-
last_activity: 2026-05-13 - Completed quick task 260513-sfp: Added auto-y-limit V/A/L buttons to WidgetButtonBar with backward-compatible default. Verified on live industrial-plant demo.
7+
last_activity: 2026-05-14 -- Quick task 260513-s0y shipped (PR #143): Tile + Close all buttons on FastSenseCompanion top toolbar with 3 tracking fixes (sync from Engines_, public trackOpenedFigure hook, de-maximize + Units=pixels coercion). Verified live on industrial-plant demo. 9/9 sub-tests + 64/64 regression PASS.
88
progress:
99
total_phases: 6
1010
completed_phases: 2
@@ -20,7 +20,7 @@ Phase: 1028
2020
Plan: Not started
2121
Milestone: v3.0 FastSense Companion — SHIPPED 2026-04-30
2222
Status: Awaiting next milestone (run `/gsd:new-milestone` to scope v3.x or v4.0)
23-
Last activity: 2026-05-13 - Completed quick task 260513-sfp: Added auto-y-limit V/A/L buttons to WidgetButtonBar with backward-compatible default. Verified on live industrial-plant demo.
23+
Last activity: 2026-05-14 - Quick task 260513-s0y shipped as PR #143 (commits 182d6f1, 2867caa, 1be2cc8, e58bc35, c47c0c1, db9ef88). FastSenseCompanion: Tile + Close all toolbar buttons. Three tracking fixes uncovered by live verification: (1) syncOpenedFigures_ walks Engines_ — DashboardListPane fires OpenDashboardRequested BEFORE engine.render(), and run_demo's pre-rendered demo dashboard never goes through that event flow; (2) public trackOpenedFigure hook on the companion called from InspectorPane.onOpenDetail_ + CompanionEventViewer.openEventDashboard_ which spawn figures directly bypassing the event flow; (3) de-maximize + Units=pixels coercion before set(Position) — DashboardEngine.render defaults to Units=normalized so pixel rectangles got treated as screen fractions, pushing figures off-canvas (root cause of "Tile button does nothing" report). 9/9 sub-tests + 64/64 regression PASS. Verified on live industrial-plant demo.
2424

2525
### Quick Tasks Completed
2626

@@ -65,6 +65,7 @@ Last activity: 2026-05-13 - Completed quick task 260513-sfp: Added auto-y-limit
6565
| 260513-ovt | Preserve widget X and Y views across Live ticks + Follow toggle reaches every page — (1) added LiveViewMode='follow' guard inside FastSenseWidget.autoScaleY_, (2) removed `autoScaleY_(y)` from FastSenseWidget.refresh/update, (3) removed `broadcastTimeRange(tStart, tEnd)` from DashboardEngine.onLiveTick, (4) flipped FastSenseWidget.LiveViewMode default 'reset'→'preserve', (5) made FastSenseToolbar.syncFollowState public so FastSense.onXLimChanged's auto-disengage hook actually syncs the Follow button, (6) made DashboardEngine.{allPageWidgets,activePageWidgets} public + onFollowToggle uses allPageWidgets() so Follow actually flips every FastSenseWidget across all pages on multi-page dashboards (was silently no-op via swallowed MethodRestricted). Live mode is now strictly "append data only"; Follow does width-preserving slide with 2% right-edge gap. test_fastsense_follow_toggle 10/10, test_dashboard_time_sync_all_pages 5/5, test_dashboard_range_selector_integration 2/2; verified end-to-end on industrial plant demo (Follow ON: XLim+0.140d toward tail, width preserved exactly, 2/2 widgets switched; OFF: 2/2 reverted) | 2026-05-13 | 498a5f3, ca5be95, 8d41c48, 63cdff4 | — | [260513-ovt-when-follow-button-is-pressed-y-axis-lim](./quick/260513-ovt-when-follow-button-is-pressed-y-axis-lim/) |
6666
| 260513-q7w | Debounced post-resize refresh + ZOMBIE-PANEL fix that stops widgets going white during drag-resize and tab switching — TWO parallel timers on every figure resize event (300 ms cheap two-pass refresh + 1.2 s unconditional rerenderWidgets backstop). switchPage cancels both timers AND waits up to 3 s for in-flight rerenderWidgets to complete before mutating state. `IsRerendering_` flag prevents rerender-cascade scheduling. Re-entrancy guard aborts instead of self-rescheduling. **Root-cause fix**: rerenderWidgets now deletes the OUTER cell panel (via hCellPanel, falling back to hPanel for pre-realization widgets) — previous code deleted only `hPanel` which after realization points to the INNER content panel, leaving the outer cell + its WidgetButtonBar chrome alive on the canvas as "zombies" that stacked up over multiple rerenders and painted over freshly switched-to pages. test_dashboard_range_selector_integration 2/2, test_dashboard_time_sync_all_pages 5/5; canvas-children-count canary verifies zero zombie accumulation across 4 rerenders + resize + tab switch (constant 29) | 2026-05-13 | 577bf95, 99c8808, 4eda604, bc305dc, 54d5aa0, 20bcd4c | — | [260513-q7w-during-dashboard-figure-resize-fastsense](./quick/260513-q7w-during-dashboard-figure-resize-fastsense/) |
6767
| 260513-sfp | Add auto-y-limit control buttons (V/A/L) to FastSenseWidget WidgetButtonBar — new YLimitMode property (auto-visible / auto-all / locked, default 'auto-visible' reproduces pre-260513-sfp behaviour), setYLimitMode public method (clears UserZoomedY on explicit click so click re-engages autoscale), autoScaleY_ refactored to dispatch on mode AFTER existing precedence guards (YLimits pin / UserZoomedY / FastSense.LiveViewMode=='follow') so 260513-ovt Follow semantics are preserved. DashboardLayout duck-types widget chrome via ismethod(widget,'setYLimitMode'), so future widgets that expose Y-rescale modes opt in without touching DashboardLayout. ASCII glyphs (V/A/L) match existing Info/Detach. reflowChrome_ re-anchors on resize. toStruct omits the default so legacy dashboards stay diff-invisible. test_fastsense_widget_ylimit_modes 11/11, test_fastsense_widget_tag 7/7, test_fastsense_follow_toggle 10/10, test_dashboard_time_sync_all_pages 5/5. Verified on live industrial-plant demo, all 8 scenarios approved. Known caveat: V/A/L cluster butts against Info button (0-px gap) — inherited from pre-existing addInfoIcon 28-px-typo, explicitly out-of-scope per plan; logged in deferred-items.md | 2026-05-13 | 4db9138, cc18c7f, a9cc181 | Verified | [260513-sfp-add-auto-y-limit-control-buttons-to-fast](./quick/260513-sfp-add-auto-y-limit-control-buttons-to-fast/) |
68+
| 260513-s0y | Add Tile + Close all buttons to FastSenseCompanion top toolbar — private OpenedFigures_ tracking + syncOpenedFigures_ (walks Engines_ before tile/close-all) + public trackOpenedFigure hook (InspectorPane.onOpenDetail_ and CompanionEventViewer.openEventDashboard_ forward their figure handles). tileOpenedWindows: ceil(sqrt(N))×ceil(N/cols) grid on monitor containing the companion, 24px margin, 8px gutter, row-major top-down. Before set(Position), coerces each figure to WindowState='normal' + Units='pixels' — root cause of initial "Tile does nothing" report was DashboardEngine.render defaulting to Units='normalized' (pixel rects got treated as screen fractions, pushing figures off-canvas). closeAllOpenedWindows: snapshot + close(h) per handle (honors each figure's CloseRequestFcn). Inner toolbar grid 1×4→1×6 (Events / Live / Tile / Close all / spacer / gear; gear Layout.Column 4→6). 9 sub-tests in test_companion_tile_close_buttons.m PASS; TestFastSenseCompanion regression 64/64 PASS. Verified on live industrial-plant demo. Shipped as PR #143. | 2026-05-14 | 182d6f1, 2867caa, 1be2cc8, e58bc35, c47c0c1, db9ef88 | Shipped (PR #143) | [260513-s0y-add-tile-windows-and-close-all-windows-b](./quick/260513-s0y-add-tile-windows-and-close-all-windows-b/) |
6869

6970
## Progress Bar
7071

libs/Dashboard/DashboardEngine.m

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,10 +2016,13 @@ function refreshActivePageWidgetsAfterResize_(obj)
20162016
return;
20172017
end
20182018
ws = obj.activePageWidgets();
2019+
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
20192020
% --- Pass 1: cheap data re-push ---
20202021
for i = 1:numel(ws)
20212022
w = ws{i};
2022-
if isempty(w) || ~isvalid(w)
2023+
% Octave 7+ has no isvalid() for classdef handles, so treat
2024+
% as valid there and rely on downstream guards / try-catch.
2025+
if isempty(w) || (~isOctave && ~isvalid(w))
20232026
continue;
20242027
end
20252028
if ~w.Realized || isempty(w.hPanel) || ~ishandle(w.hPanel)
@@ -2042,10 +2045,10 @@ function refreshActivePageWidgetsAfterResize_(obj)
20422045
stillWhite = false;
20432046
for i = 1:numel(ws)
20442047
w = ws{i};
2045-
if isempty(w) || ~isvalid(w) || ~isa(w, 'FastSenseWidget')
2048+
if isempty(w) || (~isOctave && ~isvalid(w)) || ~isa(w, 'FastSenseWidget')
20462049
continue;
20472050
end
2048-
if isempty(w.FastSenseObj) || ~isvalid(w.FastSenseObj) || ~w.FastSenseObj.IsRendered
2051+
if isempty(w.FastSenseObj) || (~isOctave && ~isvalid(w.FastSenseObj)) || ~w.FastSenseObj.IsRendered
20492052
continue;
20502053
end
20512054
if ~obj.isWidgetLineWhite_(w)

libs/Dashboard/DashboardLayout.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,10 @@ function syncYLimitButtonsState_(bar, mode)
10351035
inactiveBg = [];
10361036
if isfield(ud, 'YLimitWidget') && ~isempty(ud.YLimitWidget)
10371037
w = ud.YLimitWidget;
1038-
if isobject(w) && isvalid(w) && ...
1038+
% Octave 7+ has no isvalid() for classdef handles; treat as
1039+
% valid there and rely on the property-access guards below.
1040+
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
1041+
if isobject(w) && (isOctave || isvalid(w)) && ...
10391042
~isempty(w.ParentTheme) && isstruct(w.ParentTheme) && ...
10401043
isfield(w.ParentTheme, 'ToolbarBackground')
10411044
inactiveBg = w.ParentTheme.ToolbarBackground;

libs/Dashboard/FastSenseWidget.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,11 @@ function autoScaleY_(obj, y)
450450
if obj.UserZoomedY
451451
return;
452452
end
453-
if ~isempty(obj.FastSenseObj) && isvalid(obj.FastSenseObj) && ...
453+
% Octave 7+ has no isvalid() for classdef handles, so treat the
454+
% FastSense handle as valid there and let downstream property
455+
% access surface real failures.
456+
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
457+
if ~isempty(obj.FastSenseObj) && (isOctave || isvalid(obj.FastSenseObj)) && ...
454458
strcmp(obj.FastSenseObj.LiveViewMode, 'follow')
455459
return;
456460
end

0 commit comments

Comments
 (0)