|
186 | 186 | function close(obj) |
187 | 187 | %CLOSE Idempotent teardown — deletes listeners, owned timers, and the uifigure. |
188 | 188 | % Does NOT affect any DashboardEngine figure or ad-hoc plot figures. |
| 189 | + % |
| 190 | + % Bulletproof: every cleanup step is wrapped in try/catch so a |
| 191 | + % single failure (e.g. a stale listener handle, a pane already |
| 192 | + % half-deleted) cannot prevent the uifigure itself from being |
| 193 | + % deleted. The X-button must always close the window. |
189 | 194 | if ~isvalid(obj) |
190 | 195 | return; |
191 | 196 | end |
192 | 197 | if isempty(obj.hFig_) || ~isvalid(obj.hFig_) |
| 198 | + obj.hFig_ = []; |
| 199 | + obj.IsOpen = false; |
193 | 200 | return; |
194 | 201 | end |
195 | | - % Detach panes (releases their listeners) |
196 | | - if ~isempty(obj.CatalogPane_) && isvalid(obj.CatalogPane_) |
197 | | - obj.CatalogPane_.detach(); |
| 202 | + % Diagnostic — confirms the X click reached close(). |
| 203 | + fprintf('[FastSenseCompanion] close() invoked, tearing down...\n'); |
| 204 | + % Detach panes (releases their listeners + debounce timers). |
| 205 | + try |
| 206 | + if ~isempty(obj.CatalogPane_) && isvalid(obj.CatalogPane_) |
| 207 | + obj.CatalogPane_.detach(); |
| 208 | + end |
| 209 | + catch err |
| 210 | + fprintf(2, '[FastSenseCompanion] CatalogPane.detach failed: %s\n', err.message); |
198 | 211 | end |
199 | | - if ~isempty(obj.ListPane_) && isvalid(obj.ListPane_) |
200 | | - obj.ListPane_.detach(); |
| 212 | + try |
| 213 | + if ~isempty(obj.ListPane_) && isvalid(obj.ListPane_) |
| 214 | + obj.ListPane_.detach(); |
| 215 | + end |
| 216 | + catch err |
| 217 | + fprintf(2, '[FastSenseCompanion] ListPane.detach failed: %s\n', err.message); |
| 218 | + end |
| 219 | + try |
| 220 | + if ~isempty(obj.InspectorPane_) && isvalid(obj.InspectorPane_) |
| 221 | + obj.InspectorPane_.detach(); |
| 222 | + end |
| 223 | + catch err |
| 224 | + fprintf(2, '[FastSenseCompanion] InspectorPane.detach failed: %s\n', err.message); |
201 | 225 | end |
202 | | - if ~isempty(obj.InspectorPane_) && isvalid(obj.InspectorPane_) |
203 | | - obj.InspectorPane_.detach(); |
| 226 | + % Release orchestrator-level listeners. |
| 227 | + try |
| 228 | + delete(obj.Listeners_); |
| 229 | + catch err |
| 230 | + fprintf(2, '[FastSenseCompanion] Listeners delete failed: %s\n', err.message); |
204 | 231 | end |
205 | | - % Release orchestrator-level listeners |
206 | | - delete(obj.Listeners_); |
207 | 232 | obj.Listeners_ = {}; |
208 | | - % No companion-owned timers in Phase 1018 — pattern established for Phases 1019+: |
209 | | - % stop(t); delete(t); (always in this order) |
210 | | - % Close the uifigure |
211 | | - delete(obj.hFig_); |
| 233 | + % Always delete the uifigure last and unconditionally — this is |
| 234 | + % what makes the X click actually close the window. |
| 235 | + try |
| 236 | + delete(obj.hFig_); |
| 237 | + catch err |
| 238 | + fprintf(2, '[FastSenseCompanion] hFig delete failed: %s\n', err.message); |
| 239 | + end |
212 | 240 | obj.hFig_ = []; |
213 | 241 | obj.IsOpen = false; |
| 242 | + fprintf('[FastSenseCompanion] close() complete.\n'); |
214 | 243 | end |
215 | 244 |
|
216 | 245 | function delete(obj) |
|
0 commit comments