Skip to content

Commit f390828

Browse files
committed
feat: route standalone structeditor to modern app
1 parent 8046bd3 commit f390828

14 files changed

Lines changed: 728 additions & 63 deletions

File tree

‎code/+nansen/+manage/OptionsManager.m‎

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -384,12 +384,22 @@ function setOptions(obj, optionsName, options)
384384

385385
sEditor = structeditor(obj.Options, 'OptionsManager', obj, 'Title', name);
386386
sEditor.waitfor()
387-
wasAborted = sEditor.wasCanceled;
388-
389-
if wasAborted
390-
obj.Options = sEditor.dataOrig;
387+
388+
if isprop(sEditor, 'FinishState')
389+
wasAborted = sEditor.FinishState ~= "Finished";
390+
if wasAborted
391+
obj.Options = sEditor.OriginalData;
392+
else
393+
obj.Options = sEditor.Data;
394+
end
391395
else
392-
obj.Options = sEditor.dataEdit;
396+
wasAborted = sEditor.wasCanceled;
397+
398+
if wasAborted
399+
obj.Options = sEditor.dataOrig;
400+
else
401+
obj.Options = sEditor.dataEdit;
402+
end
393403
end
394404

395405
delete(sEditor)
@@ -420,8 +430,13 @@ function setOptions(obj, optionsName, options)
420430
'OptionsManager', obj, ...
421431
'Title', titleStr, ...
422432
'Prompt', promptStr );
423-
424-
hOptionsEditor.changeOptionsSelectionDropdownValue(optionsName);
433+
434+
if ismethod(hOptionsEditor, 'changeOptionsSelectionDropdownValue')
435+
hOptionsEditor.changeOptionsSelectionDropdownValue(optionsName);
436+
else
437+
plugin = hOptionsEditor.getPlugin("nansen.manage.OptionsManagerPlugin");
438+
plugin.setCurrentOptionsName(optionsName);
439+
end
425440

426441
end
427442

@@ -449,14 +464,25 @@ function setOptions(obj, optionsName, options)
449464

450465
sEditor = obj.openOptionsEditor(optsName, optsStruct);
451466
sEditor.waitfor()
452-
453-
wasAborted = sEditor.wasCanceled;
454467

455-
if sEditor.wasCanceled
456-
optsStruct = sEditor.dataOrig;
468+
if isprop(sEditor, 'FinishState')
469+
wasAborted = sEditor.FinishState ~= "Finished";
470+
plugin = sEditor.getPlugin("nansen.manage.OptionsManagerPlugin");
471+
if wasAborted
472+
optsStruct = sEditor.OriginalData;
473+
else
474+
optsStruct = sEditor.Data;
475+
optsName = plugin.CurrentOptionsName;
476+
end
457477
else
458-
optsStruct = sEditor.dataEdit;
459-
optsName = sEditor.currentOptionsName;
478+
wasAborted = sEditor.wasCanceled;
479+
480+
if sEditor.wasCanceled
481+
optsStruct = sEditor.dataOrig;
482+
else
483+
optsStruct = sEditor.dataEdit;
484+
optsName = sEditor.currentOptionsName;
485+
end
460486
end
461487

