Skip to content

Commit 74647d2

Browse files
HanSur94claude
andcommitted
fix: detached widgets restore all live references including group children
DetachedMirror.cloneWidget now uses a restoreLiveRefs helper that copies all non-serializable properties (Sensor, DataFcn, Data, Events, PlotFcn, ImageFcn, StatusFcn, ValueFcn, SensorX/Y/Color, Sensors, EventStoreObj) from the original to the clone. For GroupWidget, this is applied recursively to all children and tab widgets, so detaching a collapsible group with TableWidget/TextWidget children now shows the data correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 42b2f97 commit 74647d2

1 file changed

Lines changed: 70 additions & 16 deletions

File tree

libs/Dashboard/DetachedMirror.m

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,25 +176,79 @@ function onFigureClose(obj)
176176
'Unknown widget type: %s', s.type);
177177
end
178178

179-
% Restore live Sensor reference for FastSenseWidget.
180-
% toStruct serializes the sensor by key; fromStruct calls SensorRegistry.get()
181-
% which may return the same live Sensor — but we copy the reference directly
182-
% to guarantee binding even on a registry miss.
183-
if isa(w, 'FastSenseWidget') && ~isempty(original.Sensor)
184-
w.Sensor = original.Sensor;
185-
% Force independent time axis zoom/pan (DETACH-05)
186-
w.UseGlobalTime = false;
187-
end
188-
189-
% Restore function handle references for RawAxesWidget.
190-
% func2str/str2func loses closure-captured variables; copy directly
191-
% (safe here because this is an in-memory clone, not a disk round-trip).
192-
if isa(w, 'RawAxesWidget') && ~isempty(original.PlotFcn)
193-
w.PlotFcn = original.PlotFcn;
194-
w.DataRangeFcn = original.DataRangeFcn;
179+
% Restore live references lost during serialization round-trip
180+
DetachedMirror.restoreLiveRefs(w, original);
181+
182+
% GroupWidget: restore live refs on each child/tab widget
183+
if isa(w, 'GroupWidget')
184+
origChildren = original.Children;
185+
for i = 1:min(numel(w.Children), numel(origChildren))
186+
DetachedMirror.restoreLiveRefs(w.Children{i}, origChildren{i});
187+
end
188+
for ti = 1:min(numel(w.Tabs), numel(original.Tabs))
189+
origTab = original.Tabs{ti};
190+
clonedTab = w.Tabs{ti};
191+
for j = 1:min(numel(clonedTab.widgets), numel(origTab.widgets))
192+
DetachedMirror.restoreLiveRefs(clonedTab.widgets{j}, origTab.widgets{j});
193+
end
194+
end
195195
end
196196
end
197197

198+
function restoreLiveRefs(cloned, original)
199+
%RESTORELIVEREFS Copy non-serializable live references from original to cloned widget.
200+
% Sensor reference (FastSenseWidget, NumberWidget, StatusWidget, GaugeWidget, etc.)
201+
if isprop(cloned, 'Sensor') && ~isempty(original.Sensor)
202+
cloned.Sensor = original.Sensor;
203+
end
204+
if isa(cloned, 'FastSenseWidget') && ~isempty(original.Sensor)
205+
cloned.UseGlobalTime = false;
206+
end
207+
% Function handles (RawAxesWidget, HeatmapWidget, BarChartWidget, ImageWidget, TableWidget)
208+
if isprop(cloned, 'PlotFcn') && ~isempty(original.PlotFcn)
209+
cloned.PlotFcn = original.PlotFcn;
210+
end
211+
if isprop(cloned, 'DataRangeFcn') && ~isempty(original.DataRangeFcn)
212+
cloned.DataRangeFcn = original.DataRangeFcn;
213+
end
214+
if isprop(cloned, 'DataFcn') && ~isempty(original.DataFcn)
215+
cloned.DataFcn = original.DataFcn;
216+
end
217+
if isprop(cloned, 'ImageFcn') && ~isempty(original.ImageFcn)
218+
cloned.ImageFcn = original.ImageFcn;
219+
end
220+
if isprop(cloned, 'StatusFcn') && ~isempty(original.StatusFcn)
221+
cloned.StatusFcn = original.StatusFcn;
222+
end
223+
if isprop(cloned, 'ValueFcn') && ~isempty(original.ValueFcn)
224+
cloned.ValueFcn = original.ValueFcn;
225+
end
226+
% Static data (TableWidget Data, EventTimelineWidget Events)
227+
if isprop(cloned, 'Data') && ~isempty(original.Data)
228+
cloned.Data = original.Data;
229+
end
230+
if isprop(cloned, 'Events') && ~isempty(original.Events)
231+
cloned.Events = original.Events;
232+
end
233+
% Scatter sensor pairs
234+
if isprop(cloned, 'SensorX') && ~isempty(original.SensorX)
235+
cloned.SensorX = original.SensorX;
236+
end
237+
if isprop(cloned, 'SensorY') && ~isempty(original.SensorY)
238+
cloned.SensorY = original.SensorY;
239+
end
240+
if isprop(cloned, 'SensorColor') && ~isempty(original.SensorColor)
241+
cloned.SensorColor = original.SensorColor;
242+
end
243+
% MultiStatus sensors
244+
if isprop(cloned, 'Sensors') && ~isempty(original.Sensors)
245+
cloned.Sensors = original.Sensors;
246+
end
247+
% Histogram sensor
248+
if isprop(cloned, 'EventStoreObj') && ~isempty(original.EventStoreObj)
249+
cloned.EventStoreObj = original.EventStoreObj;
250+
end
251+
end
198252
end
199253

200254
end

0 commit comments

Comments
 (0)