bm-core-ui 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1624 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/build/@types/index.d.ts +14078 -0
- package/build/BMCodeEditor/BMCodeEditor.js +707 -0
- package/build/BMCollectionView/BMCollectionView.js +5861 -0
- package/build/BMCollectionView/BMCollectionViewCell.js +688 -0
- package/build/BMCollectionView/BMCollectionViewFlowLayout.js +4467 -0
- package/build/BMCollectionView/BMCollectionViewLayout.js +830 -0
- package/build/BMCollectionView/BMCollectionViewLayoutAttributes.js +673 -0
- package/build/BMCollectionView/BMCollectionViewMasonryLayout.js +491 -0
- package/build/BMCollectionView/BMCollectionViewStackLayout.js +634 -0
- package/build/BMCollectionView/BMCollectionViewTileLayout.js +1121 -0
- package/build/BMCoreUI.css +2833 -0
- package/build/BMView/BMAttributedLabelView.js +310 -0
- package/build/BMView/BMLayoutConstraint_v2.5.js +1813 -0
- package/build/BMView/BMLayoutGuide.js +192 -0
- package/build/BMView/BMLayoutSizeClass.js +466 -0
- package/build/BMView/BMMenu.js +574 -0
- package/build/BMView/BMScrollView.js +247 -0
- package/build/BMView/BMTextField.js +512 -0
- package/build/BMView/BMTextFieldDelegate.js +71 -0
- package/build/BMView/BMView_v2.5.js +3572 -0
- package/build/BMView/BMViewport.js +221 -0
- package/build/BMViewLayoutEditor/BMLayoutEditor.js +6117 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorConstraintSettings.js +417 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorDelegate.js +59 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingCells.js +1653 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettings.js +1480 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsComplexCells.js +431 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorSettingsDelegate.js +46 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorVariablesController.js +460 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewGroupSettings.js +293 -0
- package/build/BMViewLayoutEditor/BMLayoutEditorViewSettings.js +382 -0
- package/build/BMViewLayoutEditor/BMLayoutVariableProvider.js +202 -0
- package/build/BMWindow/BMConfirmationPopup.js +477 -0
- package/build/BMWindow/BMKeyboardShortcut.js +151 -0
- package/build/BMWindow/BMPopover/BMPopover.js +492 -0
- package/build/BMWindow/BMToolWindow.js +71 -0
- package/build/BMWindow/BMWindow.js +2000 -0
- package/build/Core/BMAnimationContext.js +1181 -0
- package/build/Core/BMColor.js +991 -0
- package/build/Core/BMCoreUI.js +470 -0
- package/build/Core/BMFunctionCollection.js +110 -0
- package/build/Core/BMIndexPath.js +165 -0
- package/build/Core/BMInset.js +138 -0
- package/build/Core/BMKeyPath.js +100 -0
- package/build/Core/BMPoint.js +291 -0
- package/build/Core/BMRect.js +556 -0
- package/build/Core/BMSize.js +137 -0
- package/build/iScroll/LICENSE +22 -0
- package/build/iScroll/iscroll-probe.js +2154 -0
- package/build/images/AlignBottom.png +0 -0
- package/build/images/AlignCenterX.png +0 -0
- package/build/images/AlignCenterY.png +0 -0
- package/build/images/AlignLeading.png +0 -0
- package/build/images/AlignTop.png +0 -0
- package/build/images/AlignTrailing.png +0 -0
- package/build/images/AllConstraints.png +0 -0
- package/build/images/BottomConstraint.png +0 -0
- package/build/images/CenterXConstraint.png +0 -0
- package/build/images/CenterYConstraint.png +0 -0
- package/build/images/CoreUI2.png +0 -0
- package/build/images/CoreUI2@2x.png +0 -0
- package/build/images/Desktop.png +0 -0
- package/build/images/DesktopMini.png +0 -0
- package/build/images/EqualHeight.png +0 -0
- package/build/images/EqualHorizontalSpacing.png +0 -0
- package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
- package/build/images/EqualVerticalSpacing.png +0 -0
- package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
- package/build/images/EqualWidth.png +0 -0
- package/build/images/HeightConstraint.png +0 -0
- package/build/images/InactiveConstraints.png +0 -0
- package/build/images/Layout.png +0 -0
- package/build/images/LayoutVariables.png +0 -0
- package/build/images/LeftConstraint.png +0 -0
- package/build/images/OwnConstraints.png +0 -0
- package/build/images/Phone.png +0 -0
- package/build/images/PhoneLandscape.png +0 -0
- package/build/images/PhoneLandscapeMini.png +0 -0
- package/build/images/PhoneMini.png +0 -0
- package/build/images/PhonePortrait.png +0 -0
- package/build/images/PhonePortraitMini.png +0 -0
- package/build/images/Properties.png +0 -0
- package/build/images/RightConstraint.png +0 -0
- package/build/images/SubviewConstraints.png +0 -0
- package/build/images/Tablet.png +0 -0
- package/build/images/TabletLandscape.png +0 -0
- package/build/images/TabletLandscapeMini.png +0 -0
- package/build/images/TabletMini.png +0 -0
- package/build/images/TabletPortrait.png +0 -0
- package/build/images/TabletPortraitMini.png +0 -0
- package/build/images/TopConstraint.png +0 -0
- package/build/images/WidthConstraint.png +0 -0
- package/build/index.js +40 -0
- package/lib/@types/BMCoreUI.min.d.ts +14078 -0
- package/lib/BMCoreUI.min.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1480 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {YES, NO, BMExtend} from '../Core/BMCoreUI'
|
|
4
|
+
import {BMPointMake} from '../Core/BMPoint'
|
|
5
|
+
import {BMSizeMake} from '../Core/BMSize'
|
|
6
|
+
import {BMIndexPathMakeWithRow} from '../Core/BMIndexPath'
|
|
7
|
+
import {__BMVelocityAnimate, BMAnimationContextGetCurrent, BMHook, BMAnimationBeginWithDuration, BMAnimationApply, BMAnimationContextBeginStatic} from '../Core/BMAnimationContext'
|
|
8
|
+
import {BMView} from '../BMView/BMView_v2.5'
|
|
9
|
+
import {BMCollectionViewFlowLayoutSupplementaryView, BMCollectionViewFlowLayoutGravity, BMCollectionViewFlowLayoutAlignment} from '../BMCollectionView/BMCollectionViewFlowLayout'
|
|
10
|
+
import {BMCollectionView} from '../BMCollectionView/BMCollectionView'
|
|
11
|
+
import { BMLayoutEditorSettingsCell, BMLayoutEditorSettingsConstraintCell, BMLayoutEditorSettingsFooter, BMLayoutEditorSettingsTitleCell, BMLayoutEditorSettingsIntegerCell, BMLayoutEditorSettingsReadonlyCell, BMLayoutEditorSettingsDeactivateConstraintsCell, BMLayoutEditorSettingsSegmentCell, BMLayoutEditorSettingsBooleanCell, BMLayoutEditorSettingsStringCell, BMLayoutEditorSettingsNumberCell, BMLayoutEditorSettingsViewCell, BMLayoutEditorSettingsDropdownCell, BMLayoutEditorSettingsConstantCell, BMLayoutEditorSettingsDeleteConstraintCell } from './BMLayoutEditorSettingCells'
|
|
12
|
+
import { _BMLayoutEditorViewLayoutSettingsTab, _BMLayoutEditorViewSettingsPanel } from './BMLayoutEditorViewSettings'
|
|
13
|
+
import { _BMLayoutEditorConstraintSettingsPanel } from './BMLayoutEditorConstraintSettings'
|
|
14
|
+
import { _BMLayoutEditorViewGroupSettingsPanel } from './BMLayoutEditorViewGroupSettings'
|
|
15
|
+
import { BMCollectionViewCell } from '../BMCollectionView/BMCollectionViewCell'
|
|
16
|
+
import { BMLayoutEditorSettingsInsetCell, BMLayoutEditorSettingsSizeCell } from './BMLayoutEditorSettingsComplexCells'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns the URL to the given image based on whether CoreUI is running within thingworx or standalone.
|
|
20
|
+
* @path <String> The image path.
|
|
21
|
+
* @return <String> The appropriate URL.
|
|
22
|
+
*/
|
|
23
|
+
export function _BMURLOfImageAtPath(path) {
|
|
24
|
+
return (('TW' in window) ? '/Thingworx/Common/extensions/BMCoreUI/ui/BMCoreUI/' : '') + path;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// @type _BMLayoutEditorSettingsView
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The layout editor settings view manages the settings sidebar of the layout editor. It is responsible for creating the various
|
|
31
|
+
* settings panels that display the actual settings and maintaining a back stack used for navigation when selecting between the
|
|
32
|
+
* various options displayed by those panels.
|
|
33
|
+
*/
|
|
34
|
+
export function _BMLayoutEditorSettingsView() {} // <constructor>
|
|
35
|
+
|
|
36
|
+
_BMLayoutEditorSettingsView.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
37
|
+
constructor: _BMLayoutEditorSettingsView,
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The layout editor managing this view.
|
|
41
|
+
*/
|
|
42
|
+
_editor: undefined, // <BMLayoutEditor>
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* An array of settings panels that are currently in the setting pane's backstack.
|
|
46
|
+
*/
|
|
47
|
+
_panels: undefined, // <[_BMLayoutEditorSettingsPanel]>
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* When this settings view is displayed in a tool window, this property contains a reference
|
|
51
|
+
* to that window.
|
|
52
|
+
*/
|
|
53
|
+
_window: undefined, // <BMWindow, nullable>
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Set to `YES` after the user dismisses the settings window via the close button.
|
|
57
|
+
* After it is set to `YES`, the window will no longer open after selecting a view.
|
|
58
|
+
*/
|
|
59
|
+
_dismissedByUser: NO, // <Boolean>
|
|
60
|
+
|
|
61
|
+
get window() {
|
|
62
|
+
return this._window;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The currently displayed settings panel.
|
|
67
|
+
*/
|
|
68
|
+
get currentPanel() { // <_BMLayoutEditorSettingsPanel, nullable>
|
|
69
|
+
return this._panels[this._panels.length - 1];
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Initializes this settings view with the given DOM node.
|
|
74
|
+
* @param node <DOMNode> The DOM node to be used by the settings view.
|
|
75
|
+
* {
|
|
76
|
+
* @param editor <BMLayoutEditor> The layout editor managing this view.
|
|
77
|
+
* }
|
|
78
|
+
* @return <_BMLayoutEditorSettingsView> This settings view.
|
|
79
|
+
*/
|
|
80
|
+
initWithDOMNode(node, {forEditor: editor}) {
|
|
81
|
+
BMView.prototype.initWithDOMNode.call(this, node);
|
|
82
|
+
|
|
83
|
+
this.node.style.overflow = 'hidden';
|
|
84
|
+
this._editor = editor;
|
|
85
|
+
this._panels = [];
|
|
86
|
+
|
|
87
|
+
return this;
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// @override - BMWindowDelegate
|
|
91
|
+
async windowWillAppear(window) {
|
|
92
|
+
if (this._panels.length && this._panels[this._panels.length - 1]._awaitsLayout) {
|
|
93
|
+
await 0;
|
|
94
|
+
const panel = this._panels[this._panels.length - 1];
|
|
95
|
+
panel.view.needsLayout = YES;
|
|
96
|
+
panel.view.layoutIfNeeded();
|
|
97
|
+
panel.settingsPanelDidLayoutView();
|
|
98
|
+
panel._awaitsLayout = NO;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Prepares the given settings panel for display.
|
|
104
|
+
* @param panel <_BMLayoutEditorSettingsPanel> The panel to display.
|
|
105
|
+
* {
|
|
106
|
+
* @param withBackButton <Boolean, nullable> Controls whether this panel will contain a back button.
|
|
107
|
+
* }
|
|
108
|
+
*/
|
|
109
|
+
_provisionPanel(panel, {withBackButton: backButton} = {withBackButton: NO}) {
|
|
110
|
+
panel._settingsView = this;
|
|
111
|
+
|
|
112
|
+
// Create a container that will contain both the panel's content and its title view
|
|
113
|
+
const container = BMView.view();
|
|
114
|
+
this.addSubview(container);
|
|
115
|
+
container.leading.equalTo(this.leading).isActive = YES;
|
|
116
|
+
container.trailing.equalTo(this.trailing).isActive = YES;
|
|
117
|
+
container.top.equalTo(this.top).isActive = YES;
|
|
118
|
+
container.bottom.equalTo(this.bottom).isActive = YES;
|
|
119
|
+
panel._container = container;
|
|
120
|
+
container.node.classList.add('BMLayoutEditorDetails');
|
|
121
|
+
container.node.style.cssText = 'border: none !important;'
|
|
122
|
+
container.node.style.overflow = 'hidden';
|
|
123
|
+
|
|
124
|
+
// Create a view that will contain the panel's content
|
|
125
|
+
const view = BMView.view();
|
|
126
|
+
container.addSubview(view);
|
|
127
|
+
|
|
128
|
+
view.leading.equalTo(container.leading).isActive = YES;
|
|
129
|
+
view.trailing.equalTo(container.trailing).isActive = YES;
|
|
130
|
+
view.bottom.equalTo(container.bottom).isActive = YES;
|
|
131
|
+
|
|
132
|
+
panel._view = view;
|
|
133
|
+
|
|
134
|
+
// Create a title view for the panel
|
|
135
|
+
const titleView = BMView.view();
|
|
136
|
+
titleView.node.innerText = (backButton ? '‹ ' : '') + panel.title || '';
|
|
137
|
+
titleView.node.className = 'BMWindowTitle BMLayoutEditorDetailsTitle';
|
|
138
|
+
let titleViewLeading = 64;
|
|
139
|
+
if (backButton) {
|
|
140
|
+
titleView.node.classList.add('BMLayoutEditorDetailsItemReadonlyValueClickable');
|
|
141
|
+
titleView.node.classList.add('BMLayoutEditorDetailsBackButton');
|
|
142
|
+
titleViewLeading -= 8;
|
|
143
|
+
}
|
|
144
|
+
titleView.node.style.zIndex = 1;
|
|
145
|
+
titleView.supportsAutomaticIntrinsicSize = YES;
|
|
146
|
+
container.addSubview(titleView);
|
|
147
|
+
|
|
148
|
+
titleView.leading.equalTo(container.leading, {plus: titleViewLeading}).isActive = YES;
|
|
149
|
+
titleView.trailing.lessThanOrEqualTo(container.trailing, {plus: -16}).isActive = YES;
|
|
150
|
+
titleView.height.greaterThanOrEqualTo(32).isActive = YES;
|
|
151
|
+
titleView.top.equalTo(container.top, {plus: 12}).isActive = YES;
|
|
152
|
+
panel._titleView = titleView;
|
|
153
|
+
|
|
154
|
+
view.top.equalTo(titleView.bottom, {plus: 12}).isActive = YES;
|
|
155
|
+
|
|
156
|
+
if (backButton) {
|
|
157
|
+
titleView.node.addEventListener('click', e => this.popPanel());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Instruct the panel to set up its contents
|
|
161
|
+
panel.settingsPanelDidLoad();
|
|
162
|
+
|
|
163
|
+
if (this._window && !this._window._visible) {
|
|
164
|
+
panel._awaitsLayout = YES;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Perform layout
|
|
168
|
+
this.layoutIfNeeded();
|
|
169
|
+
panel.settingsPanelDidLayoutView();
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Selects the given view, creating a settings panel for it and pushing it onto the settings stack.
|
|
175
|
+
* @param view <BMView> The view to select. This must be part of the layout hierarchy managed by this
|
|
176
|
+
* settings view's layout editor.
|
|
177
|
+
*/
|
|
178
|
+
selectView(view) {
|
|
179
|
+
// Prevent the settings view from reacting to this selection
|
|
180
|
+
this._ignoresSelection = YES;
|
|
181
|
+
this._editor.selectView(view);
|
|
182
|
+
|
|
183
|
+
// Create and push a panel for this view
|
|
184
|
+
const panel = (new _BMLayoutEditorViewSettingsPanel).initWithSettingsView(this, {forView: view});
|
|
185
|
+
this._provisionPanel(panel, {withBackButton: YES});
|
|
186
|
+
this.pushPanel(panel);
|
|
187
|
+
|
|
188
|
+
// Restore the usual selection behaviour
|
|
189
|
+
this._ignoresSelection = NO;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Invoked upon the layout editor selecting a view. Clears the current settings stack, then creates a settings
|
|
194
|
+
* panel for the selected view and pushes it onto the stack.
|
|
195
|
+
* @param view <BMView> The view that was selected.
|
|
196
|
+
*/
|
|
197
|
+
layoutEditorDidSelectView(view) {
|
|
198
|
+
if (this._ignoresSelection) return;
|
|
199
|
+
|
|
200
|
+
let animated = YES;
|
|
201
|
+
|
|
202
|
+
if (this._window && !this._window._visible) {
|
|
203
|
+
animated = NO;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const panel = (new _BMLayoutEditorViewSettingsPanel).initWithSettingsView(this, {forView: view});
|
|
207
|
+
this._provisionPanel(panel);
|
|
208
|
+
|
|
209
|
+
this.resetToPanel(panel, {animated});
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Invoked upon the layout editor selecting a goup views. Clears the current settings stack, then creates a settings
|
|
214
|
+
* panel for multiple views and pushes it onto the stack.
|
|
215
|
+
* @param views <[BMView]> The views that were selected.
|
|
216
|
+
*/
|
|
217
|
+
layoutEditorDidSelectViews(views) {
|
|
218
|
+
if (this._panels[0] instanceof _BMLayoutEditorViewGroupSettingsPanel) {
|
|
219
|
+
this._panels[0].views = views;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (this._ignoresSelection) return;
|
|
223
|
+
|
|
224
|
+
let animated = YES;
|
|
225
|
+
|
|
226
|
+
if (this._window && !this._window._visible) {
|
|
227
|
+
animated = NO;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const panel = (new _BMLayoutEditorViewGroupSettingsPanel).initWithSettingsView(this, {forViews: views});
|
|
231
|
+
this._provisionPanel(panel);
|
|
232
|
+
|
|
233
|
+
this.resetToPanel(panel, {animated});
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Selects the given constraint with the given view as a reference, creating a settings panel for it and pushing it
|
|
238
|
+
* onto the settings stack.
|
|
239
|
+
* @param constraint <BMLayoutConstraint> The constraint to select. This must be part of the layout hierarchy managed by
|
|
240
|
+
* this setting view's layout editor.
|
|
241
|
+
* {
|
|
242
|
+
* @param withReferenceView <BMView> The view from which the constraint was selected.
|
|
243
|
+
* }
|
|
244
|
+
*/
|
|
245
|
+
selectConstraint(constraint, {withReferenceView: view}) {
|
|
246
|
+
// Prevent the settings view from reacting to this selection
|
|
247
|
+
this._ignoresSelection = YES;
|
|
248
|
+
this._editor.selectConstraint(constraint, {withReferenceView: view});
|
|
249
|
+
|
|
250
|
+
// Create and push a panel for this view
|
|
251
|
+
const panel = (new _BMLayoutEditorConstraintSettingsPanel).initWithSettingsView(this, {forConstraint: constraint, withReferenceView: view});
|
|
252
|
+
this._provisionPanel(panel, {withBackButton: YES});
|
|
253
|
+
this.pushPanel(panel);
|
|
254
|
+
|
|
255
|
+
// Restore the usual selection behaviour
|
|
256
|
+
this._ignoresSelection = NO;
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Invoked upon the layout editor selecting a constraint. Clears the current settings stack, then creates a settings
|
|
261
|
+
* panel for the selected constraint and pushes it onto the stack.
|
|
262
|
+
* @param constrant <BMLayoutConstraint> The constraint that was selected.
|
|
263
|
+
* {
|
|
264
|
+
* @param withReferenceView <BMView> The view from which the constraint was selected.
|
|
265
|
+
* }
|
|
266
|
+
*/
|
|
267
|
+
layoutEditorDidSelectConstraint(constraint, {withReferenceView: view}) {
|
|
268
|
+
if (this._ignoresSelection) return;
|
|
269
|
+
|
|
270
|
+
let animated = YES;
|
|
271
|
+
|
|
272
|
+
if (this._window && !this._window._visible) {
|
|
273
|
+
animated = NO;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const panel = (new _BMLayoutEditorConstraintSettingsPanel).initWithSettingsView(this, {forConstraint: constraint, withReferenceView: view});
|
|
277
|
+
this._provisionPanel(panel, {withBackButton: YES});
|
|
278
|
+
|
|
279
|
+
this.pushPanel(panel, {animated});
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Invoked upon the layout editor changing the currently selected layout size class.
|
|
284
|
+
* This method in turns forwards this message to the settings panels currently in the back stack,
|
|
285
|
+
* allowing them to customize their options for this size class change.
|
|
286
|
+
* @param sizeClass <BMLayoutSizeClass> The size class that was selected by the layout editor.
|
|
287
|
+
*/
|
|
288
|
+
layoutEditorDidSelectSizeClass(sizeClass) {
|
|
289
|
+
for (const panel of this._panels) {
|
|
290
|
+
panel.layoutEditorDidSelectSizeClass(sizeClass);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Clears the back stack, then pushes the given panel onto the stack, making it visible. This change will be animated.
|
|
296
|
+
* @param panel <_BMLayoutEditorSettingsPanel, nullable> The panel to display. If `undefined`, the settings view will be cleared.
|
|
297
|
+
* {
|
|
298
|
+
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
299
|
+
* }
|
|
300
|
+
* @return <Promise<void>> A promise that resolves when this operation completes.
|
|
301
|
+
*/
|
|
302
|
+
async resetToPanel(panel, {animated = YES} = {animated: YES}) {
|
|
303
|
+
// Unload everything except the current panel
|
|
304
|
+
const currentPanel = this.currentPanel;
|
|
305
|
+
|
|
306
|
+
for (const panel of this._panels) {
|
|
307
|
+
if (panel != currentPanel) {
|
|
308
|
+
panel.settingsPanelWillUnload();
|
|
309
|
+
panel._container.release();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// If there is no current panel, just display the given panel immediately
|
|
314
|
+
if (!currentPanel) {
|
|
315
|
+
if (panel) {
|
|
316
|
+
this._panels.push(panel);
|
|
317
|
+
this._ignoresSelection = YES;
|
|
318
|
+
panel.settingsPanelWillAppear(NO);
|
|
319
|
+
this._ignoresSelection = NO;
|
|
320
|
+
panel.settingsPanelDidAppear(NO);
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this._panels = panel ? [panel] : [];
|
|
326
|
+
|
|
327
|
+
if (currentPanel) {
|
|
328
|
+
// Animate the outgoing panel, if it exists
|
|
329
|
+
currentPanel.settingsPanelWillDisappear(YES);
|
|
330
|
+
if (animated) BMAnimationBeginWithDuration(200, {easing: 'easeInOutQuad'});
|
|
331
|
+
|
|
332
|
+
//const frame = currentPanel._container.frame.copy();
|
|
333
|
+
//frame.offsetWithX(0, {y: 128});
|
|
334
|
+
//currentPanel._container.frame = frame;
|
|
335
|
+
currentPanel._container.opacity = 0;
|
|
336
|
+
if (animated) {
|
|
337
|
+
const controller = BMAnimationContextGetCurrent().controllerForObject(currentPanel._container, {node: currentPanel._container.node});
|
|
338
|
+
controller.registerBuiltInPropertiesWithDictionary({scaleX: .85, scaleY: .85});
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
BMHook(currentPanel._container.node, {scaleX: .85, scaleY: .85});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
if (panel && animated) BMAnimationBeginWithDuration(200, {easing: 'easeInOutQuad'});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (panel) {
|
|
349
|
+
// Prepare the incoming panel for the animation
|
|
350
|
+
BMAnimationContextBeginStatic();
|
|
351
|
+
this._ignoresSelection = YES;
|
|
352
|
+
panel.settingsPanelWillAppear(YES);
|
|
353
|
+
this._ignoresSelection = NO;
|
|
354
|
+
/*const initialFrame = panel._container.frame.copy();
|
|
355
|
+
const animationFrame = initialFrame.copy();
|
|
356
|
+
animationFrame.offsetWithX(0, {y: 256});
|
|
357
|
+
panel._container.frame = animationFrame;*/
|
|
358
|
+
panel._container.opacity = 0;
|
|
359
|
+
|
|
360
|
+
BMHook(panel._container.node, {scaleX: 1.23, scaleY: 1.23});
|
|
361
|
+
if (animated) BMAnimationApply();
|
|
362
|
+
|
|
363
|
+
// Animate the incoming panel
|
|
364
|
+
if (animated) {
|
|
365
|
+
const controller = BMAnimationContextGetCurrent().controllerForObject(panel._container, {node: panel._container.node});
|
|
366
|
+
controller.registerBuiltInPropertiesWithDictionary({scaleX: 1, scaleY: 1});
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
BMHook(panel._container.node, {scaleX: 1, scaleY: 1});
|
|
370
|
+
}
|
|
371
|
+
//panel._container.frame = initialFrame;
|
|
372
|
+
panel._container.opacity = 1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if ((panel || currentPanel) && animated) await BMAnimationApply();
|
|
376
|
+
|
|
377
|
+
if (currentPanel) {
|
|
378
|
+
// Perform any cleanup needed after the animation
|
|
379
|
+
currentPanel.settingsPanelDidDisappear(YES);
|
|
380
|
+
currentPanel.settingsPanelWillUnload();
|
|
381
|
+
currentPanel._container.release();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (panel) panel.settingsPanelDidAppear(YES);
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Pushes the given panel onto the back stack, making it visible. This change will be animated.
|
|
389
|
+
* @param panel <_BMLayoutEditorSettingsPanel> The panel to push.
|
|
390
|
+
* {
|
|
391
|
+
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
392
|
+
* }
|
|
393
|
+
* @return <Promise<void>> A promise that resolves when this operation completes.
|
|
394
|
+
*/
|
|
395
|
+
async pushPanel(panel, {animated = YES} = {animated: YES}) {
|
|
396
|
+
const currentPanel = this.currentPanel;
|
|
397
|
+
this._panels.push(panel);
|
|
398
|
+
|
|
399
|
+
if (currentPanel) {
|
|
400
|
+
// Animate the outgoing panel, if it exists
|
|
401
|
+
if (animated) BMAnimationBeginWithDuration(200, {easing: 'easeInOutQuad'});
|
|
402
|
+
currentPanel.settingsPanelWillDisappear(YES);
|
|
403
|
+
|
|
404
|
+
const frame = currentPanel._container.frame.copy();
|
|
405
|
+
frame.offsetWithX(-frame.width / 2 | 0, {y: 0});
|
|
406
|
+
currentPanel._container.frame = frame;
|
|
407
|
+
currentPanel._container.opacity = 0;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
if (animated) BMAnimationBeginWithDuration(200, {easing: 'easeInOutQuad'});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Prepare the incoming panel for the animation
|
|
414
|
+
BMAnimationContextBeginStatic();
|
|
415
|
+
const initialFrame = panel._container.frame.copy();
|
|
416
|
+
const animationFrame = initialFrame.copy();
|
|
417
|
+
animationFrame.offsetWithX(initialFrame.width, {y: 0});
|
|
418
|
+
panel._container.frame = animationFrame;
|
|
419
|
+
BMAnimationApply();
|
|
420
|
+
this._ignoresSelection = YES;
|
|
421
|
+
panel.settingsPanelWillAppear(YES);
|
|
422
|
+
this._ignoresSelection = NO;
|
|
423
|
+
|
|
424
|
+
// Animate the incoming panel
|
|
425
|
+
panel._container.frame = initialFrame;
|
|
426
|
+
|
|
427
|
+
if (animated) await BMAnimationApply();
|
|
428
|
+
|
|
429
|
+
// Perform any cleanup needed after the animation
|
|
430
|
+
currentPanel.settingsPanelDidDisappear(YES);
|
|
431
|
+
currentPanel._container.isVisible = NO;
|
|
432
|
+
|
|
433
|
+
panel.settingsPanelDidAppear(YES);
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Pops the topmost settings panel from the back stack, revealing the previous one. This change will be animated.
|
|
438
|
+
* @return <Promise<void>> A promise that resolves when this operation completes.
|
|
439
|
+
*/
|
|
440
|
+
async popPanel() {
|
|
441
|
+
const currentPanel = this.currentPanel;
|
|
442
|
+
currentPanel._container.node.style.pointerEvents = 'none';
|
|
443
|
+
this._panels.pop();
|
|
444
|
+
|
|
445
|
+
// Animate the outgoing panel
|
|
446
|
+
BMAnimationBeginWithDuration(200, {easing: 'easeInOutQuad'});
|
|
447
|
+
currentPanel.settingsPanelWillDisappear(YES);
|
|
448
|
+
|
|
449
|
+
const frame = this.frame.copy();
|
|
450
|
+
frame.origin = BMPointMake();
|
|
451
|
+
frame.offsetWithX(frame.width , {y: 0});
|
|
452
|
+
currentPanel._container.frame = frame;
|
|
453
|
+
|
|
454
|
+
const previousPanel = this.currentPanel;
|
|
455
|
+
|
|
456
|
+
if (previousPanel) {
|
|
457
|
+
// Prepare the incoming panel for the animationƒ
|
|
458
|
+
BMAnimationContextBeginStatic();
|
|
459
|
+
const initialFrame = this.frame.copy();
|
|
460
|
+
initialFrame.origin = BMPointMake();
|
|
461
|
+
const animationFrame = initialFrame.copy();
|
|
462
|
+
animationFrame.offsetWithX(-frame.width / 2 | 0, {y: 0});
|
|
463
|
+
previousPanel._container.frame = animationFrame;
|
|
464
|
+
previousPanel._container.opacity = 0;
|
|
465
|
+
previousPanel._container.isVisible = YES;
|
|
466
|
+
BMAnimationApply();
|
|
467
|
+
this._ignoresSelection = YES;
|
|
468
|
+
previousPanel.settingsPanelWillAppear(YES);
|
|
469
|
+
this._ignoresSelection = NO;
|
|
470
|
+
|
|
471
|
+
// Animate the incoming panel
|
|
472
|
+
previousPanel._container.frame = initialFrame;
|
|
473
|
+
previousPanel._container.opacity = 1;
|
|
474
|
+
|
|
475
|
+
if (previousPanel._awaitsLayout) {
|
|
476
|
+
BMAnimationContextBeginStatic();
|
|
477
|
+
previousPanel._titleView.invalidateIntrinsicSize();
|
|
478
|
+
previousPanel.view.needsLayout = YES;
|
|
479
|
+
previousPanel.view.layoutIfNeeded();
|
|
480
|
+
previousPanel.settingsPanelDidLayoutView();
|
|
481
|
+
previousPanel._awaitsLayout = NO;
|
|
482
|
+
BMAnimationApply();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
await BMAnimationApply();
|
|
487
|
+
|
|
488
|
+
// Perform any cleanup needed after the animation
|
|
489
|
+
currentPanel.settingsPanelDidDisappear(YES);
|
|
490
|
+
currentPanel.settingsPanelWillUnload();
|
|
491
|
+
currentPanel._container.release();
|
|
492
|
+
|
|
493
|
+
if (previousPanel) previousPanel.settingsPanelDidAppear(YES);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Constructs and initializes a settings view for the given node.
|
|
499
|
+
* @param node <DOMNode> The DOM node to be used by the settings view.
|
|
500
|
+
* {
|
|
501
|
+
* @param editor <BMLayoutEditor> The layout editor managing this view.
|
|
502
|
+
* }
|
|
503
|
+
* @return <_BMLayoutEditorSettingsView> A settings view.
|
|
504
|
+
*/
|
|
505
|
+
_BMLayoutEditorSettingsView.settingsViewWithNode = function (node, {forEditor: editor}) {
|
|
506
|
+
return (new _BMLayoutEditorSettingsView).initWithDOMNode(node, {forEditor: editor});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// @endtype
|
|
510
|
+
|
|
511
|
+
// @type _BMLayoutEditorSettingsPanel
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* A settings panel controls the settings that are available on the settings view.
|
|
515
|
+
* A different subclass of the settings panel is typically used depending on the selected item.
|
|
516
|
+
*/
|
|
517
|
+
export function _BMLayoutEditorSettingsPanel() {} // <constructor>
|
|
518
|
+
|
|
519
|
+
_BMLayoutEditorSettingsPanel.prototype = {
|
|
520
|
+
constructor: _BMLayoutEditorSettingsPanel,
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* The settings view to which this panel belongs.
|
|
524
|
+
*/
|
|
525
|
+
_settingsView: undefined, // <_BMLayoutEditorSettingsView>
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* This panel's title.
|
|
529
|
+
*/
|
|
530
|
+
_title: undefined, // <String>
|
|
531
|
+
|
|
532
|
+
get title() {
|
|
533
|
+
return this._title;
|
|
534
|
+
},
|
|
535
|
+
set title(title) {
|
|
536
|
+
this._title = title;
|
|
537
|
+
if (this._titleView) {
|
|
538
|
+
this._titleView.node.innerText = title;
|
|
539
|
+
this._titleView.invalidateIntrinsicSize();
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* The view representing this panel.
|
|
545
|
+
*/
|
|
546
|
+
_container: undefined, // <BMView, nullable>
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* The title view associated to this panel.
|
|
550
|
+
*/
|
|
551
|
+
_titleView: undefined, // <BMView, nullable>
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* The view to which this panel's content should be added.
|
|
555
|
+
*/
|
|
556
|
+
_view: undefined, // <BMView, nullable>
|
|
557
|
+
|
|
558
|
+
get view() {
|
|
559
|
+
return this._view;
|
|
560
|
+
},
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* The layout editor to which this panel belongs.
|
|
564
|
+
*/
|
|
565
|
+
get layoutEditor() { // <BMLayoutEditor>
|
|
566
|
+
return this._settingsView._editor;
|
|
567
|
+
},
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Designated initializer. Initializes this settings panel with the given settings view.
|
|
571
|
+
* @param view <_BMLayoutEditorSettingsView> The settings view.
|
|
572
|
+
* @return <_BMLayoutEditorSettingsPanel> This setttings panel.
|
|
573
|
+
*/
|
|
574
|
+
initWithSettingsView(view) {
|
|
575
|
+
this._settingsView = view;
|
|
576
|
+
|
|
577
|
+
return this;
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Invoked whenever this panel is about to be added to the DOM.
|
|
582
|
+
* When this method is invoked, a new view has been created from this panel
|
|
583
|
+
* and should be used to add new subviews to it.
|
|
584
|
+
* The default implementation does nothing.
|
|
585
|
+
*/
|
|
586
|
+
settingsPanelDidLoad() {
|
|
587
|
+
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Invoked after the view for settings panel has finished a layout operation.
|
|
592
|
+
*/
|
|
593
|
+
settingsPanelDidLayoutView() {
|
|
594
|
+
|
|
595
|
+
},
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Invoked prior to this settings panel becoming visible, before any animation begins.
|
|
599
|
+
* At this point, the `view` property will return a view to which this panel's subviews can be added.
|
|
600
|
+
* The default implementation does nothing.
|
|
601
|
+
* @param animated <Boolean> Set to `YES` if this change is animated. If this parameter is `YES`, this
|
|
602
|
+
* method will be invoked from within an animation block.
|
|
603
|
+
*/
|
|
604
|
+
settingsPanelWillAppear(animated) {
|
|
605
|
+
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Invoked after this settings panel has become visible, after any animation has finished running.
|
|
610
|
+
* The default implementation does nothing.
|
|
611
|
+
* @param animated <Boolean> Set to `YES` if this change was animated.
|
|
612
|
+
*/
|
|
613
|
+
settingsPanelDidAppear(animated) {
|
|
614
|
+
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Invoked before this settings panel will disappear, before any associated animation starts.
|
|
619
|
+
* The default implementation does nothing.
|
|
620
|
+
* @param animated <Boolean> Set to `YES` if this change is animated. If this parameter is `YES`, this
|
|
621
|
+
* method will be invoked from within an animation block.
|
|
622
|
+
*/
|
|
623
|
+
settingsPanelWillDisappear(animated) {
|
|
624
|
+
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Invoked after this settings panel has disappeared, after any associated animation has finished running.
|
|
629
|
+
* After this method returns, this panel's `view` may be removed from its hierarchy.
|
|
630
|
+
* The default implementation does nothing.
|
|
631
|
+
* @param animated <Boolean> Set to `YES` if this change was animated.
|
|
632
|
+
*/
|
|
633
|
+
settingsPanelDidDisappear(animated) {
|
|
634
|
+
|
|
635
|
+
},
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Invoked when the content managed by this settings panel is about to be removed from the DOM.
|
|
639
|
+
* After this method returns, this panel's `view` will be removed from its view hierarchy.
|
|
640
|
+
* The default implementation does nothing.
|
|
641
|
+
*/
|
|
642
|
+
settingsPanelWillUnload() {
|
|
643
|
+
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// @endtype
|
|
648
|
+
|
|
649
|
+
// @type _BMLayoutEditorCollectionSettingsPanel
|
|
650
|
+
|
|
651
|
+
export function _BMLayoutEditorCollectionSettingsPanel() {} // <constructor>
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* A subclass of settings panel that manages a list of settings tabs, associating a collection
|
|
655
|
+
* view of settings with each of the tabs.
|
|
656
|
+
*/
|
|
657
|
+
_BMLayoutEditorCollectionSettingsPanel.prototype = BMExtend(Object.create(_BMLayoutEditorSettingsPanel.prototype), {
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* An array of settings tab controlling the settings
|
|
662
|
+
* available for this panel.
|
|
663
|
+
*/
|
|
664
|
+
_tabs: undefined, // <[BMLayoutEditorSettingsTab]>
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* The currently visible tab.
|
|
668
|
+
*/
|
|
669
|
+
_currentTab: undefined, // <BMLayoutEditorSettingsTab>
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Configures the layout settings of the given collection view.
|
|
673
|
+
* @param collectionView <BMCollectionView> The collection view.
|
|
674
|
+
* {
|
|
675
|
+
* @param forTab <BMLayoutEditorSettingsTab> The tab for which to configure this collection view.
|
|
676
|
+
* }
|
|
677
|
+
*/
|
|
678
|
+
_configureCollectionViewLayout(collectionView, {forTab: tab}) {
|
|
679
|
+
collectionView.node.style.position = 'absolute';
|
|
680
|
+
|
|
681
|
+
// Configure the layout
|
|
682
|
+
// Automatic cell size is only used on the constraints tab
|
|
683
|
+
if (tab instanceof _BMLayoutEditorViewLayoutSettingsTab) {
|
|
684
|
+
collectionView.layout.expectedCellSize = BMSizeMake(384, 48);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
collectionView.layout.expectedCellSize = BMSizeMake(384, 32);
|
|
688
|
+
}
|
|
689
|
+
collectionView.layout.maximumCellsPerRow = 1;
|
|
690
|
+
collectionView.layout.gravity = BMCollectionViewFlowLayoutGravity.Expand;
|
|
691
|
+
collectionView.layout.rowSpacing = 0;
|
|
692
|
+
collectionView.layout.contentGravity = BMCollectionViewFlowLayoutAlignment.Start;
|
|
693
|
+
collectionView.layout.showsFooters = YES;
|
|
694
|
+
collectionView.layout.footerHeight = 17;
|
|
695
|
+
collectionView.layout.topPadding = 8;
|
|
696
|
+
|
|
697
|
+
collectionView.identityComparator = (setting1, setting2) => {
|
|
698
|
+
if (!setting1 && !setting2) return YES;
|
|
699
|
+
if (!setting1 || !setting2) return NO;
|
|
700
|
+
return setting1.isEqualToSetting(setting2);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Register the classes for the default cell types
|
|
704
|
+
for (const key in BMLayoutEditorSettingKind) {
|
|
705
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsCell, {forReuseIdentifier: BMLayoutEditorSettingKind[key]});
|
|
706
|
+
}
|
|
707
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsConstraintCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Constraint});
|
|
708
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsTitleCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Title});
|
|
709
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsIntegerCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Integer});
|
|
710
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsNumberCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Number});
|
|
711
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsConstantCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Constant});
|
|
712
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsStringCell, {forReuseIdentifier: BMLayoutEditorSettingKind.String});
|
|
713
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsBooleanCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Boolean});
|
|
714
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsReadonlyCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Info});
|
|
715
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsDeleteConstraintCell, {forReuseIdentifier: BMLayoutEditorSettingKind.DeleteConstraintButton})
|
|
716
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsDeactivateConstraintsCell, {forReuseIdentifier: BMLayoutEditorSettingKind.DeactivateConstraintsButton});
|
|
717
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsSegmentCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Segment});
|
|
718
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsViewCell, {forReuseIdentifier: BMLayoutEditorSettingKind.View});
|
|
719
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsDropdownCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Enum});
|
|
720
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsInsetCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Insets});
|
|
721
|
+
collectionView.registerCellClass(BMLayoutEditorSettingsSizeCell, {forReuseIdentifier: BMLayoutEditorSettingKind.Size});
|
|
722
|
+
collectionView.registerSupplementaryViewClass(BMLayoutEditorSettingsFooter, {forReuseIdentifier: BMCollectionViewFlowLayoutSupplementaryView.Footer});
|
|
723
|
+
collectionView.registerSupplementaryViewClass(BMLayoutEditorSettingsFooter, {forReuseIdentifier: BMCollectionViewFlowLayoutSupplementaryView.Empty});
|
|
724
|
+
|
|
725
|
+
// Register the custom cell classes
|
|
726
|
+
for (const key in this.layoutEditor._settingCellClasses) {
|
|
727
|
+
collectionView.registerCellClass(this.layoutEditor._settingCellClasses[key], {forReuseIdentifier: key});
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
// @override - BMLayoutEditorSettingsPanel
|
|
732
|
+
settingsPanelDidLoad() {
|
|
733
|
+
// Create the topmost tab selector
|
|
734
|
+
const tabView = BMView.view();
|
|
735
|
+
tabView.CSSClass = 'BMLayoutEditorDetailsTabView';
|
|
736
|
+
this.view.addSubview(tabView);
|
|
737
|
+
|
|
738
|
+
// Set up the constraints for the tab view
|
|
739
|
+
tabView.height.equalTo(48).isActive = YES;
|
|
740
|
+
tabView.leading.equalTo(this.view.leading).isActive = YES;
|
|
741
|
+
tabView.trailing.equalTo(this.view.trailing).isActive = YES;
|
|
742
|
+
tabView.top.equalTo(this.view.top).isActive = YES;
|
|
743
|
+
|
|
744
|
+
// Create the tab host, to which individual tabs will be added
|
|
745
|
+
const tabHost = BMView.view();
|
|
746
|
+
this.view.addSubview(tabHost);
|
|
747
|
+
|
|
748
|
+
// Set up the constraints for the tab host
|
|
749
|
+
tabHost.top.equalTo(tabView.bottom).isActive = YES;
|
|
750
|
+
tabHost.leading.equalTo(this.view.leading).isActive = YES;
|
|
751
|
+
tabHost.trailing.equalTo(this.view.trailing).isActive = YES;
|
|
752
|
+
tabHost.bottom.equalTo(this.view.bottom).isActive = YES;
|
|
753
|
+
|
|
754
|
+
for (const tab of this._tabs) {
|
|
755
|
+
// Create a collection view for each tab that will display its contents
|
|
756
|
+
const collectionView = BMCollectionView.collectionView();
|
|
757
|
+
|
|
758
|
+
collectionView.allowsOffscreenLayout = NO;
|
|
759
|
+
|
|
760
|
+
tabHost.addSubview(collectionView);
|
|
761
|
+
|
|
762
|
+
this._configureCollectionViewLayout(collectionView, {forTab: tab});
|
|
763
|
+
|
|
764
|
+
collectionView.leading.equalTo(tabHost.leading).isActive = true;
|
|
765
|
+
collectionView.trailing.equalTo(tabHost.trailing).isActive = true;
|
|
766
|
+
collectionView.top.equalTo(tabHost.top).isActive = true;
|
|
767
|
+
collectionView.bottom.equalTo(tabHost.bottom).isActive = true;
|
|
768
|
+
|
|
769
|
+
tab._collectionView = collectionView;
|
|
770
|
+
collectionView._settingsTab = tab;
|
|
771
|
+
|
|
772
|
+
// Default to showing the current tab, hiding all other content
|
|
773
|
+
if (tab != this._currentTab) {
|
|
774
|
+
collectionView.isVisible = NO;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Create the clickable tabs
|
|
778
|
+
const tabNode = document.createElement('img');
|
|
779
|
+
tabNode.className = 'BMLayoutEditorDetailsTab';
|
|
780
|
+
tabNode.src = tab.icon;
|
|
781
|
+
tabView.contentNode.appendChild(tabNode);
|
|
782
|
+
|
|
783
|
+
// Default to showing the current tab
|
|
784
|
+
if (tab == this._currentTab) {
|
|
785
|
+
tabNode.classList.add('BMLayoutEditorDetailsTabSelected');
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
tab._tabNode = tabNode;
|
|
789
|
+
|
|
790
|
+
// Switch between the tabs on click
|
|
791
|
+
tabNode.addEventListener('click', event => {
|
|
792
|
+
if (tabNode == this._currentTab) return;
|
|
793
|
+
// Deselect the current tab
|
|
794
|
+
this._currentTab._collectionView.isVisible = NO;
|
|
795
|
+
this._currentTab._tabNode.classList.remove('BMLayoutEditorDetailsTabSelected');
|
|
796
|
+
// Then select the new one
|
|
797
|
+
this._currentTab = tab;
|
|
798
|
+
collectionView.isVisible = YES;
|
|
799
|
+
tabNode.classList.add('BMLayoutEditorDetailsTabSelected');
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
|
|
804
|
+
// @override - BMLayoutEditorSettingsPanel
|
|
805
|
+
settingsPanelDidLayoutView() {
|
|
806
|
+
for (const tab of this._tabs) {
|
|
807
|
+
tab.collectionView = tab._collectionView;
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
|
|
811
|
+
// @override - BMLayoutEditorSettingsPanel
|
|
812
|
+
settingsPanelWillUnload() {
|
|
813
|
+
for (const tab of this._tabs) {
|
|
814
|
+
tab._collectionView.release();
|
|
815
|
+
tab._collectionView = undefined;
|
|
816
|
+
}
|
|
817
|
+
},
|
|
818
|
+
|
|
819
|
+
// @override - BMLayoutEditorSettingsPanel
|
|
820
|
+
layoutEditorDidSelectSizeClass(sizeClass) {
|
|
821
|
+
for (const tab of this._tabs) {
|
|
822
|
+
tab.layoutEditorDidSelectSizeClass(sizeClass);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
// @endtype
|
|
828
|
+
|
|
829
|
+
// @type BMLayoutEditorSettingsTab implements BMCollectionViewDataSet, BMCollectionViewDelegate
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* A settings tab represents a page of settings for the layout editor.
|
|
833
|
+
*/
|
|
834
|
+
export function BMLayoutEditorSettingsTab() {} // <constructor>
|
|
835
|
+
|
|
836
|
+
BMLayoutEditorSettingsTab.prototype = {
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* The name of the tab.
|
|
840
|
+
*/
|
|
841
|
+
name: undefined, // <String>
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* The URL pointing to an icon that represents this tab.
|
|
845
|
+
*/
|
|
846
|
+
icon: undefined, // <String>
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* The constraint to which this tab applies.
|
|
850
|
+
*/
|
|
851
|
+
_constraint: undefined, // <BMLayoutConstraint, nullable>
|
|
852
|
+
get constraint() {
|
|
853
|
+
return this._constraint;
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* The view to which this tab applies.
|
|
858
|
+
*/
|
|
859
|
+
_view: undefined, // <BMView, nullable>
|
|
860
|
+
get view() {
|
|
861
|
+
return this._view;
|
|
862
|
+
},
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* An array of sections making up this settings tab.
|
|
866
|
+
*/
|
|
867
|
+
_settingSections: undefined, // <[BMLayoutEditorSettingsSection]>
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* The array of setting sections making up this settings tab.
|
|
871
|
+
* This also includes settings created for variations.
|
|
872
|
+
*/
|
|
873
|
+
_compiledSettingSections: undefined, // <[BMLayoutEditorSettingsSection]>
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* The collection view managed by this settings tab, which displays
|
|
877
|
+
* the actual content.
|
|
878
|
+
*/
|
|
879
|
+
_collectionView: undefined, // <BMCollectionView, nullable>
|
|
880
|
+
|
|
881
|
+
get collectionView() {
|
|
882
|
+
return this._collectionView;
|
|
883
|
+
},
|
|
884
|
+
set collectionView(view) {
|
|
885
|
+
this._compiledSettingSections.length = 0;
|
|
886
|
+
|
|
887
|
+
// Compile the settings when first being assigned a collection view, as the layout editor
|
|
888
|
+
// will not have been available the first time `commitUpdates` was invoked
|
|
889
|
+
for (const section of this._settingSections) {
|
|
890
|
+
this._compiledSettingSections.push(section._compiledSectionForSizeClass(this.layoutEditor.activeSizeClass));
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
this._collectionView = view;
|
|
894
|
+
view.dataSet = this;
|
|
895
|
+
view.delegate = this;
|
|
896
|
+
},
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* The settings panel to which this tab was added.
|
|
900
|
+
*/
|
|
901
|
+
_settingsPanel: undefined, // <_BMLayoutEditorSettingsPanel, nullable>
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* The layout editor to which this tab was added.
|
|
905
|
+
*/
|
|
906
|
+
get layoutEditor() { // <BMLayoutEditor, nullable>
|
|
907
|
+
if (this._settingsPanel) {
|
|
908
|
+
return this._settingsPanel.layoutEditor;
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Initializes this tab with the given name and icon.
|
|
914
|
+
* @param name <String> The name of the tab.
|
|
915
|
+
* {
|
|
916
|
+
* @param icon <String> The URL of the icon representing this tab.
|
|
917
|
+
* }
|
|
918
|
+
* @return <BMLayoutEditorSettingsTab> This settings tab.
|
|
919
|
+
*/
|
|
920
|
+
initWithName(name, {icon}) {
|
|
921
|
+
this.name = name;
|
|
922
|
+
this.icon = icon;
|
|
923
|
+
this._settingSections = [];
|
|
924
|
+
this._compiledSettingSections = [];
|
|
925
|
+
|
|
926
|
+
return this;
|
|
927
|
+
},
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* An identifier unique to each update settings request.
|
|
931
|
+
*/
|
|
932
|
+
_updateSettingsID: 0, // <Number>
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Should be invoked to cause this tab to reload its settings whenever a setting is added or removed.
|
|
936
|
+
*/
|
|
937
|
+
async updateSettings() {
|
|
938
|
+
this._updateSettingsID = this._updateSettingsID + 1;
|
|
939
|
+
const ID = this._updateSettingsID;
|
|
940
|
+
|
|
941
|
+
// Await for any current data update
|
|
942
|
+
while (this._collectionView && this._collectionView.isUpdatingData) {
|
|
943
|
+
await this._collectionView._dataUpdatePromise;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// If a new update request arrives before this one gets a chance to execute, omit this update
|
|
947
|
+
if (this._updateSettingsID != ID) return;
|
|
948
|
+
|
|
949
|
+
this.beginUpdates();
|
|
950
|
+
this._settingsPanel._updateSettingsForTab(this);
|
|
951
|
+
this.commitUpdates();
|
|
952
|
+
},
|
|
953
|
+
|
|
954
|
+
// @override - BMCollectionViewDataSet
|
|
955
|
+
numberOfSections() {
|
|
956
|
+
return this._compiledSettingSections.length;
|
|
957
|
+
},
|
|
958
|
+
|
|
959
|
+
// @override - BMCollectionViewDataSet
|
|
960
|
+
numberOfObjectsInSectionAtIndex(index) {
|
|
961
|
+
return this._compiledSettingSections[index]._settings.length + (this._compiledSettingSections[index].name ? 1 : 0);
|
|
962
|
+
},
|
|
963
|
+
|
|
964
|
+
// @override - BMCollectionViewDataSet
|
|
965
|
+
indexPathForObjectAtRow(row, {inSectionAtIndex: section}) {
|
|
966
|
+
if (this._compiledSettingSections[section].name) {
|
|
967
|
+
if (row == 0) {
|
|
968
|
+
return BMIndexPathMakeWithRow(row, {section, forObject: this._compiledSettingSections[section]._nameSetting});
|
|
969
|
+
}
|
|
970
|
+
return BMIndexPathMakeWithRow(row, {section, forObject: this._compiledSettingSections[section]._settings[row - 1]});
|
|
971
|
+
}
|
|
972
|
+
return BMIndexPathMakeWithRow(row, {section, forObject: this._compiledSettingSections[section]._settings[row]});
|
|
973
|
+
},
|
|
974
|
+
|
|
975
|
+
// @override - BMCollectionViewDataSet
|
|
976
|
+
indexPathForObject(object) {
|
|
977
|
+
for (const [sectionIndex, section] of this._compiledSettingSections.entries()) {
|
|
978
|
+
for (const [settingIndex, setting] of section._settings.entries()) {
|
|
979
|
+
if (setting == section._nameSetting) {
|
|
980
|
+
return BMIndexPathMakeWithRow(0, {section: sectionIndex, forObject: object});
|
|
981
|
+
}
|
|
982
|
+
if (setting.isEqualToSetting(object)) {
|
|
983
|
+
if (section.name) {
|
|
984
|
+
return BMIndexPathMakeWithRow(settingIndex + 1, {section: sectionIndex, forObject: object});
|
|
985
|
+
}
|
|
986
|
+
return BMIndexPathMakeWithRow(settingIndex, {section: sectionIndex, forObject: object});
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
|
|
992
|
+
// @override - BMCollectionViewDataSet
|
|
993
|
+
cellForItemAtIndexPath(indexPath) {
|
|
994
|
+
const cell = this._collectionView.dequeueCellForReuseIdentifier(indexPath.object.kind);
|
|
995
|
+
cell.node.classList.add('BMLayoutEditorDetailsCell');
|
|
996
|
+
cell._tab = this;
|
|
997
|
+
cell.setting = indexPath.object;
|
|
998
|
+
return cell;
|
|
999
|
+
},
|
|
1000
|
+
|
|
1001
|
+
// @override - BMCollectionViewDataSet
|
|
1002
|
+
cellForSupplementaryViewWithIdentifier(identifier, {atIndexPath: indexPath}) {
|
|
1003
|
+
const cell = this._collectionView.dequeueCellForSupplementaryViewWithIdentifier(identifier);
|
|
1004
|
+
cell._tab = this;
|
|
1005
|
+
cell.section = this._compiledSettingSections[indexPath.section];
|
|
1006
|
+
return cell;
|
|
1007
|
+
},
|
|
1008
|
+
|
|
1009
|
+
// @override - BMCollectionViewDataSet
|
|
1010
|
+
useOldData(use) {
|
|
1011
|
+
if (!this._oldData) {
|
|
1012
|
+
throw new Error('[BMLayoutEditorSettingsTab] Illegal state - Data update performed outside of a transaction.');
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
this._compiledSettingSections = use ? this._oldData : this._newData;
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
// @override - BMCollectionViewDataSet
|
|
1019
|
+
isUsingOldData() {
|
|
1020
|
+
if (!this._oldData) {
|
|
1021
|
+
throw new Error('[BMLayoutEditorSettingsTab] Illegal state - Data update performed outside of a transaction.');
|
|
1022
|
+
}
|
|
1023
|
+
return this._compiledSettingSections === this._oldData;
|
|
1024
|
+
},
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* This method must be invoked whenever the settings available in this tab are modified.
|
|
1028
|
+
* After this method is invoked, the settings sections and their contents can be modified freely.
|
|
1029
|
+
*
|
|
1030
|
+
* After the changes have been performed, it is required to invoke the `commitUpdates()` method to
|
|
1031
|
+
* actually update the display.
|
|
1032
|
+
*/
|
|
1033
|
+
beginUpdates() {
|
|
1034
|
+
if (!this._collectionView) return;
|
|
1035
|
+
|
|
1036
|
+
this._oldData = this._compiledSettingSections.map(s => BMLayoutEditorSettingsSection.sectionWithSection(s));
|
|
1037
|
+
this._compiledSettingSections = [];
|
|
1038
|
+
this._newData = this._compiledSettingSections;
|
|
1039
|
+
},
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* This method must be invoked after every call to `beginUpdates()` to commit the changes that were pending.
|
|
1043
|
+
* @return <Promise<void>> A promise that resolves when this operation completes.
|
|
1044
|
+
*/
|
|
1045
|
+
async commitUpdates() {
|
|
1046
|
+
if (!this.layoutEditor) return;
|
|
1047
|
+
|
|
1048
|
+
if (!this._collectionView) return;
|
|
1049
|
+
|
|
1050
|
+
for (const section of this._settingSections) {
|
|
1051
|
+
this._compiledSettingSections.push(section._compiledSectionForSizeClass(this.layoutEditor.activeSizeClass));
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Await for any current data update
|
|
1055
|
+
while (this._collectionView.isUpdatingData) {
|
|
1056
|
+
await this._collectionView._dataUpdatePromise;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const promise = this._collectionView.updateEntireDataAnimated(YES);
|
|
1060
|
+
this._oldData = undefined;
|
|
1061
|
+
this._compiledSettingSections = this._newData;
|
|
1062
|
+
this._newData = undefined;
|
|
1063
|
+
await promise;
|
|
1064
|
+
},
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Invoked upon the layout editor selecting a different size class.
|
|
1068
|
+
* Causes this tab to create additional settings for those settings that support variations
|
|
1069
|
+
* and have their `automaticallyExpandsVariations` property set to `YES`.
|
|
1070
|
+
* @param sizeClass <BMLayoutSizeClass, nullable> The newly selected size class, or `undefined` if `All Size Classes` was selected.
|
|
1071
|
+
*/
|
|
1072
|
+
layoutEditorDidSelectSizeClass(sizeClass) {
|
|
1073
|
+
for (const section of this._settingSections) {
|
|
1074
|
+
for (const setting of section._settings) {
|
|
1075
|
+
if (setting.variations && setting.automaticallyExpandsVariations) {
|
|
1076
|
+
this.beginUpdates();
|
|
1077
|
+
this.commitUpdates();
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Constructs and returns a settings tab with the given name and icon.
|
|
1087
|
+
* @param name <String> The name of the tab.
|
|
1088
|
+
* {
|
|
1089
|
+
* @param icon <String> The URL of the icon representing this tab.
|
|
1090
|
+
* }
|
|
1091
|
+
* @return <BMLayoutEditorSettingsTab> A settings tab.
|
|
1092
|
+
*/
|
|
1093
|
+
BMLayoutEditorSettingsTab.tabWithName = function (name, {icon}) {
|
|
1094
|
+
return (new BMLayoutEditorSettingsTab).initWithName(name, {icon});
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// @endtype
|
|
1098
|
+
|
|
1099
|
+
// @type BMLayoutEditorSettingsSection
|
|
1100
|
+
|
|
1101
|
+
export function BMLayoutEditorSettingsSection() {} // <constructor>
|
|
1102
|
+
|
|
1103
|
+
BMLayoutEditorSettingsSection.prototype = {
|
|
1104
|
+
/**
|
|
1105
|
+
* The name of this settings section. If omitted, this section
|
|
1106
|
+
* will not show a name in the UI.
|
|
1107
|
+
*/
|
|
1108
|
+
_name: undefined, // <String, nullable>
|
|
1109
|
+
|
|
1110
|
+
get name() {
|
|
1111
|
+
return this._name;
|
|
1112
|
+
},
|
|
1113
|
+
set name(name) {
|
|
1114
|
+
this._name = name;
|
|
1115
|
+
if (name) {
|
|
1116
|
+
this._nameSetting = BMLayoutEditorSetting.settingWithName(name, {kind: BMLayoutEditorSettingKind.Title, target: this});
|
|
1117
|
+
this._nameSetting._section = this;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Controls whether this section can be collapsed and expanded.
|
|
1126
|
+
*/
|
|
1127
|
+
collapsible: NO, // <Boolean>
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Controls whether this section is collapsed.
|
|
1131
|
+
*/
|
|
1132
|
+
_collapsed: NO, // <Boolean>
|
|
1133
|
+
|
|
1134
|
+
get isCollapsed() {
|
|
1135
|
+
return this._collapsed;
|
|
1136
|
+
},
|
|
1137
|
+
set isCollapsed(collapsed) {
|
|
1138
|
+
if (this._tab) {
|
|
1139
|
+
if (collapsed) {
|
|
1140
|
+
this.collapse();
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
this.expand();
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
this._collapsed = collapsed;
|
|
1147
|
+
},
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* An array of settings belonging to this section. You should not directly add settings to this
|
|
1151
|
+
* array; instead you should assign to this property directly.
|
|
1152
|
+
*/
|
|
1153
|
+
_settings: undefined, // <[BMLayoutEditorSetting]>
|
|
1154
|
+
get settings() {
|
|
1155
|
+
return this._settings.slice();
|
|
1156
|
+
},
|
|
1157
|
+
set settings(settings) {
|
|
1158
|
+
this._settings = settings.slice();
|
|
1159
|
+
},
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Returns a copy of this section that contains additional settings for all defined and
|
|
1163
|
+
* automatically expanding variations.
|
|
1164
|
+
* @param sizeClass <BMLayoutSizeClass> The size class for which this section should be compiled.
|
|
1165
|
+
* @return <BMLayoutEditorSettingsSection> A settings section.
|
|
1166
|
+
*/
|
|
1167
|
+
_compiledSectionForSizeClass(sizeClass) {
|
|
1168
|
+
const section = BMLayoutEditorSettingsSection.section();
|
|
1169
|
+
|
|
1170
|
+
section._name = this._name;
|
|
1171
|
+
section._nameSetting = this._nameSetting;
|
|
1172
|
+
section.collapsible = section.collapsible;
|
|
1173
|
+
section._settings = [];
|
|
1174
|
+
|
|
1175
|
+
for (const setting of this._settings) {
|
|
1176
|
+
section._settings.push(setting);
|
|
1177
|
+
// If the setting supports variations, create additional associated settings for each defined variation
|
|
1178
|
+
if (setting.variations) {
|
|
1179
|
+
for (const key in setting.target._variations) {
|
|
1180
|
+
const variation = setting.target._variations[key];
|
|
1181
|
+
// When the setting supports automatically expanding variations, skip the current variation as a setting will be created for it regardless of whether
|
|
1182
|
+
// it had been previously defined or not
|
|
1183
|
+
if (variation.sizeClass == sizeClass && setting.automaticallyExpandsVariations) continue;
|
|
1184
|
+
|
|
1185
|
+
// If this variation contains the target property, create a setting for it
|
|
1186
|
+
if (setting.property in variation) {
|
|
1187
|
+
const variationSetting = BMLayoutEditorSetting.settingWithName(setting.name, {kind: setting.kind, target: setting.target, variations: NO, nullable: setting.nullable, property: setting.property});
|
|
1188
|
+
variationSetting._sizeClass = variation.sizeClass;
|
|
1189
|
+
variationSetting._associatedSetting = setting;
|
|
1190
|
+
section._settings.push(variationSetting);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// If the setting automatically expands variations, create setting for the current size class
|
|
1195
|
+
if (setting.automaticallyExpandsVariations && sizeClass) {
|
|
1196
|
+
const variationSetting = BMLayoutEditorSetting.settingWithName(setting.name, {kind: setting.kind, target: setting.target, variations: NO, nullable: YES, property: setting.property});
|
|
1197
|
+
variationSetting._sizeClass = sizeClass;
|
|
1198
|
+
variationSetting._associatedSetting = setting;
|
|
1199
|
+
section._settings.push(variationSetting);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
return section;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Constructs and returns a settings section.
|
|
1210
|
+
* @return <BMLayoutEditorSettingsSection> A section.
|
|
1211
|
+
*/
|
|
1212
|
+
BMLayoutEditorSettingsSection.section = function () {
|
|
1213
|
+
const section = new BMLayoutEditorSettingsSection;
|
|
1214
|
+
|
|
1215
|
+
section._settings = [];
|
|
1216
|
+
|
|
1217
|
+
return section;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Constructs and returns a section by copying the contents of the given section.
|
|
1222
|
+
*/
|
|
1223
|
+
BMLayoutEditorSettingsSection.sectionWithSection = function (section) {
|
|
1224
|
+
const newSection = new BMLayoutEditorSettingsSection;
|
|
1225
|
+
|
|
1226
|
+
newSection.name = section.name;
|
|
1227
|
+
newSection.collapsible = section.collapsible;
|
|
1228
|
+
newSection._settings = section._settings.slice();
|
|
1229
|
+
|
|
1230
|
+
return newSection;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// @endtype
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
// @type BMLayoutEditorSettingKind
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Constants representing the various kinds of settings that can be used by layout editor settings. These constants refer to the setting
|
|
1240
|
+
* kinds available, but custom setting kinds can be used by specifying a custom identifier string when creating a setting and registering
|
|
1241
|
+
* a setting cell class with the same name. Note that underscore prefixed identifiers are reserved by CoreUI.
|
|
1242
|
+
*/
|
|
1243
|
+
export var BMLayoutEditorSettingKind = Object.freeze({ // <enum>
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* A string setting kind.
|
|
1247
|
+
*/
|
|
1248
|
+
String: '_String', // <enum>
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* An integer setting kind.
|
|
1252
|
+
*/
|
|
1253
|
+
Integer: '_Integer', // <enum>
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* A number setting kind.
|
|
1257
|
+
*/
|
|
1258
|
+
Number: '_Number', // <enum>
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* A boolean setting kind.
|
|
1262
|
+
*/
|
|
1263
|
+
Boolean: '_Boolean', // <enum>
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* A `BMSize` setting kind.
|
|
1267
|
+
*/
|
|
1268
|
+
Size: '_BMSize', // <enum>
|
|
1269
|
+
|
|
1270
|
+
/**
|
|
1271
|
+
* A setting kind representing the possible values for a constraint constant.
|
|
1272
|
+
*/
|
|
1273
|
+
Constant: '_Constant', // <enum>
|
|
1274
|
+
|
|
1275
|
+
/**
|
|
1276
|
+
* A setting kind representing a constraint.
|
|
1277
|
+
*/
|
|
1278
|
+
Constraint: '_Constraint', // <enum>
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* A setting kind representing a clickable link to a view.
|
|
1282
|
+
*/
|
|
1283
|
+
View: '_View', // <enum>
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* A setting kind representing a list of possible options that is a presented as a dropdown menu.
|
|
1287
|
+
*/
|
|
1288
|
+
Enum: '_Enum', // <enum>
|
|
1289
|
+
|
|
1290
|
+
/**
|
|
1291
|
+
* A setting kind representing a list of possible options that is presented as a segmented control.
|
|
1292
|
+
*/
|
|
1293
|
+
Segment: '_Segment', // <enum>
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* A setting kind that represents a non-editable string.
|
|
1297
|
+
*/
|
|
1298
|
+
Info: '_Info', // <enum>
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* A setting kind that represents a non-editable section title.
|
|
1302
|
+
*/
|
|
1303
|
+
Title: '_Title', // <enum>
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* A setting kind that represents an insets object.
|
|
1307
|
+
*/
|
|
1308
|
+
Insets: '_Insets', // <enum>
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* A setting kind that represents the deactivate constraint button.
|
|
1312
|
+
*/
|
|
1313
|
+
DeactivateConstraintsButton: '_DeactivateConstraintsButton', // <enum>
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* A setting kind that represents the delete constraint button.
|
|
1317
|
+
*/
|
|
1318
|
+
DeleteConstraintButton: '_DeleteConstraintButton', // <enum>
|
|
1319
|
+
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// @endtype
|
|
1323
|
+
|
|
1324
|
+
// @type BMLayoutEditorSetting
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Represents a setting that can be displayed and modified in the layout editor.
|
|
1328
|
+
*/
|
|
1329
|
+
export function BMLayoutEditorSetting() {} // <constructor>
|
|
1330
|
+
|
|
1331
|
+
BMLayoutEditorSetting.prototype = {
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* The name of this setting.
|
|
1335
|
+
*/
|
|
1336
|
+
name: undefined, // <String>
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* The data kind.
|
|
1340
|
+
*/
|
|
1341
|
+
kind: BMLayoutEditorSettingKind.String, // <BMLayoutEditorSettingKind or String>
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* Should be set to `YES` for settings that support null values.
|
|
1345
|
+
* Note that some data kinds such as `String` cannot be set to null through the editor.
|
|
1346
|
+
*/
|
|
1347
|
+
nullable: NO, // <Boolean>
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* Should be set to `YES` for settings that support variations.
|
|
1351
|
+
*/
|
|
1352
|
+
variations: NO, // <Boolean>
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* When set to `YES`, selecting a size class within the layout editor will cause a variation setting to be
|
|
1356
|
+
* automatically generated for this setting.
|
|
1357
|
+
*/
|
|
1358
|
+
automaticallyExpandsVariations: NO, // <Boolean>
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* When the base setting supports variations and this setting represents a variation of it,
|
|
1362
|
+
* this property indicates the size class to which the variation applies.
|
|
1363
|
+
*/
|
|
1364
|
+
_sizeClass: undefined, // <BMLayoutSizeClass, nullable>
|
|
1365
|
+
|
|
1366
|
+
get sizeClass() {
|
|
1367
|
+
return this._sizeClass;
|
|
1368
|
+
},
|
|
1369
|
+
|
|
1370
|
+
/**
|
|
1371
|
+
* When the base setting supports variations and this setting represents a variation of it,
|
|
1372
|
+
* this property indicates the base setting from which this setting was generated.
|
|
1373
|
+
*/
|
|
1374
|
+
_associatedProperty: undefined, // <BMLayoutEditorSetting, nullable>
|
|
1375
|
+
|
|
1376
|
+
get associatedProperty() {
|
|
1377
|
+
return this._associatedProperty;
|
|
1378
|
+
},
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* The object that represents the target of this setting.
|
|
1382
|
+
*/
|
|
1383
|
+
target: undefined, // <AnyObject>
|
|
1384
|
+
|
|
1385
|
+
/**
|
|
1386
|
+
* The property that this setting manages. The value of this property
|
|
1387
|
+
* will be passed to the target object whenever the value of this setting changes.
|
|
1388
|
+
*/
|
|
1389
|
+
property: undefined, // <String, nullable>
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Initializes this setting with the given name, data kind and target object.
|
|
1393
|
+
* @param name <String> The name of the setting.
|
|
1394
|
+
* {
|
|
1395
|
+
* @param kind <BMLayoutEditorSettingKind or String> The data kind.
|
|
1396
|
+
* @param target <AnyObject> The target object of the setting.
|
|
1397
|
+
* @param variations <Boolean, nullable> Defaults to `NO`. Controls whether this setting supports variations.
|
|
1398
|
+
* @param nullable <Boolean, nullable> Defaults to `NO`. Controls whether this setting supports null value.
|
|
1399
|
+
* @param property <String, nullable> If specified, this represents the property on the target object which is controlled by this
|
|
1400
|
+
* setting. This value is not directly by the layout editor but can be used by the target object.
|
|
1401
|
+
* }
|
|
1402
|
+
* @return <BMLayoutEditorSetting> This setting.
|
|
1403
|
+
*/
|
|
1404
|
+
initWithName(name, {kind, target, variations = NO, nullable = NO, property = undefined}) {
|
|
1405
|
+
this.name = name;
|
|
1406
|
+
this.kind = kind;
|
|
1407
|
+
this.target = target;
|
|
1408
|
+
|
|
1409
|
+
this.variations = variations;
|
|
1410
|
+
this.nullable = nullable;
|
|
1411
|
+
if (property !== undefined) this.property = property;
|
|
1412
|
+
|
|
1413
|
+
return this;
|
|
1414
|
+
},
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Tests whether this setting and the given setting are equivalent.
|
|
1418
|
+
* Two settings are equivalent if they have the same name, target object, target property and size class.
|
|
1419
|
+
* @param setting <BMLayoutEditorSetting> The setting to test against.
|
|
1420
|
+
* @return <Boolean> `YES` if the settings are equivalent, `NO` otherwise.
|
|
1421
|
+
*/
|
|
1422
|
+
isEqualToSetting(setting) {
|
|
1423
|
+
const result = setting.name == this.name && setting.target == this.target && setting._sizeClass == this._sizeClass && setting.property == this.property;
|
|
1424
|
+
return result;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Constructs and returns a setting with the given name, data kind and target object.
|
|
1430
|
+
* @param name <String> The name of the setting.
|
|
1431
|
+
* {
|
|
1432
|
+
* @param kind <BMLayoutEditorSettingKind> The data kind.
|
|
1433
|
+
* @param target <AnyObject> The target object of the setting.
|
|
1434
|
+
* @param variations <Boolean, nullable> Defaults to `NO`. Controls whether this setting supports variations.
|
|
1435
|
+
* @param nullable <Boolean, nullable> Defaults to `NO`. Controls whether this setting supports null value.
|
|
1436
|
+
* @param property <String, nullable> If specified, this represents the property on the target object which is controlled by this
|
|
1437
|
+
* setting. This value is not directly by the layout editor but can be used by the target object.
|
|
1438
|
+
* }
|
|
1439
|
+
* @return <BMLayoutEditorSetting> A setting.
|
|
1440
|
+
*/
|
|
1441
|
+
BMLayoutEditorSetting.settingWithName = function (name, {kind, target, variations = NO, nullable = NO, property = undefined}) {
|
|
1442
|
+
return (new this).initWithName(name, {kind, target, variations, nullable, property});
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// @endtype
|
|
1446
|
+
|
|
1447
|
+
// @type BMLayoutEditorEnumSetting extends BMLayoutEditorSetting
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Represents a setting that can be displayed and modified in the layout editor.
|
|
1451
|
+
* This setting also specifies a number of possible options via the `options` property.
|
|
1452
|
+
*
|
|
1453
|
+
* Settings created with this class must have their `kind` property set to `.Enum` or `.Segment`.
|
|
1454
|
+
*/
|
|
1455
|
+
export function BMLayoutEditorEnumSetting() {} // <constructor>
|
|
1456
|
+
|
|
1457
|
+
BMLayoutEditorEnumSetting.prototype = BMExtend(Object.create(BMLayoutEditorSetting.prototype), {
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* An array of menu items that define which options are available for this setting.
|
|
1461
|
+
*
|
|
1462
|
+
* The `userInfo` property of these menu items must be set to the value of the property that each item
|
|
1463
|
+
* represents.
|
|
1464
|
+
*
|
|
1465
|
+
* The `action` property of these objects will be ignored.
|
|
1466
|
+
*/
|
|
1467
|
+
options: undefined, // <[BMMenuItem]>
|
|
1468
|
+
|
|
1469
|
+
initWithName(name, args) {
|
|
1470
|
+
BMLayoutEditorSetting.prototype.initWithName.apply(this, arguments);
|
|
1471
|
+
|
|
1472
|
+
this.options = [];
|
|
1473
|
+
|
|
1474
|
+
return this;
|
|
1475
|
+
}
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
BMLayoutEditorEnumSetting.settingWithName = BMLayoutEditorSetting.settingWithName;
|
|
1479
|
+
|
|
1480
|
+
// @endtype BMLayoutEditorEnumSetting
|