Skip to content

Commit aab1b49

Browse files
committed
Merge: stable multitag renders
2 parents 162b616 + acc476e commit aab1b49

1 file changed

Lines changed: 32 additions & 10 deletions

File tree

libs/FastSenseCompanion/InspectorPane.m

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
hRangeLbl_ = [] % "Range: last X min (max. 30 min)" label under sparkline
2828
SparkWindowSec_ = 1800 % sparkline horizon — last 30 minutes of data
2929
ThresholdsCache_ = [] % containers.Map(tagKey -> rules cell); built lazily
30+
IsRendering_ = false % re-entrance guard — refreshLive no-ops while true
3031
hTagTable_ = [] % uitable in tag state (live mode updates Data only)
3132
hTagTitle_ = [] % uilabel for the tag-name title (in-place update)
3233
hDashTable_ = [] % uitable in dashboard state (live mode updates Data only)
@@ -115,9 +116,12 @@ function refreshLive(obj)
115116
% Called by the orchestrator's live timer at LivePeriod_.
116117
% - tag state: update uitable Data + sparkline XData/YData
117118
% - dashboard state: update uitable Data
119+
% - multitag state: update each card's sparkline in place
118120
% - other states: no-op
119121
% Falls back to a full renderState_() if the cached handles are
120-
% stale (e.g., setState swapped state but the timer ticked first).
122+
% stale. No-ops while a render is in progress to avoid
123+
% re-entering and accessing half-built widgets.
124+
if obj.IsRendering_; return; end
121125
try
122126
switch obj.State_
123127
case 'tag'
@@ -259,6 +263,11 @@ function updateDashboardInPlace_(obj, db)
259263
end
260264
end
261265

266+
function clearIsRendering_(obj)
267+
%CLEARISRENDERING_ Reset the re-entrance guard (called by onCleanup).
268+
obj.IsRendering_ = false;
269+
end
270+
262271
function alertOrLog_(obj, err)
263272
%ALERTORLOG_ Best-effort error surface that won't crash on invisible figures.
264273
% Use this instead of raw uialert in catch blocks: uialert refuses
@@ -291,21 +300,28 @@ function alertOrLog_(obj, err)
291300

292301
function renderState_(obj)
293302
%RENDERSTATE_ Clear hContent_.Children and dispatch to per-state renderer.
303+
wasInvisible = false;
304+
obj.IsRendering_ = true;
305+
cleanupGuard = onCleanup(@() obj.clearIsRendering_());
294306
try
295307
if isempty(obj.hContent_) || ~isvalid(obj.hContent_); return; end
296-
% Hide hContent_ before deleting children. Without this,
297-
% the delete cascade fires position-update events against
298-
% freshly-stale panel handles; if a DashboardEngine live
299-
% timer happens to drawnow during our delete, it dispatches
300-
% those events on invalid handles ('Value must be a handle'
301-
% errors). Visibility=off suppresses position events for
302-
% the subtree being torn down.
303-
try; obj.hContent_.Visible = 'off'; catch; end
308+
% Hide hContent_ for the ENTIRE delete + build cycle.
309+
% Without this, MATLAB dispatches position-update events
310+
% during widget creation/deletion. A DashboardEngine live
311+
% timer firing drawnow during this dispatches those events
312+
% against half-built or freshly-stale handles, producing
313+
% 'Value must be a handle' / 'Invalid or deleted object'
314+
% errors — most visible on multitag renders that build
315+
% several axes back-to-back.
316+
try
317+
obj.hContent_.Visible = 'off';
318+
wasInvisible = true;
319+
catch
320+
end
304321
kids = obj.hContent_.Children;
305322
for i = numel(kids):-1:1
306323
try; delete(kids(i)); catch; end
307324
end
308-
try; obj.hContent_.Visible = 'on'; catch; end
309325
obj.hSparkAxes_ = []; obj.hSparkPanel_ = []; obj.hSparkLine_ = [];
310326
obj.hRangeLbl_ = [];
311327
obj.hOpenDetail_ = []; obj.hPlayBtn_ = []; obj.hPauseBtn_ = [];
@@ -325,7 +341,13 @@ function renderState_(obj)
325341
error('FastSenseCompanion:invalidState', ...
326342
'Unknown inspector state: ''%s''.', obj.State_);
327343
end
344+
if wasInvisible
345+
try; obj.hContent_.Visible = 'on'; catch; end
346+
end
328347
catch err
348+
if wasInvisible
349+
try; obj.hContent_.Visible = 'on'; catch; end
350+
end
329351
obj.alertOrLog_(err);
330352
end
331353
end

0 commit comments

Comments
 (0)