462488
% Clear modified options!
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
classdef OptionsManagerPlugin < handle
2+
3+
properties (SetAccess = private)
4+
OptionsManager
5+
CurrentOptionsName char = ''
6+
end
7+
8+
properties (Access = private)
9+
Editor
10+
HeaderGrid matlab.ui.container.GridLayout
11+
OptionsDropDown matlab.ui.control.DropDown
12+
SaveButton matlab.ui.control.Button
13+
DefaultButton matlab.ui.control.Button
14+
IsUpdating (1,1) logical = false
15+
end
16+
17+
methods
18+
function obj = OptionsManagerPlugin(optionsManager, optionsName)
19+
arguments
20+
optionsManager
21+
optionsName char = ''
22+
end
23+
24+
obj.OptionsManager = optionsManager;
25+
if isempty(optionsName)
26+
optionsName = optionsManager.OptionsName;
27+
end
28+
if isempty(optionsName)
29+
optionsName = optionsManager.getPreferredOptionsName();
30+
end
31+
obj.CurrentOptionsName = char(optionsName);
32+
end
33+
34+
function attach(obj, editor)
35+
obj.Editor = editor;
36+
parent = editor.getAttachmentContainer("header");
37+
38+
obj.HeaderGrid = uigridlayout(parent);
39+
obj.HeaderGrid.ColumnWidth = {'1x', 82, 96};
40+
obj.HeaderGrid.RowHeight = {'1x'};
41+
obj.HeaderGrid.Padding = [0 0 0 0];
42+
obj.HeaderGrid.ColumnSpacing = 6;
43+
44+
obj.OptionsDropDown = uidropdown(obj.HeaderGrid);
45+
obj.OptionsDropDown.Layout.Row = 1;
46+
obj.OptionsDropDown.Layout.Column = 1;
47+
obj.OptionsDropDown.ValueChangedFcn = @obj.onOptionsSetChanged;
48+
49+
obj.SaveButton = uibutton(obj.HeaderGrid, ...
50+
"Text", "Save Options", ...
51+
"ButtonPushedFcn", @obj.onSaveOptionsPushed);
52+
obj.SaveButton.Layout.Row = 1;
53+
obj.SaveButton.Layout.Column = 2;
54+
55+
obj.DefaultButton = uibutton(obj.HeaderGrid, ...
56+
"Text", "Make Default", ...
57+
"ButtonPushedFcn", @obj.onMakeDefaultPushed);
58+
obj.DefaultButton.Layout.Row = 1;
59+
obj.DefaultButton.Layout.Column = 3;
60+
61+
obj.refreshOptionsDropdown();
62+
end
63+
64+
function setCurrentOptionsName(obj, optionsName)
65+
obj.CurrentOptionsName = char(optionsName);
66+
obj.refreshOptionsDropdown();
67+
end
68+
69+
function onDataChanged(obj, ~, ~)
70+
if obj.IsUpdating || isempty(obj.OptionsDropDown)
71+
return
72+
end
73+
74+
opts = obj.Editor.Data;
75+
currentName = obj.CurrentOptionsName;
76+
77+
if contains(currentName, 'Modified')
78+
obj.OptionsManager.appendModifiedOptions(opts, currentName)
79+
else
80+
obj.CurrentOptionsName = sprintf('%s (Modified)', currentName);
81+
obj.OptionsManager.appendModifiedOptions(opts, obj.CurrentOptionsName)
82+
end
83+
84+
obj.refreshOptionsDropdown();
85+
end
86+
87+
function onFinishStateChanged(~, ~, ~)
88+
% Hook kept for the generic plugin lifecycle. OptionsManager
89+
% state is read by callers after editor.waitfor returns.
90+
end
91+
92+
function control = getTestControl(obj, controlName)
93+
control = obj.(controlName);
94+
end
95+
end
96+
97+
methods (Access = private)
98+
function onOptionsSetChanged(obj, src, ~)
99+
newName = char(src.Value);
100+
oldName = obj.CurrentOptionsName;
101+
102+
if strcmp(newName, oldName)
103+
return
104+
end
105+
106+
if contains(oldName, 'Modified')
107+
obj.OptionsManager.appendModifiedOptions(obj.Editor.Data, oldName)
108+
end
109+
110+
if obj.OptionsManager.isModified(newName)
111+
newOptions = obj.OptionsManager.getModifiedOptions(newName);
112+
else
113+
newOptions = obj.OptionsManager.getOptions(newName);
114+
end
115+
116+
obj.IsUpdating = true;
117+
cleanupObj = onCleanup(@() obj.clearUpdatingFlag());
118+
obj.Editor.replaceData(newOptions);
119+
obj.CurrentOptionsName = newName;
120+
obj.refreshOptionsDropdown();
121+
delete(cleanupObj)
122+
end
123+
124+
function onSaveOptionsPushed(obj, ~, ~)
125+
currentName = obj.CurrentOptionsName;
126+
givenName = obj.OptionsManager.saveCustomOptions(obj.Editor.Data);
127+
if isempty(givenName)
128+
return
129+
end
130+
131+
if contains(currentName, 'Modified')
132+
obj.OptionsManager.removeModifiedOptions(currentName)
133+
end
134+
135+
obj.CurrentOptionsName = givenName;
136+
obj.refreshOptionsDropdown();
137+
end
138+
139+
function onMakeDefaultPushed(obj, ~, ~)
140+
if isempty(obj.CurrentOptionsName) || contains(obj.CurrentOptionsName, 'Modified')
141+
return
142+
end
143+
144+
obj.OptionsManager.setDefault(obj.CurrentOptionsName);
145+
obj.refreshOptionsDropdown();
146+
end
147+
148+
function refreshOptionsDropdown(obj)
149+
if isempty(obj.OptionsDropDown) || ~isvalid(obj.OptionsDropDown)
150+
return
151+
end
152+
153+
[displayNames, optionNames] = obj.getDropdownNames();
154+
obj.OptionsDropDown.Items = displayNames;
155+
obj.OptionsDropDown.ItemsData = optionNames;
156+
157+
if any(strcmp(optionNames, obj.CurrentOptionsName))
158+
obj.OptionsDropDown.Value = obj.CurrentOptionsName;
159+
elseif ~isempty(optionNames)
160+
obj.CurrentOptionsName = optionNames{1};
161+
obj.OptionsDropDown.Value = obj.CurrentOptionsName;
162+
end
163+
164+
obj.updateButtonStates();
165+
end
166+
167+
function [displayNames, optionNames] = getDropdownNames(obj)
168+
presetNames = obj.OptionsManager.PresetOptionNames;
169+
customNames = obj.OptionsManager.CustomOptionNames;
170+
editedNames = obj.OptionsManager.EditedOptionNames;
171+
optionNames = [presetNames, customNames, editedNames];
172+
173+
displayNames = [ ...
174+
obj.OptionsManager.formatPresetNames(presetNames), ...
175+
customNames, ...
176+
obj.OptionsManager.formatEditedNames(editedNames)];
177+
178+
defaultName = obj.OptionsManager.getPreferredOptionsName();
179+
isDefault = strcmp(optionNames, defaultName);
180+
displayNames(isDefault) = obj.OptionsManager.formatDefaultName(displayNames(isDefault));
181+
end
182+
183+
function updateButtonStates(obj)
184+
if isempty(obj.SaveButton) || ~isvalid(obj.SaveButton)
185+
return
186+
end
187+
188+
isModified = contains(obj.CurrentOptionsName, 'Modified');
189+
obj.SaveButton.Enable = obj.onOff(isModified);
190+
191+
defaultName = obj.OptionsManager.getPreferredOptionsName();
192+
canMakeDefault = ~isempty(obj.CurrentOptionsName) ...
193+
&& ~isModified ...
194+
&& ~strcmp(obj.CurrentOptionsName, defaultName);
195+
obj.DefaultButton.Enable = obj.onOff(canMakeDefault);
196+
end
197+
198+
function clearUpdatingFlag(obj)
199+
obj.IsUpdating = false;
200+
end
201+
end
202+
203+
methods (Static, Access = private)
204+
function value = onOff(tf)
205+
if tf
206+
value = 'on';
207+
else
208+
value = 'off';
209+
end
210+
end
211+
end
212+
end

