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,1653 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
|
|
3
|
+
import { BMExtend, BMStringByCapitalizingString, YES, NO } from "../Core/BMCoreUI";
|
|
4
|
+
import { BMCollectionViewCell } from "../BMCollectionView/BMCollectionViewCell";
|
|
5
|
+
import { BMLayoutAttribute } from "../BMView/BMLayoutConstraint_v2.5";
|
|
6
|
+
import { _BMURLOfImageAtPath } from "./BMLayoutEditorSettings";
|
|
7
|
+
import { BMView } from "../BMView/BMView_v2.5";
|
|
8
|
+
import { BMLayoutSizeClass, BMLayoutOrientation } from "../BMView/BMLayoutSizeClass";
|
|
9
|
+
import { BMPointMake } from "../Core/BMPoint";
|
|
10
|
+
import { BMMenuKind } from "../BMView/BMMenu";
|
|
11
|
+
import { BMRectMakeWithNodeFrame } from "../Core/BMRect";
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const BMLayoutEdittorSettingCellRowSpacing = 2;
|
|
15
|
+
|
|
16
|
+
// @type BMLayoutEditorSettingsTitleView extends BMView
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A view subclass used by layout editor setting cells as the setting title.
|
|
20
|
+
* In addition to displaying the cell's title, it also manages adding and removing variations
|
|
21
|
+
* for the setting.
|
|
22
|
+
*/
|
|
23
|
+
export function BMLayoutEditorSettingsTitleView() {} // <constructor>
|
|
24
|
+
|
|
25
|
+
BMLayoutEditorSettingsTitleView.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
26
|
+
constructor: BMLayoutEditorSettingsTitleView,
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The view displaying the title text.
|
|
30
|
+
*/
|
|
31
|
+
_titleLabel: undefined, // <BMView>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The view displaying the size class badge, if there is one.
|
|
35
|
+
*/
|
|
36
|
+
_badge: undefined, // <BMView>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The view displaying the variations button, if there is one.
|
|
40
|
+
*/
|
|
41
|
+
_button: undefined, // <BMView>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The string to display as a title.
|
|
45
|
+
*/
|
|
46
|
+
_title: undefined, // <String>
|
|
47
|
+
get title() {
|
|
48
|
+
return this._title;
|
|
49
|
+
},
|
|
50
|
+
set title(title) {
|
|
51
|
+
this._title = title;
|
|
52
|
+
this._titleLabel.node.innerText = title;
|
|
53
|
+
this._titleLabel.invalidateIntrinsicSize();
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The setting displayed by this title view.
|
|
58
|
+
*/
|
|
59
|
+
_setting: undefined, // <BMLayoutEditorSetting, nullable>
|
|
60
|
+
get setting() {
|
|
61
|
+
return this._setting;
|
|
62
|
+
},
|
|
63
|
+
set setting(setting) {
|
|
64
|
+
this._setting = setting;
|
|
65
|
+
|
|
66
|
+
if (setting.sizeClass) {
|
|
67
|
+
switch (setting.sizeClass) {
|
|
68
|
+
case BMLayoutSizeClass.phoneSizeClass():
|
|
69
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImagePhoneMini';
|
|
70
|
+
break;
|
|
71
|
+
case BMLayoutSizeClass.phoneSizeClassWithOrientation(BMLayoutOrientation.Portrait):
|
|
72
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImagePhonePortraitMini';
|
|
73
|
+
break;
|
|
74
|
+
case BMLayoutSizeClass.phoneSizeClassWithOrientation(BMLayoutOrientation.Landscape):
|
|
75
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImagePhoneLandscapeMini';
|
|
76
|
+
break;
|
|
77
|
+
case BMLayoutSizeClass.tabletSizeClass():
|
|
78
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImageTabletMini';
|
|
79
|
+
break;
|
|
80
|
+
case BMLayoutSizeClass.tabletSizeClassWithOrientation(BMLayoutOrientation.Portrait):
|
|
81
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImageTabletPortraitMini';
|
|
82
|
+
break;
|
|
83
|
+
case BMLayoutSizeClass.tabletSizeClassWithOrientation(BMLayoutOrientation.Landscape):
|
|
84
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadgeIcon BMLayoutEditorImageTabletLandscapeMini';
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
this._badge.node.className = 'BMLayoutEditorDetailsItemBadge';
|
|
88
|
+
this._badge.node.innerText = setting.sizeClass._hashString;
|
|
89
|
+
}
|
|
90
|
+
this._button.node.innerHTML = '<i class="material-icons">remove_circle</i>';
|
|
91
|
+
this._button.isVisible = YES;
|
|
92
|
+
|
|
93
|
+
this._badgeWidthConstraint.constant = 16;
|
|
94
|
+
this._buttonWidthConstraint.constant = 24;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this._badge.node.className = '';
|
|
98
|
+
if (setting.variations) {
|
|
99
|
+
this._button.node.innerHTML = '<i class="material-icons">add_circle</i>';
|
|
100
|
+
this._button.isVisible = YES;
|
|
101
|
+
this._buttonWidthConstraint.constant = 24;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this._button.node.innerHTML = '';
|
|
105
|
+
this._button.isVisible = NO;
|
|
106
|
+
this._buttonWidthConstraint.constant = 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this._badgeWidthConstraint.constant = 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this._badge.invalidateIntrinsicSize();
|
|
113
|
+
this.title = setting.name + ':';
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
initWithDOMNode() {
|
|
117
|
+
BMView.prototype.initWithDOMNode.apply(this, arguments);
|
|
118
|
+
this.debuggingName = 'TitleView';
|
|
119
|
+
|
|
120
|
+
// Create and configure the label text.
|
|
121
|
+
const label = this._titleLabel = BMView.view();
|
|
122
|
+
label.debuggingName = 'TitleViewLabel';
|
|
123
|
+
label.supportsAutomaticIntrinsicSize = YES;
|
|
124
|
+
label.node.className = 'BMLayoutEditorDetailsTitleCell';
|
|
125
|
+
this.addSubview(label);
|
|
126
|
+
label.trailing.equalTo(this.trailing).isActive = YES;
|
|
127
|
+
label.top.equalTo(this.top).isActive = YES;
|
|
128
|
+
label.bottom.equalTo(this.bottom).isActive = YES;
|
|
129
|
+
|
|
130
|
+
label.compressionResistance = 700;
|
|
131
|
+
|
|
132
|
+
// Create and configure the size class badge
|
|
133
|
+
const badge = this._badge = BMView.view();
|
|
134
|
+
badge.debuggingName = 'TitleViewBadge';
|
|
135
|
+
badge.supportsAutomaticIntrinsicSize = YES;
|
|
136
|
+
this.addSubview(badge);
|
|
137
|
+
label.leading.equalTo(badge.trailing, {plus: 4}).isActive = YES;
|
|
138
|
+
badge.centerY.equalTo(label.centerY).isActive = YES;
|
|
139
|
+
this._badgeWidthConstraint = badge.width.greaterThanOrEqualTo(16);
|
|
140
|
+
this._badgeWidthConstraint.isActive = YES;
|
|
141
|
+
badge.height.greaterThanOrEqualTo(16).isActive = YES;
|
|
142
|
+
|
|
143
|
+
// Create and configure the variations button
|
|
144
|
+
const button = this._button = BMView.view();
|
|
145
|
+
button.debuggingName = 'TitleViewButton';
|
|
146
|
+
button.node.className = 'BMLayoutEditorDetailsItemVariationsButton';
|
|
147
|
+
this.addSubview(button);
|
|
148
|
+
button.leading.equalTo(this.leading).isActive = YES;
|
|
149
|
+
badge.leading.greaterThanOrEqualTo(button.trailing, {plus: 4}).isActive = YES;
|
|
150
|
+
button.centerY.equalTo(label.centerY).isActive = YES;
|
|
151
|
+
this._buttonWidthConstraint = button.width.equalTo(24);
|
|
152
|
+
this._buttonWidthConstraint.isActive = YES;
|
|
153
|
+
button.height.equalTo(24).isActive = YES;
|
|
154
|
+
|
|
155
|
+
button.node.addEventListener('click', e => this.variationsButtonClickedWithEvent(e));
|
|
156
|
+
|
|
157
|
+
return this;
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Invoked when the user clicks on the variations button.
|
|
162
|
+
* @param event <Event> The event that triggered this action.
|
|
163
|
+
*/
|
|
164
|
+
variationsButtonClickedWithEvent(event) {
|
|
165
|
+
if (this.setting.sizeClass) {
|
|
166
|
+
this.setting.target[`remove${BMStringByCapitalizingString(this.setting.property)}VariationForSizeClass`](this.setting.sizeClass);
|
|
167
|
+
// This is necessary in the case of removing variations for constant or isActive in the case of selected constraint for the currently selected size class
|
|
168
|
+
this.rootView.setting = this.rootView.setting;
|
|
169
|
+
this.rootView.tab.updateSettings();
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// TODO check if a variation already exists for this size class; ideally filter the menu
|
|
173
|
+
this.rootView.layoutEditor._showSizeClassMenuAtPoint(BMPointMake(event.clientX, event.clientY), {kind: BMMenuKind.Menu, action: sizeClass => {
|
|
174
|
+
// Skip adding a new variation for a size class when it has already been defined
|
|
175
|
+
if (this.setting.target._variations && this.setting.target._variations[sizeClass] && this.setting.property in this.setting.target._variations[sizeClass]) return;
|
|
176
|
+
|
|
177
|
+
// Create the variation, then reload the tab contents
|
|
178
|
+
this.setting.target[`set${BMStringByCapitalizingString(this.setting.property)}`](this.setting.target[this.setting.property], {forSizeClass: sizeClass});
|
|
179
|
+
this.rootView.tab.updateSettings();
|
|
180
|
+
}});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Constructs and returns a title view.
|
|
187
|
+
* @return <BMLayoutEditorSettingsTitleView> A title view.
|
|
188
|
+
*/
|
|
189
|
+
BMLayoutEditorSettingsTitleView.titleView = function () {
|
|
190
|
+
return (new BMLayoutEditorSettingsTitleView).initWithDOMNode(document.createElement('div'));
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// @endtype
|
|
194
|
+
|
|
195
|
+
// @type BMLayoutEditorSettingsFooter extends BMCollectionViewCell
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The settings footer is a specialized subclass of collection view cell that is used by the layout editor
|
|
199
|
+
* to display section separators.
|
|
200
|
+
*/
|
|
201
|
+
export function BMLayoutEditorSettingsFooter() {} // <constructor>
|
|
202
|
+
|
|
203
|
+
BMLayoutEditorSettingsFooter.prototype = BMExtend(Object.create(BMCollectionViewCell.prototype), {
|
|
204
|
+
constructor: BMLayoutEditorSettingsFooter,
|
|
205
|
+
|
|
206
|
+
initWithCollectionView() {
|
|
207
|
+
BMCollectionViewCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
208
|
+
|
|
209
|
+
const contentView = BMView.view();
|
|
210
|
+
this.addSubview(contentView);
|
|
211
|
+
|
|
212
|
+
contentView.leading.equalTo(this.leading).isActive = YES;
|
|
213
|
+
contentView.trailing.equalTo(this.trailing).isActive = YES;
|
|
214
|
+
contentView.top.equalTo(this.top).isActive = YES;
|
|
215
|
+
contentView.bottom.equalTo(this.bottom).isActive = YES;
|
|
216
|
+
|
|
217
|
+
const dividerView = BMView.view();
|
|
218
|
+
contentView.addSubview(dividerView);
|
|
219
|
+
dividerView.node.className = 'BMLayoutEditorDetailsDividerCell';
|
|
220
|
+
|
|
221
|
+
dividerView.leading.equalTo(contentView.leading).isActive = YES;
|
|
222
|
+
dividerView.trailing.equalTo(contentView.trailing).isActive = YES;
|
|
223
|
+
dividerView.height.equalTo(1).isActive = YES;
|
|
224
|
+
dividerView.centerY.equalTo(contentView.centerY).isActive = YES;
|
|
225
|
+
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// @endtype
|
|
231
|
+
|
|
232
|
+
// @type BMLayoutEditorSettingsCell extends BMCollectionViewCell
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The settings cell is a specialized subclass of collection view cell that is used by the layout editor
|
|
236
|
+
* to display view and constraint settings.
|
|
237
|
+
*
|
|
238
|
+
* The settings cell class itself is never used directly; instead, dependending on the type of the setting
|
|
239
|
+
* an appropriate subclass is used.
|
|
240
|
+
*
|
|
241
|
+
* For settings where the built in cell types are not sufficient, this class serves as the base setting class
|
|
242
|
+
* that can be subclassed to provide custom settings interfaces.
|
|
243
|
+
*/
|
|
244
|
+
export function BMLayoutEditorSettingsCell() {} // <constructor>
|
|
245
|
+
|
|
246
|
+
BMLayoutEditorSettingsCell.prototype = BMExtend(Object.create(BMCollectionViewCell.prototype), {
|
|
247
|
+
constructor: BMLayoutEditorSettingsCell,
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The view to which this cell's contents should be added.
|
|
251
|
+
*/
|
|
252
|
+
_contentView: undefined, // <BMView>
|
|
253
|
+
|
|
254
|
+
get contentView() {
|
|
255
|
+
return this._contentView;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
initWithCollectionView() {
|
|
259
|
+
BMCollectionViewCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
260
|
+
|
|
261
|
+
const contentView = BMView.view();
|
|
262
|
+
contentView.debuggingName = 'ContentView';
|
|
263
|
+
this._contentView = contentView;
|
|
264
|
+
this.addSubview(contentView);
|
|
265
|
+
|
|
266
|
+
if (this.constructor === BMLayoutEditorSettingsCell) {
|
|
267
|
+
contentView.node.innerText = 'This is a test!';
|
|
268
|
+
|
|
269
|
+
contentView.node.style.color = 'white';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
contentView.leading.equalTo(this.leading).isActive = YES;
|
|
273
|
+
contentView.trailing.equalTo(this.trailing).isActive = YES;
|
|
274
|
+
contentView.top.equalTo(this.top).isActive = YES;
|
|
275
|
+
contentView.bottom.equalTo(this.bottom).isActive = YES;
|
|
276
|
+
|
|
277
|
+
contentView.height.greaterThanOrEqualTo(32, {priority: 750}).isActive = YES;
|
|
278
|
+
|
|
279
|
+
return this;
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The setting displayed by this cell.
|
|
284
|
+
*/
|
|
285
|
+
_setting: undefined, // <BMLayoutEditorSetting>
|
|
286
|
+
|
|
287
|
+
get setting() {
|
|
288
|
+
return this._setting;
|
|
289
|
+
},
|
|
290
|
+
set setting(setting) {
|
|
291
|
+
this._setting = setting;
|
|
292
|
+
this.cellDidBindToSetting(setting);
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The settings tab displaying this cell.
|
|
297
|
+
* This property is typically not available during construction, but will
|
|
298
|
+
* become available after this cell has been bound to a setting.
|
|
299
|
+
*/
|
|
300
|
+
_tab: undefined, // <BMLayoutEditorSettingsTab, nullable>
|
|
301
|
+
|
|
302
|
+
get tab() {
|
|
303
|
+
return this._tab;
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The settings view displaying this cell.
|
|
308
|
+
* This property is typically not available during construction, but will
|
|
309
|
+
* become available after this cell has been bound to a setting.
|
|
310
|
+
*/
|
|
311
|
+
get settingsView() { // <_BMLayoutEditorSettingsView, nullable>
|
|
312
|
+
const tab = this.tab;
|
|
313
|
+
if (tab) {
|
|
314
|
+
return tab._settingsPanel._settingsView;
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The layout editor displaying this cell.
|
|
320
|
+
* This property is typically not available during construction, but will
|
|
321
|
+
* become available after this cell has been bound to a setting.
|
|
322
|
+
*/
|
|
323
|
+
get layoutEditor() { // <BMLayoutEditor, nullable>
|
|
324
|
+
const tab = this.tab;
|
|
325
|
+
if (tab) {
|
|
326
|
+
return tab.layoutEditor;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Invoked when the setting displayed by this cell changes.
|
|
332
|
+
* Subclasses can use this method to update their display to match the new setting.
|
|
333
|
+
* The default implementation does nothing.
|
|
334
|
+
* @param setting <BMLayoutEditorSetting> The new setting.
|
|
335
|
+
*/
|
|
336
|
+
cellDidBindToSetting(setting) {
|
|
337
|
+
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// @endtype
|
|
342
|
+
|
|
343
|
+
// @type BMLayoutEditorSettingsConstraintCell extends BMLayoutEditorSettingsCell
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* A cell type specialized for displaying constraints.
|
|
347
|
+
*/
|
|
348
|
+
export function BMLayoutEditorSettingsConstraintCell() {} // <constructor>
|
|
349
|
+
|
|
350
|
+
BMLayoutEditorSettingsConstraintCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
351
|
+
constructor: BMLayoutEditorSettingsConstraintCell,
|
|
352
|
+
|
|
353
|
+
_constraintView: undefined,
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* The constraint view leading layout constraint.
|
|
357
|
+
*/
|
|
358
|
+
_constraintViewLeading: undefined, // <BMLayoutConstraint>
|
|
359
|
+
|
|
360
|
+
initWithCollectionView() {
|
|
361
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
362
|
+
|
|
363
|
+
const constraintView = BMView.view();
|
|
364
|
+
constraintView.debuggingName = 'ConstraintView';
|
|
365
|
+
this._constraintView = constraintView;
|
|
366
|
+
this.contentView.addSubview(constraintView);
|
|
367
|
+
constraintView.supportsAutomaticIntrinsicSize = YES;
|
|
368
|
+
|
|
369
|
+
this._constraintViewLeading = constraintView.leading.equalTo(this.leading, {plus: 8});
|
|
370
|
+
this._constraintViewLeading.isActive = YES;
|
|
371
|
+
constraintView.trailing.equalTo(this.trailing, {plus: -8}).isActive = YES;
|
|
372
|
+
constraintView.top.equalTo(this.top, {plus: 8}).isActive = YES;
|
|
373
|
+
constraintView.bottom.equalTo(this.bottom).isActive = YES;
|
|
374
|
+
|
|
375
|
+
constraintView.node.className = 'BMLayoutEditorDetailsConstraintItem BMLayoutEditorDetailsConstraintItemCell BMCollectionViewCellEventHandler';
|
|
376
|
+
|
|
377
|
+
constraintView.node.addEventListener('click', event => {
|
|
378
|
+
const constraint = this.setting.target;
|
|
379
|
+
if (!constraint._internal) this.settingsView.selectConstraint(constraint, {withReferenceView: this.tab._displayedView})
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return this;
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
// @override - BMCollectionViewCell
|
|
386
|
+
cellDidBindToSetting(setting) {
|
|
387
|
+
const constraintNode = this._constraintView.node;
|
|
388
|
+
const constraint = setting.target;
|
|
389
|
+
|
|
390
|
+
constraintNode.innerText = constraint.descriptionRelativeToView(this.tab._displayedView);
|
|
391
|
+
|
|
392
|
+
const icon = document.createElement('img');
|
|
393
|
+
icon.className = 'BMLayoutEditorDetailsConstraintIcon';
|
|
394
|
+
|
|
395
|
+
if (constraint.isConstraintCollection) {
|
|
396
|
+
icon.src = _BMURLOfImageAtPath('images/OwnConstraints.png');
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
const attribute = constraint._sourceView == this.tab._displayedView ? '_sourceViewAttribute' : '_targetViewAttribute';
|
|
400
|
+
switch (constraint[attribute]) {
|
|
401
|
+
case BMLayoutAttribute.Left:
|
|
402
|
+
case BMLayoutAttribute.Leading:
|
|
403
|
+
icon.src = _BMURLOfImageAtPath('images/LeftConstraint.png');
|
|
404
|
+
break;
|
|
405
|
+
case BMLayoutAttribute.Right:
|
|
406
|
+
case BMLayoutAttribute.Trailing:
|
|
407
|
+
icon.src = _BMURLOfImageAtPath('images/RightConstraint.png');
|
|
408
|
+
break;
|
|
409
|
+
case BMLayoutAttribute.CenterX:
|
|
410
|
+
icon.src = _BMURLOfImageAtPath('images/CenterXConstraint.png');
|
|
411
|
+
break;
|
|
412
|
+
case BMLayoutAttribute.CenterY:
|
|
413
|
+
icon.src = _BMURLOfImageAtPath('images/CenterYConstraint.png');
|
|
414
|
+
break;
|
|
415
|
+
case BMLayoutAttribute.Top:
|
|
416
|
+
icon.src = _BMURLOfImageAtPath('images/TopConstraint.png');
|
|
417
|
+
break;
|
|
418
|
+
case BMLayoutAttribute.Bottom:
|
|
419
|
+
icon.src = _BMURLOfImageAtPath('images/BottomConstraint.png');
|
|
420
|
+
break;
|
|
421
|
+
case BMLayoutAttribute.Width:
|
|
422
|
+
icon.src = _BMURLOfImageAtPath('images/WidthConstraint.png');
|
|
423
|
+
break;
|
|
424
|
+
case BMLayoutAttribute.Height:
|
|
425
|
+
icon.src = _BMURLOfImageAtPath('images/HeightConstraint.png');
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
constraintNode.insertBefore(icon, constraintNode.firstChild);
|
|
431
|
+
|
|
432
|
+
if (constraint._priority > 500 && constraint._priority < 1000) {
|
|
433
|
+
constraintNode.classList.add('BMLayoutEditorDetailsConstraintItemStrong');
|
|
434
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemWeak');
|
|
435
|
+
}
|
|
436
|
+
else if (constraint._priority <= 500) {
|
|
437
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemStrong');
|
|
438
|
+
constraintNode.classList.add('BMLayoutEditorDetailsConstraintItemWeak');
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemStrong');
|
|
442
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemWeak');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (constraint._internal) {
|
|
446
|
+
constraintNode.classList.add('BMLayoutEditorDetailsConstraintItemReadonly');
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemReadonly');
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (!constraint._configuration.isActive) {
|
|
453
|
+
constraintNode.classList.add('BMLayoutEditorDetailsConstraintItemInactive');
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
constraintNode.classList.remove('BMLayoutEditorDetailsConstraintItemInactive');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
this._constraintView.invalidateIntrinsicSize();
|
|
460
|
+
|
|
461
|
+
return constraintNode;
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
// @endtype
|
|
466
|
+
|
|
467
|
+
// @type BMLayoutEditorSettingsDeactivateConstraintsCell extends BMLayoutEditorSettingsCell
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* A cell type specialized for displaying constraints.
|
|
471
|
+
*/
|
|
472
|
+
export function BMLayoutEditorSettingsDeactivateConstraintsCell() {} // <constructor>
|
|
473
|
+
|
|
474
|
+
BMLayoutEditorSettingsDeactivateConstraintsCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
475
|
+
constructor: BMLayoutEditorSettingsDeactivateConstraintsCell,
|
|
476
|
+
|
|
477
|
+
_buttonView: undefined,
|
|
478
|
+
|
|
479
|
+
prepareForDisplay() {
|
|
480
|
+
window.addEventListener('keydown', this._keyboardModifier, YES);
|
|
481
|
+
window.addEventListener('keyup', this._keyboardModifier, YES);
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
prepareForReuse() {
|
|
485
|
+
window.removeEventListener('keydown', this._keyboardModifier, YES);
|
|
486
|
+
window.removeEventListener('keyup', this._keyboardModifier, YES);
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
invalidate() {
|
|
490
|
+
window.removeEventListener('keydown', this._keyboardModifier, YES);
|
|
491
|
+
window.removeEventListener('keyup', this._keyboardModifier, YES);
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
initWithCollectionView() {
|
|
495
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
496
|
+
|
|
497
|
+
const buttonView = BMView.view();
|
|
498
|
+
buttonView.debuggingName = 'DeactivateButton';
|
|
499
|
+
this._buttonView = buttonView;
|
|
500
|
+
this.contentView.addSubview(buttonView);
|
|
501
|
+
buttonView.supportsAutomaticIntrinsicSize = YES;
|
|
502
|
+
|
|
503
|
+
buttonView.leading.equalTo(this.contentView.leading, {plus: 32}).isActive = YES;
|
|
504
|
+
buttonView.trailing.equalTo(this.contentView.trailing, {plus: -32}).isActive = YES;
|
|
505
|
+
buttonView.top.equalTo(this.contentView.top, {plus: 8}).isActive = YES;
|
|
506
|
+
buttonView.bottom.equalTo(this.contentView.bottom, {plus: -8}).isActive = YES;
|
|
507
|
+
|
|
508
|
+
buttonView.node.className = 'BMWindowButton BMCollectionViewCellEventHandler';
|
|
509
|
+
buttonView.node.style.boxSizing = 'border-box';
|
|
510
|
+
buttonView.node.style.textAlign = 'center';
|
|
511
|
+
buttonView.node.style.pointer = 'default';
|
|
512
|
+
buttonView.node.style.margin = 0;
|
|
513
|
+
buttonView.node.innerText = 'Deactivate Constraints';
|
|
514
|
+
|
|
515
|
+
let hasOption = NO;
|
|
516
|
+
let hasShift = NO;
|
|
517
|
+
let hasCtrl = NO;
|
|
518
|
+
|
|
519
|
+
buttonView.node.addEventListener('click', event => {
|
|
520
|
+
if (hasOption && hasShift && hasCtrl) {
|
|
521
|
+
new Set(this.setting.target.rootView.allConstraints).forEach(constraint => constraint.remove());
|
|
522
|
+
}
|
|
523
|
+
else if (hasOption) {
|
|
524
|
+
for (let constraint of this.setting.target.localConstraints) {
|
|
525
|
+
if (hasShift) {
|
|
526
|
+
constraint.remove();
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
if (this._activeSizeClass) {
|
|
530
|
+
constraint.setIsActive(NO, {forSizeClass: this._activeSizeClass});
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
constraint.isActive = NO;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const constraintCategoryMap = this.tab._constraintCategories;
|
|
540
|
+
Object.keys(constraintCategoryMap).forEach(key => {
|
|
541
|
+
for (let constraint of constraintCategoryMap[key]) {
|
|
542
|
+
if (hasShift) {
|
|
543
|
+
constraint.remove();
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
if (this.layoutEditor._activeSizeClass) {
|
|
547
|
+
constraint.setIsActive(NO, {forSizeClass: this.layoutEditor._activeSizeClass});
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
constraint.isActive = NO;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
this.tab._updateSettings();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
buttonView.node.addEventListener('contextmenu', event => event.preventDefault());
|
|
561
|
+
|
|
562
|
+
this._keyboardModifier = event => {
|
|
563
|
+
switch (event.key) {
|
|
564
|
+
case 'Alt':
|
|
565
|
+
hasOption = (event.type == 'keydown');
|
|
566
|
+
break;
|
|
567
|
+
case 'Shift':
|
|
568
|
+
hasShift = (event.type == 'keydown');
|
|
569
|
+
break;
|
|
570
|
+
case 'Control':
|
|
571
|
+
hasCtrl = (event.type == 'keydown');
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (hasOption) {
|
|
576
|
+
if (hasShift) {
|
|
577
|
+
if (hasCtrl) {
|
|
578
|
+
buttonView.node.innerText = 'Reset Layout';
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
buttonView.node.innerText = 'Remove All Constraints';
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
buttonView.node.innerText = 'Deactivate All Constraints';
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else if (hasShift) {
|
|
589
|
+
buttonView.node.innerText = 'Remove Constraints';
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
buttonView.node.innerText = 'Deactivate Constraints';
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
return this;
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
// @override - BMCollectionViewCell
|
|
600
|
+
cellDidBindToSetting(setting) {
|
|
601
|
+
}
|
|
602
|
+
})
|
|
603
|
+
|
|
604
|
+
// @endtype
|
|
605
|
+
|
|
606
|
+
// @type BMLayoutEditorSettingsDeleteConstraintCell extends BMLayoutEditorSettingsCell
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* A cell type specialized for displaying constraints.
|
|
610
|
+
*/
|
|
611
|
+
export function BMLayoutEditorSettingsDeleteConstraintCell() {} // <constructor>
|
|
612
|
+
|
|
613
|
+
BMLayoutEditorSettingsDeleteConstraintCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
614
|
+
constructor: BMLayoutEditorSettingsDeleteConstraintCell,
|
|
615
|
+
|
|
616
|
+
_buttonView: undefined,
|
|
617
|
+
|
|
618
|
+
initWithCollectionView() {
|
|
619
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
620
|
+
|
|
621
|
+
const buttonView = BMView.view();
|
|
622
|
+
this._buttonView = buttonView;
|
|
623
|
+
this.contentView.addSubview(buttonView);
|
|
624
|
+
buttonView.supportsAutomaticIntrinsicSize = YES;
|
|
625
|
+
buttonView.debuggingName = 'DeleteConstraintsButton';
|
|
626
|
+
|
|
627
|
+
buttonView.leading.equalTo(this.contentView.leading, {plus: 32}).isActive = YES;
|
|
628
|
+
buttonView.trailing.equalTo(this.contentView.trailing, {plus: -32}).isActive = YES;
|
|
629
|
+
buttonView.top.equalTo(this.contentView.top, {plus: 8}).isActive = YES;
|
|
630
|
+
buttonView.bottom.equalTo(this.contentView.bottom, {plus: -8}).isActive = YES;
|
|
631
|
+
|
|
632
|
+
buttonView.node.className = 'BMWindowButton BMCollectionViewCellEventHandler';
|
|
633
|
+
buttonView.node.style.boxSizing = 'border-box';
|
|
634
|
+
buttonView.node.style.textAlign = 'center';
|
|
635
|
+
buttonView.node.style.pointer = 'default';
|
|
636
|
+
buttonView.node.style.margin = 0;
|
|
637
|
+
buttonView.node.innerText = 'Delete Constraint';
|
|
638
|
+
|
|
639
|
+
buttonView.node.addEventListener('click', event => {
|
|
640
|
+
this.setting.target.remove();
|
|
641
|
+
this.layoutEditor.selectView(this.tab._settingsPanel._referenceView);
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
buttonView.node.addEventListener('contextmenu', event => event.preventDefault());
|
|
645
|
+
|
|
646
|
+
return this;
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
// @override - BMCollectionViewCell
|
|
650
|
+
cellDidBindToSetting(setting) {
|
|
651
|
+
}
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
// @endtype
|
|
655
|
+
|
|
656
|
+
// @type BMLayoutEditorSettingsConstraintCell extends BMLayoutEditorSettingsCell
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* A cell type specialized for displaying section titles.
|
|
660
|
+
*/
|
|
661
|
+
export function BMLayoutEditorSettingsTitleCell() {} // <constructor>
|
|
662
|
+
|
|
663
|
+
BMLayoutEditorSettingsTitleCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
664
|
+
constructor: BMLayoutEditorSettingsTitleCell,
|
|
665
|
+
|
|
666
|
+
_titleView: undefined,
|
|
667
|
+
|
|
668
|
+
initWithCollectionView() {
|
|
669
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
670
|
+
|
|
671
|
+
const titleView = BMView.view();
|
|
672
|
+
this._titleView = titleView;
|
|
673
|
+
this.contentView.addSubview(titleView);
|
|
674
|
+
titleView.supportsAutomaticIntrinsicSize = YES;
|
|
675
|
+
titleView.debuggingName = 'TitleView';
|
|
676
|
+
|
|
677
|
+
titleView.leading.equalTo(this.leading, {plus: 8}).isActive = YES;
|
|
678
|
+
titleView.trailing.equalTo(this.trailing, {plus: -8}).isActive = YES;
|
|
679
|
+
titleView.top.equalTo(this.top).isActive = YES;
|
|
680
|
+
titleView.bottom.equalTo(this.bottom).isActive = YES;
|
|
681
|
+
|
|
682
|
+
titleView.node.className = 'BMLayoutEditorDetailsTitleCell';
|
|
683
|
+
|
|
684
|
+
return this;
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
// @override - BMCollectionViewCell
|
|
688
|
+
cellDidBindToSetting(setting) {
|
|
689
|
+
const titleNode = this._titleView.node;
|
|
690
|
+
titleNode.innerText = setting.name;
|
|
691
|
+
this._titleView.invalidateIntrinsicSize();
|
|
692
|
+
}
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
// @endtype
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
// @type BMLayoutEditorSettingsReadonlyCell extends BMLayoutEditorSettingsCell
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* A cell type specialized for displaying readonly settings.
|
|
702
|
+
*/
|
|
703
|
+
export function BMLayoutEditorSettingsReadonlyCell() {} // <constructor>
|
|
704
|
+
|
|
705
|
+
BMLayoutEditorSettingsReadonlyCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
706
|
+
constructor: BMLayoutEditorSettingsReadonlyCell,
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* The view that displays this setting's title.
|
|
710
|
+
*/
|
|
711
|
+
_titleView: undefined,
|
|
712
|
+
get titleView() {
|
|
713
|
+
return this._titleView;
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* The view that displays this setting's input node.
|
|
718
|
+
*/
|
|
719
|
+
_inputView: undefined,
|
|
720
|
+
get inputView() {
|
|
721
|
+
return this._inputView;
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
initWithCollectionView(collectionView, args) {
|
|
725
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
726
|
+
|
|
727
|
+
const titleView = BMView.view();
|
|
728
|
+
this._titleView = titleView;
|
|
729
|
+
this.contentView.addSubview(titleView);
|
|
730
|
+
titleView.supportsAutomaticIntrinsicSize = YES;
|
|
731
|
+
titleView.debuggingName = 'TitleView';
|
|
732
|
+
|
|
733
|
+
titleView.leading.equalTo(this.leading, {plus: 8}).isActive = YES;
|
|
734
|
+
titleView.top.equalTo(this.top, {plus: BMLayoutEdittorSettingCellRowSpacing}).isActive = YES;
|
|
735
|
+
titleView.node.className = 'BMLayoutEditorDetailsTitleCell';
|
|
736
|
+
titleView.node.style.textAlign = 'right';
|
|
737
|
+
titleView.height.greaterThanOrEqualTo(24).isActive = YES;
|
|
738
|
+
titleView.node.style.cssText = 'vertical-align: middle; line-height: 24px; text-align: right;';
|
|
739
|
+
|
|
740
|
+
const inputNode = document.createElement('div');
|
|
741
|
+
inputNode.className = 'BMLayoutEditorDetailsItemReadonlyValue BMLayoutEditorDetailsItemReadonlyValueCell';
|
|
742
|
+
inputNode.style.cssText = 'padding-left: 8px !important';
|
|
743
|
+
const inputView = BMView.viewForNode(inputNode);
|
|
744
|
+
this._inputView = inputView;
|
|
745
|
+
inputView.supportsAutomaticIntrinsicSize = YES;
|
|
746
|
+
inputView.debuggingName = 'LabelView';
|
|
747
|
+
|
|
748
|
+
this.contentView.addSubview(inputView);
|
|
749
|
+
inputView.leading.equalTo(titleView.trailing, {plus: 8}).isActive = YES;
|
|
750
|
+
inputView.width.greaterThanOrEqualTo(200).isActive = YES;
|
|
751
|
+
inputView.width.equalTo(titleView.width, {times: 1.5, priority: 600}).isActive = YES;
|
|
752
|
+
inputView.trailing.equalTo(this.trailing, {plus: -8}).isActive = YES;
|
|
753
|
+
inputView.top.equalTo(this.top, {plus: BMLayoutEdittorSettingCellRowSpacing}).isActive = YES;
|
|
754
|
+
inputView.height.greaterThanOrEqualTo(24).isActive = YES;
|
|
755
|
+
inputView.bottom.equalTo(this.bottom, {plus: -BMLayoutEdittorSettingCellRowSpacing}).isActive = YES;
|
|
756
|
+
|
|
757
|
+
return this;
|
|
758
|
+
},
|
|
759
|
+
|
|
760
|
+
// @override - BMCollectionViewCell
|
|
761
|
+
cellDidBindToSetting(setting) {
|
|
762
|
+
this._titleView.node.innerText = setting.name + ':';
|
|
763
|
+
this._titleView.invalidateIntrinsicSize();
|
|
764
|
+
|
|
765
|
+
this._inputView.node.innerText = setting.target[setting.property];
|
|
766
|
+
this._inputView.invalidateIntrinsicSize();
|
|
767
|
+
}
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
// @endtype
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
// @type BMLayoutEditorSettingsViewCell extends BMLayoutEditorSettingsReadonlyCell
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* A cell type specialized for displaying readonly settings.
|
|
777
|
+
*/
|
|
778
|
+
export function BMLayoutEditorSettingsViewCell() {} // <constructor>
|
|
779
|
+
|
|
780
|
+
BMLayoutEditorSettingsViewCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsReadonlyCell.prototype), {
|
|
781
|
+
constructor: BMLayoutEditorSettingsViewCell,
|
|
782
|
+
|
|
783
|
+
initWithCollectionView(collectionView, args) {
|
|
784
|
+
BMLayoutEditorSettingsReadonlyCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
785
|
+
|
|
786
|
+
this._inputView.node.classList.add('BMLayoutEditorDetailsItemReadonlyValueClickable');
|
|
787
|
+
this._inputView.node.addEventListener('click', e => {
|
|
788
|
+
this.settingsView.selectView(this.setting.target[this.setting.property]);
|
|
789
|
+
});
|
|
790
|
+
this._inputView.node.style.cssText += 'margin: 0px !important;';
|
|
791
|
+
|
|
792
|
+
return this;
|
|
793
|
+
},
|
|
794
|
+
|
|
795
|
+
// @override - BMCollectionViewCell
|
|
796
|
+
cellDidBindToSetting(setting) {
|
|
797
|
+
this._titleView.node.innerText = setting.name + ':';
|
|
798
|
+
this._titleView.invalidateIntrinsicSize();
|
|
799
|
+
|
|
800
|
+
const view = setting.target[setting.property];
|
|
801
|
+
const arrowSpan = document.createElement('span');
|
|
802
|
+
arrowSpan.innerText = '›';
|
|
803
|
+
arrowSpan.className = 'BMLayoutEditorDetailsItemViewLink BMCollectionViewCellEventHandler';
|
|
804
|
+
const viewSpan = document.createElement('span');
|
|
805
|
+
viewSpan.innerText = ' ' + (view.debuggingName || view.node.id);
|
|
806
|
+
this._inputView.node.innerHTML = '';
|
|
807
|
+
this._inputView.node.appendChild(arrowSpan);
|
|
808
|
+
this._inputView.node.appendChild(viewSpan);
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// @endtype
|
|
813
|
+
|
|
814
|
+
// @type BMLayoutEditorSettingsSegmentCell extends BMLayoutEditorSettingsCell
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* A cell type specialized for displaying segment settings.
|
|
818
|
+
*/
|
|
819
|
+
export function BMLayoutEditorSettingsSegmentCell() {} // <constructor>
|
|
820
|
+
|
|
821
|
+
BMLayoutEditorSettingsSegmentCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
822
|
+
constructor: BMLayoutEditorSettingsSegmentCell,
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* The selected menu item, if there is one.
|
|
826
|
+
*/
|
|
827
|
+
_currentMenuItem: undefined, // <BMMenuItem, nullable>
|
|
828
|
+
|
|
829
|
+
initWithCollectionView(collectionView, args) {
|
|
830
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
831
|
+
|
|
832
|
+
this.contentView.CSSClass = 'BMLayoutEditorDetailsTabView BMLayoutEditorDetailsSegmentControl';
|
|
833
|
+
|
|
834
|
+
// Set up the constraints for the tab view
|
|
835
|
+
this.contentView.height.equalTo(48).isActive = YES;
|
|
836
|
+
this.contentView.width.equalTo(1024, {priority: 750}).isActive = YES;
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
return this;
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
// @override - BMCollectionViewCell
|
|
843
|
+
cellDidBindToSetting(setting) {
|
|
844
|
+
this.contentView.contentNode.innerHTML = '';
|
|
845
|
+
for (const menuItem of setting.options) {
|
|
846
|
+
// Create the clickable tabs
|
|
847
|
+
const tabNode = document.createElement('img');
|
|
848
|
+
tabNode.className = 'BMLayoutEditorDetailsTab BMCollectionViewCellEventHandler';
|
|
849
|
+
tabNode.src = menuItem.icon;
|
|
850
|
+
this.contentView.contentNode.appendChild(tabNode);
|
|
851
|
+
|
|
852
|
+
tabNode.title = menuItem.name;
|
|
853
|
+
|
|
854
|
+
// Default to showing the current tab
|
|
855
|
+
if (menuItem.userInfo == setting.target[setting.property]) {
|
|
856
|
+
this._currentMenuItem = menuItem;
|
|
857
|
+
tabNode.classList.add('BMLayoutEditorDetailsTabSelected');
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
menuItem._tabNode = tabNode;
|
|
861
|
+
|
|
862
|
+
// Switch between the tabs on click
|
|
863
|
+
tabNode.addEventListener('click', event => {
|
|
864
|
+
if (menuItem == this._currentMenuItem) return;
|
|
865
|
+
// Deselect the current tab
|
|
866
|
+
this._currentMenuItem._tabNode.classList.remove('BMLayoutEditorDetailsTabSelected');
|
|
867
|
+
// Then select the new one
|
|
868
|
+
this._currentMenuItem = menuItem;
|
|
869
|
+
tabNode.classList.add('BMLayoutEditorDetailsTabSelected');
|
|
870
|
+
|
|
871
|
+
setting.target[setting.property] = menuItem.userInfo;
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// @endtype
|
|
878
|
+
|
|
879
|
+
// @type BMLayoutEditorSettingsBooleanCell extends BMLayoutEditorSettingsCell
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* A cell type specialized for displaying input settings.
|
|
883
|
+
*/
|
|
884
|
+
export function BMLayoutEditorSettingsBooleanCell() {} // <constructor>
|
|
885
|
+
|
|
886
|
+
BMLayoutEditorSettingsBooleanCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
887
|
+
constructor: BMLayoutEditorSettingsBooleanCell,
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* The view that displays this setting's title.
|
|
891
|
+
*/
|
|
892
|
+
_titleView: undefined, // <BMView>
|
|
893
|
+
get titleView() {
|
|
894
|
+
return this._titleView;
|
|
895
|
+
},
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* The view that displays this setting's label node.
|
|
899
|
+
*/
|
|
900
|
+
_labelView: undefined, // <BMView>
|
|
901
|
+
get labelView() {
|
|
902
|
+
return this._labelView;
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
_inputNode: undefined, // <DOMNode>
|
|
906
|
+
|
|
907
|
+
initWithCollectionView(collectionView, args) {
|
|
908
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
909
|
+
|
|
910
|
+
const titleView = BMLayoutEditorSettingsTitleView.titleView();
|
|
911
|
+
this._titleView = titleView;
|
|
912
|
+
this.contentView.addSubview(titleView);
|
|
913
|
+
titleView.debuggingName = 'TitleView';
|
|
914
|
+
/*titleView.supportsAutomaticIntrinsicSize = YES;*/
|
|
915
|
+
|
|
916
|
+
titleView.leading.equalTo(this.leading, {plus: 8}).isActive = YES;
|
|
917
|
+
titleView.centerY.equalTo(this.centerY).isActive = YES;
|
|
918
|
+
/*titleView.node.className = 'BMLayoutEditorDetailsTitleCell';
|
|
919
|
+
titleView.node.style.textAlign = 'right';*/
|
|
920
|
+
|
|
921
|
+
const toggleContainer = BMView.view();
|
|
922
|
+
toggleContainer.debuggingName = 'BooleanContainer';
|
|
923
|
+
this.contentView.addSubview(toggleContainer);
|
|
924
|
+
toggleContainer.leading.equalTo(titleView.trailing, {plus: 8}).isActive = YES;
|
|
925
|
+
toggleContainer.trailing.equalTo(this.contentView.trailing, {plus: -8}).isActive = YES;
|
|
926
|
+
toggleContainer.top.equalTo(this.contentView.top, {plus: BMLayoutEdittorSettingCellRowSpacing}).isActive = YES;
|
|
927
|
+
toggleContainer.bottom.equalTo(this.contentView.bottom, {plus: -BMLayoutEdittorSettingCellRowSpacing}).isActive = YES;
|
|
928
|
+
toggleContainer.width.greaterThanOrEqualTo(200).isActive = YES;
|
|
929
|
+
toggleContainer.width.equalTo(titleView.width, {times: 1.5, priority: 600}).isActive = YES;
|
|
930
|
+
|
|
931
|
+
// Create the toggle
|
|
932
|
+
let value = document.createElement('label');
|
|
933
|
+
value.className = 'BMWindowSwitch';
|
|
934
|
+
value.style.margin = '0';
|
|
935
|
+
|
|
936
|
+
value.innerHTML = `<input type="checkbox" data-toggle="YES"> OFF
|
|
937
|
+
<div class="BMWindowSwitchGutter">
|
|
938
|
+
<div class="BMWindowSwitchKnob"></div>
|
|
939
|
+
</div>
|
|
940
|
+
ON`;
|
|
941
|
+
|
|
942
|
+
this._labelView = BMView.viewForNode(value);
|
|
943
|
+
toggleContainer.addSubview(this._labelView);
|
|
944
|
+
this._labelView.supportsAutomaticIntrinsicSize = YES;
|
|
945
|
+
this._labelView.debuggingName = 'BooleanSwitch';
|
|
946
|
+
|
|
947
|
+
this._labelView.centerY.equalTo(toggleContainer.centerY).isActive = YES;
|
|
948
|
+
this._labelView.leading.equalTo(toggleContainer.leading).isActive = YES;
|
|
949
|
+
this._labelView.width.equalTo(104).isActive = YES;
|
|
950
|
+
this._labelView.height.equalTo(20).isActive = YES;
|
|
951
|
+
|
|
952
|
+
// Set the value
|
|
953
|
+
let input = value.getElementsByTagName('input')[0];
|
|
954
|
+
this._inputNode = input;
|
|
955
|
+
|
|
956
|
+
// If the setting supports an ideterminate state, set it up
|
|
957
|
+
value.addEventListener('click', event => {
|
|
958
|
+
if (!this.setting.nullable) return;
|
|
959
|
+
if (event.target === input) return;
|
|
960
|
+
if (input.checked) {
|
|
961
|
+
event.preventDefault();
|
|
962
|
+
event.stopPropagation();
|
|
963
|
+
if (input.indeterminate) {
|
|
964
|
+
input.indeterminate = NO;
|
|
965
|
+
input.checked = NO;
|
|
966
|
+
this.inputValueDidChangeWithEvent(event, {toValue: NO});
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
input.indeterminate = YES;
|
|
970
|
+
this.inputValueDidChangeWithEvent(event, {toValue: undefined});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
input.addEventListener('change', event => {
|
|
976
|
+
let value = Boolean(event.target.checked);
|
|
977
|
+
|
|
978
|
+
// Read-only indicates an indeterminate state, if supported
|
|
979
|
+
if (this.setting.nullable && input.indeterminate) {
|
|
980
|
+
value = undefined;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
this.inputValueDidChangeWithEvent(event, {toValue: value});
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
return this;
|
|
987
|
+
},
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Invoked whenever the value of this cell's input element changes as a result of user action.
|
|
991
|
+
* This can either be due to the user typing content into the input element or pasting.
|
|
992
|
+
*
|
|
993
|
+
* The default implementation does nothing.
|
|
994
|
+
* @param event <Event> The event that triggered this action.
|
|
995
|
+
* @param toValue <Boolean, nullable> The new value.
|
|
996
|
+
*/
|
|
997
|
+
inputValueDidChangeWithEvent(event, {toValue: value}) {
|
|
998
|
+
const setting = this.setting;
|
|
999
|
+
if (setting.sizeClass) {
|
|
1000
|
+
if (value === undefined) {
|
|
1001
|
+
setting.target[`remove${BMStringByCapitalizingString(setting.property)}VariationForSizeClass`](setting.sizeClass);
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
setting.target[`set${BMStringByCapitalizingString(setting.property)}`](value, {forSizeClass: setting.sizeClass});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
setting.target[setting.property] = value;
|
|
1009
|
+
}
|
|
1010
|
+
},
|
|
1011
|
+
|
|
1012
|
+
// @override - BMCollectionViewCell
|
|
1013
|
+
cellDidBindToSetting(setting) {
|
|
1014
|
+
let value;
|
|
1015
|
+
if (setting.sizeClass) {
|
|
1016
|
+
if (setting.target._variations[setting.sizeClass]) {
|
|
1017
|
+
value = setting.target._variations[setting.sizeClass][setting.property];
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
value = undefined;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
value = setting.target[setting.property];
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (value === undefined && setting.nullable) {
|
|
1028
|
+
this._inputNode.indeterminate = YES;
|
|
1029
|
+
this._inputNode.checked = YES;
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
this._inputNode.indeterminate = NO;
|
|
1033
|
+
this._inputNode.checked = value;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
this._titleView.setting = setting;
|
|
1037
|
+
}
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
// @endtype
|
|
1041
|
+
|
|
1042
|
+
// @type BMLayoutEditorSettingsDropdownCell extends BMLayoutEditorSettingsCell
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* A cell type specialized for displaying enum settings using drop down boxes.
|
|
1046
|
+
*/
|
|
1047
|
+
export function BMLayoutEditorSettingsDropdownCell() {} // <constructor>
|
|
1048
|
+
|
|
1049
|
+
BMLayoutEditorSettingsDropdownCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
1050
|
+
constructor: BMLayoutEditorSettingsDropdownCell,
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* The view that displays this setting's title.
|
|
1054
|
+
*/
|
|
1055
|
+
_titleView: undefined,
|
|
1056
|
+
get titleView() {
|
|
1057
|
+
return this._titleView;
|
|
1058
|
+
},
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* The view that displays this setting's input node.
|
|
1062
|
+
*/
|
|
1063
|
+
_select: undefined,
|
|
1064
|
+
get inputView() {
|
|
1065
|
+
return this._select;
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Set to `YES` while this cell is bound.
|
|
1070
|
+
*/
|
|
1071
|
+
_bound: NO,
|
|
1072
|
+
|
|
1073
|
+
// @override - BMCollectionViewCell
|
|
1074
|
+
prepareForReuse() {
|
|
1075
|
+
this._bound = NO;
|
|
1076
|
+
},
|
|
1077
|
+
|
|
1078
|
+
// @override - BMCollectionViewCell
|
|
1079
|
+
prepareForDisplay() {
|
|
1080
|
+
this._bound = YES;
|
|
1081
|
+
},
|
|
1082
|
+
|
|
1083
|
+
initWithCollectionView(collectionView, args) {
|
|
1084
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1085
|
+
|
|
1086
|
+
const titleView = BMLayoutEditorSettingsTitleView.titleView();
|
|
1087
|
+
this._titleView = titleView;
|
|
1088
|
+
titleView.debuggingName = 'TitleView';
|
|
1089
|
+
this.contentView.addSubview(titleView);
|
|
1090
|
+
|
|
1091
|
+
titleView.leading.equalTo(this.leading, {plus: 8}).isActive = YES;
|
|
1092
|
+
titleView.centerY.equalTo(this.centerY).isActive = YES;
|
|
1093
|
+
|
|
1094
|
+
const inputNode = document.createElement('select');
|
|
1095
|
+
inputNode.className = 'BMWindowInput BMLayoutEditorDetailsCellInput BMLayoutEditorDetailsItemNumericValue BMLayoutEditorDetailsDropdown';
|
|
1096
|
+
const inputView = BMView.viewForNode(inputNode);
|
|
1097
|
+
inputView.debuggingName = 'DropdownView';
|
|
1098
|
+
this._inputView = inputView;
|
|
1099
|
+
|
|
1100
|
+
this.contentView.addSubview(inputView);
|
|
1101
|
+
inputView.leading.equalTo(titleView.trailing, {plus: 8}).isActive = YES;
|
|
1102
|
+
inputView.width.greaterThanOrEqualTo(200).isActive = YES;
|
|
1103
|
+
inputView.trailing.equalTo(this.trailing, {plus: -8}).isActive = YES;
|
|
1104
|
+
inputView.height.equalTo(24).isActive = YES;
|
|
1105
|
+
inputView.centerY.equalTo(this.centerY).isActive = YES;
|
|
1106
|
+
inputView.width.equalTo(titleView.width, {times: 1.5, priority: 600}).isActive = YES;
|
|
1107
|
+
|
|
1108
|
+
inputNode.addEventListener('change', event => {
|
|
1109
|
+
if (!this._bound) return;
|
|
1110
|
+
|
|
1111
|
+
this.inputValueDidChangeWithEvent(event);
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
inputNode.addEventListener('focus', e => this.retain());
|
|
1115
|
+
inputNode.addEventListener('blur', e => this.release());
|
|
1116
|
+
|
|
1117
|
+
return this;
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Invoked whenever the value of this cell's input element changes as a result of user action.
|
|
1122
|
+
* This can either be due to the user typing content into the input element or pasting.
|
|
1123
|
+
*
|
|
1124
|
+
* The default implementation does nothing.
|
|
1125
|
+
* @param event <Event> The event that triggered this action.
|
|
1126
|
+
*/
|
|
1127
|
+
inputValueDidChangeWithEvent(event) {
|
|
1128
|
+
const setting = this.setting;
|
|
1129
|
+
const value = JSON.parse(this._inputView.node.value);
|
|
1130
|
+
if (setting.sizeClass) {
|
|
1131
|
+
setting.target[`set${BMStringByCapitalizingString(setting.property)}`](value, {forSizeClass: setting.sizeClass});
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
setting.target[setting.property] = value;
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
|
|
1138
|
+
// @override - BMCollectionViewCell
|
|
1139
|
+
cellDidBindToSetting(setting) {
|
|
1140
|
+
this._inputView.node.innerHTML = '';
|
|
1141
|
+
for (const item of setting.options) {
|
|
1142
|
+
const option = document.createElement('option');
|
|
1143
|
+
|
|
1144
|
+
option.value = JSON.stringify(item.userInfo);
|
|
1145
|
+
option.innerText = item.name;
|
|
1146
|
+
|
|
1147
|
+
this._inputView.node.appendChild(option);
|
|
1148
|
+
}
|
|
1149
|
+
this._inputView.node.value = setting.sizeClass ? JSON.stringify(setting.target._variations[setting.sizeClass][setting.property]) : JSON.stringify(setting.target[setting.property]);
|
|
1150
|
+
this._titleView.setting = setting;
|
|
1151
|
+
}
|
|
1152
|
+
})
|
|
1153
|
+
|
|
1154
|
+
// @endtype
|
|
1155
|
+
|
|
1156
|
+
// @type BMLayoutEditorSettingsInputCell extends BMLayoutEditorSettingsCell
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* A cell type specialized for displaying input settings.
|
|
1160
|
+
*/
|
|
1161
|
+
export function BMLayoutEditorSettingsInputCell() {} // <constructor>
|
|
1162
|
+
|
|
1163
|
+
BMLayoutEditorSettingsInputCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsCell.prototype), {
|
|
1164
|
+
constructor: BMLayoutEditorSettingsInputCell,
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* The view that displays this setting's title.
|
|
1168
|
+
*/
|
|
1169
|
+
_titleView: undefined,
|
|
1170
|
+
get titleView() {
|
|
1171
|
+
return this._titleView;
|
|
1172
|
+
},
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* The view that displays this setting's input node.
|
|
1176
|
+
*/
|
|
1177
|
+
_inputView: undefined,
|
|
1178
|
+
get inputView() {
|
|
1179
|
+
return this._inputView;
|
|
1180
|
+
},
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Set to `YES` while this cell is bound.
|
|
1184
|
+
*/
|
|
1185
|
+
_bound: NO,
|
|
1186
|
+
|
|
1187
|
+
// @override - BMCollectionViewCell
|
|
1188
|
+
prepareForReuse() {
|
|
1189
|
+
this._bound = NO;
|
|
1190
|
+
},
|
|
1191
|
+
|
|
1192
|
+
// @override - BMCollectionViewCell
|
|
1193
|
+
prepareForDisplay() {
|
|
1194
|
+
this._bound = YES;
|
|
1195
|
+
},
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* Returns the value that indicates that the property should be "unset".
|
|
1199
|
+
*
|
|
1200
|
+
* The default implementation returns an empty string.
|
|
1201
|
+
*/
|
|
1202
|
+
get nullValue() {
|
|
1203
|
+
return '';
|
|
1204
|
+
},
|
|
1205
|
+
|
|
1206
|
+
initWithCollectionView(collectionView, args) {
|
|
1207
|
+
BMLayoutEditorSettingsCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1208
|
+
|
|
1209
|
+
const titleView = BMLayoutEditorSettingsTitleView.titleView();
|
|
1210
|
+
this._titleView = titleView;
|
|
1211
|
+
this.contentView.addSubview(titleView);
|
|
1212
|
+
titleView.debuggingName = 'TitleView';
|
|
1213
|
+
// titleView.supportsAutomaticIntrinsicSize = YES;
|
|
1214
|
+
|
|
1215
|
+
titleView.leading.equalTo(this.leading, {plus: 8}).isActive = YES;
|
|
1216
|
+
titleView.centerY.equalTo(this.centerY).isActive = YES;
|
|
1217
|
+
// titleView.node.className = 'BMLayoutEditorDetailsTitleCell';
|
|
1218
|
+
// titleView.node.style.textAlign = 'right';
|
|
1219
|
+
|
|
1220
|
+
const inputNode = document.createElement('input');
|
|
1221
|
+
inputNode.className = 'BMWindowInput BMLayoutEditorDetailsCellInput ' + args.inputClasses;
|
|
1222
|
+
inputNode.type = args.inputKind;
|
|
1223
|
+
const inputView = BMView.viewForNode(inputNode);
|
|
1224
|
+
this._inputView = inputView;
|
|
1225
|
+
inputView.debuggingName = 'InputView';
|
|
1226
|
+
|
|
1227
|
+
this.contentView.addSubview(inputView);
|
|
1228
|
+
inputView.leading.equalTo(titleView.trailing, {plus: 8}).isActive = YES;
|
|
1229
|
+
inputView.width.greaterThanOrEqualTo(200).isActive = YES;
|
|
1230
|
+
inputView.trailing.equalTo(this.trailing, {plus: -8}).isActive = YES;
|
|
1231
|
+
inputView.centerY.equalTo(this.centerY).isActive = YES;
|
|
1232
|
+
inputView.height.equalTo(24).isActive = YES;
|
|
1233
|
+
inputView.width.equalTo(titleView.width, {times: 1.5, priority: 600}).isActive = YES;
|
|
1234
|
+
|
|
1235
|
+
inputNode.addEventListener('input', event => {
|
|
1236
|
+
if (!this._bound) return;
|
|
1237
|
+
|
|
1238
|
+
this.inputValueDidChangeWithEvent(event);
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
inputNode.addEventListener('paste', event => {
|
|
1242
|
+
if (!this._bound) return;
|
|
1243
|
+
|
|
1244
|
+
this.inputValueDidChangeWithEvent(event);
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
inputNode.addEventListener('focus', e => this.retain());
|
|
1248
|
+
inputNode.addEventListener('blur', e => this.release());
|
|
1249
|
+
|
|
1250
|
+
return this;
|
|
1251
|
+
},
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Returns the value that should be applied to the setting.
|
|
1255
|
+
* Subclasses can override this getter to perform any necessary transforms
|
|
1256
|
+
* on the filter value if needed.
|
|
1257
|
+
*
|
|
1258
|
+
* The default implementation returns the `value` property of this cell's input node.
|
|
1259
|
+
*/
|
|
1260
|
+
get value() {
|
|
1261
|
+
return this._inputView.node.value;
|
|
1262
|
+
},
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Invoked whenever the value of this cell's input element changes as a result of user action.
|
|
1266
|
+
* This can either be due to the user typing content into the input element or pasting.
|
|
1267
|
+
*
|
|
1268
|
+
* The default implementation does nothing.
|
|
1269
|
+
* @param event <Event> The event that triggered this action.
|
|
1270
|
+
*/
|
|
1271
|
+
inputValueDidChangeWithEvent(event) {
|
|
1272
|
+
const setting = this.setting;
|
|
1273
|
+
if (setting.sizeClass) {
|
|
1274
|
+
const value = this.value;
|
|
1275
|
+
if (value === undefined || value === '' || value === this.nullValue) {
|
|
1276
|
+
setting.target[`remove${BMStringByCapitalizingString(setting.property)}VariationForSizeClass`](setting.sizeClass);
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
setting.target[`set${BMStringByCapitalizingString(setting.property)}`](value, {forSizeClass: setting.sizeClass});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
else {
|
|
1283
|
+
setting.target[setting.property] = this.value;
|
|
1284
|
+
}
|
|
1285
|
+
},
|
|
1286
|
+
|
|
1287
|
+
// @override - BMCollectionViewCell
|
|
1288
|
+
cellDidBindToSetting(setting) {
|
|
1289
|
+
if (setting.sizeClass) {
|
|
1290
|
+
if (setting.target._variations[setting.sizeClass]) {
|
|
1291
|
+
if (!(setting.property in setting.target._variations[setting.sizeClass])) {
|
|
1292
|
+
this._inputView.node.value = this.nullValue;
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
this._inputView.node.value = setting.target._variations[setting.sizeClass][setting.property];
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
else {
|
|
1299
|
+
this._inputView.node.value = this.nullValue;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
else {
|
|
1303
|
+
const value = setting.target[setting.property];
|
|
1304
|
+
if (value === undefined) {
|
|
1305
|
+
this._inputView.node.value = this.nullValue;
|
|
1306
|
+
}
|
|
1307
|
+
else {
|
|
1308
|
+
this._inputView.node.value = setting.target[setting.property];
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
this._titleView.setting = setting;
|
|
1313
|
+
}
|
|
1314
|
+
})
|
|
1315
|
+
|
|
1316
|
+
// @endtype
|
|
1317
|
+
|
|
1318
|
+
// @type BMLayoutEditorSettingsIntegerCell extends BMLayoutEditorSettingsInputCell
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* A cell type specialized for displaying integer settings.
|
|
1322
|
+
*/
|
|
1323
|
+
export function BMLayoutEditorSettingsIntegerCell() {} // <constructor>
|
|
1324
|
+
|
|
1325
|
+
BMLayoutEditorSettingsIntegerCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsInputCell.prototype), {
|
|
1326
|
+
constructor: BMLayoutEditorSettingsIntegerCell,
|
|
1327
|
+
|
|
1328
|
+
initWithCollectionView(collectionView, args) {
|
|
1329
|
+
args.inputKind = 'number';
|
|
1330
|
+
args.inputClasses = 'BMLayoutEditorDetailsItemNumericValue';
|
|
1331
|
+
BMLayoutEditorSettingsInputCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1332
|
+
this._inputView.node.step = 1;
|
|
1333
|
+
return this;
|
|
1334
|
+
},
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
get value() {
|
|
1338
|
+
const value = parseInt(this._inputView.node.value, 10);
|
|
1339
|
+
|
|
1340
|
+
if (isNaN(value)) {
|
|
1341
|
+
return 0;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
return value;
|
|
1345
|
+
},
|
|
1346
|
+
|
|
1347
|
+
// @override - BMCollectionViewCell
|
|
1348
|
+
cellDidBindToSetting(setting) {
|
|
1349
|
+
BMLayoutEditorSettingsInputCell.prototype.cellDidBindToSetting.apply(this, arguments);
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
// @endtype
|
|
1354
|
+
|
|
1355
|
+
// @type BMLayoutEditorSettingsNumberCell extends BMLayoutEditorSettingsInputCell
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* A cell type specialized for displaying number settings.
|
|
1359
|
+
*/
|
|
1360
|
+
export function BMLayoutEditorSettingsNumberCell() {} // <constructor>
|
|
1361
|
+
|
|
1362
|
+
BMLayoutEditorSettingsNumberCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsInputCell.prototype), {
|
|
1363
|
+
constructor: BMLayoutEditorSettingsNumberCell,
|
|
1364
|
+
|
|
1365
|
+
initWithCollectionView(collectionView, args) {
|
|
1366
|
+
args.inputKind = 'number';
|
|
1367
|
+
args.inputClasses = 'BMLayoutEditorDetailsItemNumericValue';
|
|
1368
|
+
return BMLayoutEditorSettingsInputCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1369
|
+
},
|
|
1370
|
+
|
|
1371
|
+
get value() {
|
|
1372
|
+
const value = parseFloat(this._inputView.node.value);
|
|
1373
|
+
|
|
1374
|
+
if (isNaN(value)) {
|
|
1375
|
+
return 0;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
return value;
|
|
1379
|
+
},
|
|
1380
|
+
|
|
1381
|
+
// @override - BMCollectionViewCell
|
|
1382
|
+
cellDidBindToSetting(setting) {
|
|
1383
|
+
BMLayoutEditorSettingsInputCell.prototype.cellDidBindToSetting.apply(this, arguments);
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
// @endtype
|
|
1388
|
+
|
|
1389
|
+
// @type BMLayoutEditorSettingsStringCell extends BMLayoutEditorSettingsInputCell
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* A cell type specialized for displaying string settings.
|
|
1393
|
+
*/
|
|
1394
|
+
export function BMLayoutEditorSettingsStringCell() {} // <constructor>
|
|
1395
|
+
|
|
1396
|
+
BMLayoutEditorSettingsStringCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsInputCell.prototype), {
|
|
1397
|
+
constructor: BMLayoutEditorSettingsStringCell,
|
|
1398
|
+
|
|
1399
|
+
initWithCollectionView(collectionView, args) {
|
|
1400
|
+
args.inputKind = 'text';
|
|
1401
|
+
args.inputClasses = 'BMLayoutEditorDetailsItemTextValue';
|
|
1402
|
+
return BMLayoutEditorSettingsInputCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1403
|
+
},
|
|
1404
|
+
|
|
1405
|
+
// @override - BMCollectionViewCell
|
|
1406
|
+
cellDidBindToSetting(setting) {
|
|
1407
|
+
BMLayoutEditorSettingsInputCell.prototype.cellDidBindToSetting.apply(this, arguments);
|
|
1408
|
+
}
|
|
1409
|
+
})
|
|
1410
|
+
|
|
1411
|
+
// @endtype
|
|
1412
|
+
|
|
1413
|
+
// @type BMLayoutEditorSettingsConstantCell extends BMLayoutEditorSettingsInputCell
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* A cell type specialized for displaying constraint constant settings.
|
|
1417
|
+
*/
|
|
1418
|
+
export function BMLayoutEditorSettingsConstantCell() {} // <constructor>
|
|
1419
|
+
|
|
1420
|
+
BMLayoutEditorSettingsConstantCell.prototype = BMExtend(Object.create(BMLayoutEditorSettingsInputCell.prototype), {
|
|
1421
|
+
constructor: BMLayoutEditorSettingsConstantCell,
|
|
1422
|
+
|
|
1423
|
+
get nullValue() {
|
|
1424
|
+
return '';
|
|
1425
|
+
},
|
|
1426
|
+
|
|
1427
|
+
initWithCollectionView(collectionView, args) {
|
|
1428
|
+
args.inputKind = 'text';
|
|
1429
|
+
args.inputClasses = 'BMLayoutEditorDetailsItemTextValue';
|
|
1430
|
+
BMLayoutEditorSettingsInputCell.prototype.initWithCollectionView.apply(this, arguments);
|
|
1431
|
+
|
|
1432
|
+
/** @type {HTMLInputElement} */
|
|
1433
|
+
const inputNode = this._inputView.node;
|
|
1434
|
+
// This array contains all the available layout variables and is initialized upon the input
|
|
1435
|
+
// element acquiring keyboard focus
|
|
1436
|
+
let suggestions = [];
|
|
1437
|
+
// This array contains the layout variables that match the text contained within the input element
|
|
1438
|
+
let filteredSuggestions = [];
|
|
1439
|
+
// The popup menu displaying the filtered suggestions
|
|
1440
|
+
let menu;
|
|
1441
|
+
// The index of the top hit suggestion. And index of -1 represents no top hit.
|
|
1442
|
+
let highlightedItemIndex;
|
|
1443
|
+
|
|
1444
|
+
// TODO: Consider replacing this with a BMTextField
|
|
1445
|
+
inputNode.addEventListener('focus', async event => {
|
|
1446
|
+
// Select all content upon receiving focus
|
|
1447
|
+
inputNode.select();
|
|
1448
|
+
|
|
1449
|
+
suggestions = [];
|
|
1450
|
+
// Load up the variable names
|
|
1451
|
+
const variables = Object.keys(this.layoutEditor.layoutVariableProvider.layoutVariables);
|
|
1452
|
+
const uniqueNames = new Set;
|
|
1453
|
+
variables.forEach(variable => uniqueNames.add(variable));
|
|
1454
|
+
|
|
1455
|
+
let options = [];
|
|
1456
|
+
|
|
1457
|
+
// Create a pair of regular and minus-prefixed variable names
|
|
1458
|
+
for (const variable of uniqueNames) {
|
|
1459
|
+
suggestions.unshift(variable);
|
|
1460
|
+
suggestions.push('-' + variable);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
filteredSuggestions = suggestions;
|
|
1464
|
+
highlightedItemIndex = -1;
|
|
1465
|
+
|
|
1466
|
+
for (const option of suggestions) {
|
|
1467
|
+
options.push(this.layoutEditor.constraintOptionWithLabel(option, {action: () => {
|
|
1468
|
+
menu = undefined;
|
|
1469
|
+
inputNode.value = option;
|
|
1470
|
+
inputNode.dispatchEvent(new Event('input'));
|
|
1471
|
+
|
|
1472
|
+
// Remove focus from the text box upon selecting a value
|
|
1473
|
+
inputNode.blur();
|
|
1474
|
+
}}));
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// Create and show a pulldown menu below the constant textbox, if there are any suggestions
|
|
1478
|
+
if (suggestions.length) {
|
|
1479
|
+
if (this._delaysSuggestionsDropdown) {
|
|
1480
|
+
const menuContainer = this.layoutEditor._createMenuWithOptions(options);
|
|
1481
|
+
menu = menuContainer.children[0];
|
|
1482
|
+
setTimeout(() => {
|
|
1483
|
+
if (document.activeElement == inputNode) {
|
|
1484
|
+
let frame = BMRectMakeWithNodeFrame(inputNode);
|
|
1485
|
+
let point = BMPointMake(frame.origin.x, frame.bottom);
|
|
1486
|
+
this.layoutEditor.showMenuAtPoint(point, {menu: menuContainer, kind: BMMenuKind.PullDownMenu});
|
|
1487
|
+
}
|
|
1488
|
+
}, 200);
|
|
1489
|
+
}
|
|
1490
|
+
else {
|
|
1491
|
+
let frame = BMRectMakeWithNodeFrame(inputNode);
|
|
1492
|
+
let point = BMPointMake(frame.origin.x, frame.bottom);
|
|
1493
|
+
menu = this.layoutEditor.showMenuAtPoint(point, {withOptions: options, kind: BMMenuKind.PullDownMenu});
|
|
1494
|
+
}
|
|
1495
|
+
menu.classList.add('BMLayoutEditorConstantSuggestions');
|
|
1496
|
+
|
|
1497
|
+
menu.parentNode.classList.add('BMLayoutEditorConstantSuggestionsOverlay');
|
|
1498
|
+
|
|
1499
|
+
// Prevent the menu from stealing focus, which would dismiss it before an option is selected
|
|
1500
|
+
menu.addEventListener('mousedown', e => e.preventDefault(), YES);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
const fn = event => event.preventDefault();
|
|
1504
|
+
inputNode.addEventListener('mouseup', fn);
|
|
1505
|
+
// This timeout is necessary because otherwise regular clicks would instantly dismiss the menu
|
|
1506
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
1507
|
+
inputNode.removeEventListener('mouseup', fn);
|
|
1508
|
+
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
inputNode.addEventListener('keydown', event => {
|
|
1512
|
+
if (event.code == 'ArrowDown' || event.code == 'ArrowUp') {
|
|
1513
|
+
event.preventDefault();
|
|
1514
|
+
if (filteredSuggestions.length == 0) return;
|
|
1515
|
+
|
|
1516
|
+
// Move the selection accordingly
|
|
1517
|
+
if (event.code == 'ArrowUp') {
|
|
1518
|
+
highlightedItemIndex = Math.max((highlightedItemIndex || 0) - 1, 0);
|
|
1519
|
+
}
|
|
1520
|
+
else {
|
|
1521
|
+
highlightedItemIndex = Math.min((isNaN(highlightedItemIndex) ? -1 : highlightedItemIndex) + 1, filteredSuggestions.length - 1);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// Remove the highlighted class from the previously selected item
|
|
1525
|
+
for (const element of menu.querySelectorAll('.BMLayoutEditorConstraintPopupOptionHighlighted')) {
|
|
1526
|
+
element.classList.remove('BMLayoutEditorConstraintPopupOptionHighlighted');
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// Highlight the current item
|
|
1530
|
+
menu.childNodes[highlightedItemIndex].classList.add('BMLayoutEditorConstraintPopupOptionHighlighted');
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (event.code == 'Enter') {
|
|
1534
|
+
// Upon pressing the enter key, select the currently highlighted item, if available
|
|
1535
|
+
// then remove focus from the constant box
|
|
1536
|
+
if (!isNaN(highlightedItemIndex) && filteredSuggestions.length) {
|
|
1537
|
+
inputNode.value = filteredSuggestions[highlightedItemIndex];
|
|
1538
|
+
inputNode.dispatchEvent(new Event('input'));
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
inputNode.blur();
|
|
1542
|
+
}
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
inputNode.addEventListener('input', event => {
|
|
1546
|
+
const view = this.tab._settingsPanel._referenceView;
|
|
1547
|
+
const constraint = this._setting.target;
|
|
1548
|
+
|
|
1549
|
+
// Note that these are currently static, but this may be used in the future for custom constraint or view properties as well at which point they may become
|
|
1550
|
+
// editable
|
|
1551
|
+
const property = 'constant';
|
|
1552
|
+
const capitalizedPropertyName = 'Constant';
|
|
1553
|
+
|
|
1554
|
+
if (!view || !constraint) return;
|
|
1555
|
+
|
|
1556
|
+
const number = parseFloat(inputNode.value);
|
|
1557
|
+
const constantValue = isNaN(number) ? inputNode.value : number;
|
|
1558
|
+
|
|
1559
|
+
if (menu) {
|
|
1560
|
+
// If the menu is visible, filter it accordingly
|
|
1561
|
+
filteredSuggestions = suggestions.filter(e => e.toLowerCase().startsWith(inputNode.value.toLowerCase()));
|
|
1562
|
+
|
|
1563
|
+
// Empty the menu
|
|
1564
|
+
menu.innerHTML = '';
|
|
1565
|
+
|
|
1566
|
+
// Reset the highlighted item index
|
|
1567
|
+
highlightedItemIndex = 0;
|
|
1568
|
+
|
|
1569
|
+
// Then add the filtered options
|
|
1570
|
+
for (const option of filteredSuggestions) {
|
|
1571
|
+
menu.appendChild(this.layoutEditor.constraintOptionWithLabel(option, {action: () => {
|
|
1572
|
+
menu = undefined;
|
|
1573
|
+
inputNode.value = option;
|
|
1574
|
+
inputNode.dispatchEvent(new Event('input'));
|
|
1575
|
+
}}));
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// Highlight the first item
|
|
1579
|
+
if (filteredSuggestions.length) {
|
|
1580
|
+
menu.childNodes[0].classList.add('BMLayoutEditorConstraintPopupOptionHighlighted');
|
|
1581
|
+
menu.style.display = 'block';
|
|
1582
|
+
}
|
|
1583
|
+
else {
|
|
1584
|
+
menu.style.display = 'none';
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if (!inputNode.value) {
|
|
1589
|
+
if (args.sizeClass) {
|
|
1590
|
+
if (constraint) {
|
|
1591
|
+
constraint[`remove${capitalizedPropertyName}VariationForSizeClass`](args.sizeClass);
|
|
1592
|
+
constraint._updateConfiguration();
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
view[`remove${capitalizedPropertyName}VariationForSizeClass`](args.sizeClass);
|
|
1596
|
+
view._updateConfiguration();
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
if (constraint) {
|
|
1603
|
+
|
|
1604
|
+
if (this.setting.sizeClass) {
|
|
1605
|
+
constraint[`set${capitalizedPropertyName}`](constantValue, {forSizeClass: this.setting.sizeClass});
|
|
1606
|
+
}
|
|
1607
|
+
else {
|
|
1608
|
+
constraint[property] = constantValue;
|
|
1609
|
+
}
|
|
1610
|
+
constraint._updateConfiguration();
|
|
1611
|
+
constraint._constraint = undefined;
|
|
1612
|
+
constraint._constituentConstraints = undefined;
|
|
1613
|
+
if (constraint._sourceView) {
|
|
1614
|
+
constraint._sourceView.needsLayout = YES;
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
constraint._views[0].needsLayout = YES;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
else {
|
|
1621
|
+
if (this.setting.sizeClass) {
|
|
1622
|
+
view[`set${capitalizedPropertyName}`](number, {forSizeClass: this.setting.sizeClass});
|
|
1623
|
+
}
|
|
1624
|
+
else {
|
|
1625
|
+
view[property] = number;
|
|
1626
|
+
}
|
|
1627
|
+
view.needsLayout = YES;
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
// Upon the value box losing focus, dismiss the menu, if it was open
|
|
1632
|
+
// unless the newly focused item is a descendant of the menu
|
|
1633
|
+
inputNode.addEventListener('blur', event => {
|
|
1634
|
+
if (menu) {
|
|
1635
|
+
menu.parentNode.dispatchEvent(new Event('click'));
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
return this;
|
|
1640
|
+
},
|
|
1641
|
+
|
|
1642
|
+
// @override - BMCollectionViewCell
|
|
1643
|
+
cellDidBindToSetting(setting) {
|
|
1644
|
+
BMLayoutEditorSettingsInputCell.prototype.cellDidBindToSetting.apply(this, arguments);
|
|
1645
|
+
},
|
|
1646
|
+
|
|
1647
|
+
// @override - BMLayoutEditorSettingCell
|
|
1648
|
+
inputValueDidChangeWithEvent(event) {
|
|
1649
|
+
// This cell has a custom implementation, therefore this method is a no op
|
|
1650
|
+
}
|
|
1651
|
+
})
|
|
1652
|
+
|
|
1653
|
+
// @endtype
|