‎code/apps/+imviewer/+plugin/@RoiSignalVideo/RoiSignalVideo.m‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,22 @@
6666
obj.loadSettings()
6767
obj.imviewerRef.displayMessage('Preparing video export')
6868

69-
sEditor = structeditor.App(obj.settings, 'Title', 'Set preferences for video export');
69+
sEditor = structeditor(obj.settings, 'Title', 'Set preferences for video export');
7070
sEditor.waitfor()
7171

72-
if sEditor.wasCanceled
72+
if isprop(sEditor, 'FinishState')
73+
wasCanceled = sEditor.FinishState ~= "Finished";
74+
editedSettings = sEditor.Data;
75+
else
76+
wasCanceled = sEditor.wasCanceled;
77+
editedSettings = sEditor.dataEdit;
78+
end
79+
80+
if wasCanceled
7381
obj.imviewerRef.clearMessage
7482
return
7583
else
76-
obj.settings = sEditor.dataEdit;
84+
obj.settings = editedSettings;
7785
obj.saveSettings()
7886
end
7987

‎code/apps/+imviewer/@App/App.m‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4338,18 +4338,26 @@ function uiEditStackMetadata(obj)
43384338
S.PhysicalUnits = obj.ImageStack.MetaData.getPhysicalUnits();
43394339
S.SampleRate = obj.ImageStack.getSampleRate();
43404340

4341-
h = structeditor.App(S, 'Title', 'ImageStack Properties', ...
4341+
h = structeditor(S, 'Title', 'ImageStack Properties', ...
43424342
'Prompt', 'Set ImageStack Properties:');
43434343
h.waitfor()
43444344

4345-
if ~h.wasCanceled
4346-
obj.ImageStack.MetaData.SampleRate = h.dataEdit.SampleRate;
4347-
obj.ImageStack.MetaData.SpatialPosition = h.dataEdit.SpatialPosition;
4348-
physSize = h.dataEdit.PhysicalSize;
4349-
if numel(h.dataEdit.PhysicalUnits) == 1
4350-
physUnits = strsplit(h.dataEdit.PhysicalUnits{1}, ', ');
4345+
if isprop(h, 'FinishState')
4346+
if h.FinishState ~= "Finished"; return; end
4347+
editedData = h.Data;
4348+
else
4349+
if h.wasCanceled; return; end
4350+
editedData = h.dataEdit;
4351+
end
4352+
4353+
if ~isempty(editedData)
4354+
obj.ImageStack.MetaData.SampleRate = editedData.SampleRate;
4355+
obj.ImageStack.MetaData.SpatialPosition = editedData.SpatialPosition;
4356+
physSize = editedData.PhysicalSize;
4357+
if numel(editedData.PhysicalUnits) == 1
4358+
physUnits = strsplit(editedData.PhysicalUnits{1}, ', ');
43514359
else
4352-
physUnits = h.dataEdit.PhysicalUnits;
4360+
physUnits = editedData.PhysicalUnits;
43534361
end
43544362
obj.ImageStack.MetaData.PhysicalSizeX = physSize(1);
43554363
obj.ImageStack.MetaData.PhysicalSizeY = physSize(2);

0 commit comments

Comments
 (0)