bm-core-ui 2.11.8 → 2.11.9-beta.2
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/build/@types/index.d.ts +391 -6
- package/build/BMCoreUI.css +88 -12
- package/build/BMWindow/BMPopover/BMPopover.js +607 -87
- package/build/BMWindow/BMWindow.js +468 -132
- package/lib/@types/BMCoreUI.min.d.ts +391 -6
- package/lib/BMCoreUI.min.js +2 -2
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {YES, NO, BMExtend, BMCopyProperties} from '../Core/BMCoreUI'
|
|
4
4
|
import {BMPointMake} from '../Core/BMPoint'
|
|
5
|
-
import {BMRectMake, BMRectMakeWithNodeFrame} from '../Core/BMRect'
|
|
5
|
+
import {BMRectMake, BMRectMakeWithNodeFrame, BMRectMakeWithOrigin} from '../Core/BMRect'
|
|
6
6
|
import {BMAnimateWithBlock, BMAnimationContextGetCurrent, BMAnimationContextEnableWebAnimations, __BMVelocityAnimate, BMHook} from '../Core/BMAnimationContext'
|
|
7
7
|
import {BMView, BMViewColorScheme} from '../BMView/BMView_v2.5'
|
|
8
8
|
import {BMLayoutOrientation} from '../BMView/BMLayoutSizeClass'
|
|
@@ -132,16 +132,60 @@ class BMWindowOverlay extends BMView {
|
|
|
132
132
|
|
|
133
133
|
// @type BMWindow extends BMView
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* The duration, in milliseconds, for most window animations.
|
|
137
|
+
*/
|
|
135
138
|
var _BMWindowAnimationDurationDefault = 400;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The easing to use for most window animations.
|
|
142
|
+
*/
|
|
136
143
|
var _BMWindowAnimationEasingDefault = 'easeInOutQuart';
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The easing to use for the default window appearing animation.
|
|
147
|
+
*/
|
|
137
148
|
const _BMWindowAnimationEasingIncomingDefault = 'easeOutQuart';
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The duration, in milliseconds, to use for the default window appearing animation.
|
|
152
|
+
*/
|
|
138
153
|
const _BMWindowAnimationDurationIncomingDefault = 200;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The easing to use for the default window disappearing animation.
|
|
157
|
+
*/
|
|
139
158
|
const _BMWindowAnimationEasingOutgoingDefault = 'easeInQuart';
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The duration, in milliseconds, to use for the default window disappearing animation.
|
|
162
|
+
*/
|
|
140
163
|
const _BMWindowAnimationDurationOutgoingDefault = 200;
|
|
141
164
|
|
|
165
|
+
/**
|
|
166
|
+
* The delay, in milliseconds, to wait until hiding the minimize strip after the mouse pointer
|
|
167
|
+
* moves away from the bottom of the screen.
|
|
168
|
+
*/
|
|
169
|
+
const _BMWindowMinimzeStripHideDelay = 1000;
|
|
170
|
+
|
|
171
|
+
// The maximum iterations allowed for the window showcase since it's never guaranteed that
|
|
172
|
+
// the algorithm can finish within a known number of iterations; in practice it's rare that
|
|
173
|
+
// this value will ever be reached.
|
|
142
174
|
const BM_WINDOW_SHOWCASE_MAX_ITERATIONS = 5000;
|
|
175
|
+
|
|
176
|
+
// The maximum z-index to use for the topmost window.
|
|
143
177
|
var BM_WINDOW_Z_INDEX_MAX = 2007;
|
|
144
178
|
|
|
179
|
+
// A flag that controls the size of a minimized window.
|
|
180
|
+
const BM_WINDOW_MINIMIZED_SIZE = 160;
|
|
181
|
+
|
|
182
|
+
// A flag that controls the spacing between the edges of the window
|
|
183
|
+
// and the minimized window
|
|
184
|
+
const BM_WINDOW_MINIMIZED_EDGE_SPACING = 30;
|
|
185
|
+
|
|
186
|
+
// A flag that controls the spacing between minimized windows
|
|
187
|
+
const BM_WINDOW_MINIMIZED_SPACING = 16;
|
|
188
|
+
|
|
145
189
|
/**
|
|
146
190
|
* A window is an object that manages the display and lifecycle of a popup window.
|
|
147
191
|
*/
|
|
@@ -156,6 +200,8 @@ BMWindow._windows = [];
|
|
|
156
200
|
// An array containing all minimized non-modal window DOM nodes
|
|
157
201
|
BMWindow._minimizedWindows = [];
|
|
158
202
|
|
|
203
|
+
// A weak map containing the association between minimized window nodes and window objects
|
|
204
|
+
BMWindow._minimizedWindowMap = new WeakMap;
|
|
159
205
|
|
|
160
206
|
/**
|
|
161
207
|
* Returns the z-index value that the topmost non-modal window will have.
|
|
@@ -169,6 +215,221 @@ BMWindow.zIndexMax = function () {
|
|
|
169
215
|
return BM_WINDOW_Z_INDEX_MAX;
|
|
170
216
|
}
|
|
171
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Updates the position of all animated windows based on how many there are.
|
|
220
|
+
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
221
|
+
*/
|
|
222
|
+
BMWindow._updateMinimizedWindowsAnimated = function (animated = YES) {
|
|
223
|
+
const length = this._minimizedWindows.length * BM_WINDOW_MINIMIZED_SIZE;
|
|
224
|
+
const spacing = (this._minimizedWindows.length - 1) * BM_WINDOW_MINIMIZED_SPACING;
|
|
225
|
+
|
|
226
|
+
let left = window.innerWidth / 2 - (length + spacing) / 2;
|
|
227
|
+
|
|
228
|
+
if (animated) {
|
|
229
|
+
BMAnimateWithBlock(() => {
|
|
230
|
+
BMAnimationContextEnableWebAnimations();
|
|
231
|
+
|
|
232
|
+
for (const minimizedWindow of this._minimizedWindows) {
|
|
233
|
+
const controller = BMAnimationContextGetCurrent().controllerForObject(minimizedWindow, {node: minimizedWindow});
|
|
234
|
+
controller.registerBuiltInProperty('left', {withValue: left + 'px'});
|
|
235
|
+
controller.registerBuiltInProperty('top', {withValue: window.innerHeight - BM_WINDOW_MINIMIZED_SIZE + 'px'});
|
|
236
|
+
|
|
237
|
+
// Determine where the window thumbnail should be positioned
|
|
238
|
+
const targetWindow = this._minimizedWindowMap.get(minimizedWindow);
|
|
239
|
+
const minimizedSize = targetWindow._minimizedSize;
|
|
240
|
+
const minimizedFrame = BMRectMake(left, window.innerHeight - BM_WINDOW_MINIMIZED_SIZE, BM_WINDOW_MINIMIZED_SIZE, BM_WINDOW_MINIMIZED_SIZE - BM_WINDOW_MINIMIZED_EDGE_SPACING);
|
|
241
|
+
const targetFrame = BMRectMakeWithOrigin(BMPointMake(0, 0), {size: minimizedSize});
|
|
242
|
+
targetFrame.center = minimizedFrame.center;
|
|
243
|
+
targetFrame.origin.y = minimizedFrame.bottom - targetFrame.height;
|
|
244
|
+
|
|
245
|
+
// Move the window thumbnail to the appropriate position
|
|
246
|
+
const windowFrame = targetWindow._fullScreen ? BMRectMake(0, 0, window.innerWidth, window.innerHeight) : targetWindow.frame;
|
|
247
|
+
const transformRect = windowFrame.rectWithTransformToRect(targetFrame);
|
|
248
|
+
const windowController = BMAnimationContextGetCurrent().controllerForObject(targetWindow, {node: targetWindow.node});
|
|
249
|
+
windowController.registerBuiltInPropertiesWithDictionary({translateX: transformRect.left + 'px', translateY: transformRect.top + 'px', scaleX: transformRect.width, scaleY: transformRect.height});
|
|
250
|
+
|
|
251
|
+
left += BM_WINDOW_MINIMIZED_SIZE + BM_WINDOW_MINIMIZED_SPACING;
|
|
252
|
+
}
|
|
253
|
+
}, {
|
|
254
|
+
duration: _BMWindowAnimationDurationDefault,
|
|
255
|
+
easing: _BMWindowAnimationEasingDefault
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
for (const minimizedWindow of this._minimizedWindows) {
|
|
260
|
+
// Move the container to the appropriate slot
|
|
261
|
+
BMHook(minimizedWindow, {left: left + 'px', top: window.innerHeight - BM_WINDOW_MINIMIZED_SIZE + 'px'});
|
|
262
|
+
|
|
263
|
+
// Determine where the window thumbnail should be positioned
|
|
264
|
+
const targetWindow = this._minimizedWindowMap.get(minimizedWindow);
|
|
265
|
+
const minimizedSize = targetWindow._minimizedSize;
|
|
266
|
+
const minimizedFrame = BMRectMake(left, window.innerHeight - BM_WINDOW_MINIMIZED_SIZE, BM_WINDOW_MINIMIZED_SIZE, BM_WINDOW_MINIMIZED_SIZE - BM_WINDOW_MINIMIZED_EDGE_SPACING);
|
|
267
|
+
const targetFrame = BMRectMakeWithOrigin(BMPointMake(0, 0), {size: minimizedSize});
|
|
268
|
+
targetFrame.center = minimizedFrame.center;
|
|
269
|
+
|
|
270
|
+
// Move the window thumbnail to the appropriate position
|
|
271
|
+
const windowFrame = targetWindow._fullScreen ? BMRectMake(0, 0, window.innerWidth, window.innerHeight) : targetWindow.frame;
|
|
272
|
+
const transformRect = windowFrame.rectWithTransformToRect(targetFrame);
|
|
273
|
+
BMHook(targetWindow.node, {translateX: transformRect.left + 'px', translateY: transformRect.top + 'px', scaleX: transformRect.width, scaleY: transformRect.height});
|
|
274
|
+
|
|
275
|
+
left += BM_WINDOW_MINIMIZED_SIZE + BM_WINDOW_MINIMIZED_SPACING;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (this._minimizedWindows.length) {
|
|
280
|
+
// If there are any minimized windows, set up an event handler that shows and hides the minimize strip based on the pointer's position
|
|
281
|
+
this._startMinimizeStripAutohide();
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// If there are no remaining windows, stop monitoring the pointer's position
|
|
285
|
+
this._stopMinimizeStripAutohide();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Set to `YES` while monitoring the mouse pointer to autohide the minimized windows strip.
|
|
291
|
+
*/
|
|
292
|
+
BMWindow._minimizeAutohideActive = NO;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Sets up event handlers that automatically hide the minimize strip after the mouse pointer moves away
|
|
296
|
+
* from the bottom of the screen for a certain amount of time. For touch devices, this will retain the top part
|
|
297
|
+
* of the minimize strip so that it can be invoked by tapping it. For mouse devices, this will completely hide
|
|
298
|
+
* the minimize strip.
|
|
299
|
+
*
|
|
300
|
+
* Additionally, sets up event handlers to reposition the minimize strip whenever the browser window resized.
|
|
301
|
+
*/
|
|
302
|
+
BMWindow._startMinimizeStripAutohide = function () {
|
|
303
|
+
// If handlers are already set up, don't take any action
|
|
304
|
+
if (BMWindow._minimizeAutohideActive) return;
|
|
305
|
+
BMWindow._minimizeAutohideActive = YES;
|
|
306
|
+
|
|
307
|
+
window.addEventListener('mousemove', BMWindow._mouseDidMoveWithEvent);
|
|
308
|
+
window.addEventListener('resize', BMWindow._viewportDidResizeWithEvent);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Brings the minimize strip back into view and stops monitoring the mouse pointer to autohide it.
|
|
313
|
+
*/
|
|
314
|
+
BMWindow._stopMinimizeStripAutohide = function () {
|
|
315
|
+
// If handlers are not set up, don't take any action
|
|
316
|
+
if (BMWindow._minimizeAutohideActive) return;
|
|
317
|
+
BMWindow._minimizeAutohideActive = NO;
|
|
318
|
+
|
|
319
|
+
window.removeEventListener('mousemove', BMWindow._mouseDidMoveWithEvent);
|
|
320
|
+
window.removeEventListener('resize', BMWindow._viewportDidResizeWithEvent);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Invoked whenever the viewport resizes while the minimize strip is visible. Updates the position
|
|
325
|
+
* of the minimize strip and its windows to match the new viewport size.
|
|
326
|
+
* @param event <Event> The event that triggered this action.
|
|
327
|
+
*/
|
|
328
|
+
BMWindow._viewportDidResizeWithEvent = function (event) {
|
|
329
|
+
BMWindow._updateMinimizedWindowsAnimated(NO);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Invoked whenever the mouse pointer moves while the minimize strip is visible. Tests if the mouse pointer moves
|
|
334
|
+
* sufficiently far away from the strip and hides it if it does, or shows the minimize strip if the mouse pointer
|
|
335
|
+
* moves sufficiently close to the bottom of the viewport.
|
|
336
|
+
* @param event <MouseEvent> The event that triggered this action.
|
|
337
|
+
*/
|
|
338
|
+
BMWindow._mouseDidMoveWithEvent = function (event) {
|
|
339
|
+
const position = BMPointMake(event.clientX, event.clientY);
|
|
340
|
+
const visibleHeight = BM_WINDOW_MINIMIZED_SIZE + BM_WINDOW_MINIMIZED_SPACING;
|
|
341
|
+
|
|
342
|
+
// The rect outside of which the pointer must remain in order for the strip to be hidden
|
|
343
|
+
const retainVisibilityRect = BMRectMake(0, window.innerHeight - visibleHeight, window.innerWidth, visibleHeight);
|
|
344
|
+
|
|
345
|
+
// The rect inside of which the pointer must be in order for the strip to become visible
|
|
346
|
+
const showRect = BMRectMake(0, window.innerHeight - BM_WINDOW_MINIMIZED_SPACING, window.innerWidth, BM_WINDOW_MINIMIZED_SPACING);
|
|
347
|
+
|
|
348
|
+
// Show the minimize strip as soon as the mouse pointer intersects the show rect
|
|
349
|
+
if (showRect.intersectsPoint(position) && BMWindow._minimizeStripHidden) {
|
|
350
|
+
BMWindow.showMinimizeStripAnimated();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!retainVisibilityRect.intersectsPoint(position)) {
|
|
354
|
+
if (!BMWindow._minimizeStripHidden) {
|
|
355
|
+
// If the pointer stops intersecting the retain visibility rect, start a timer that hides
|
|
356
|
+
// the minimize strip after a delay unless the mouse pointer is brought back
|
|
357
|
+
if (BMWindow._minimizeStripHideTimeout) return;
|
|
358
|
+
|
|
359
|
+
BMWindow._minimizeStripHideTimeout = window.setTimeout(() => {
|
|
360
|
+
BMWindow.hideMinimizeStripAnimated();
|
|
361
|
+
BMWindow._minimizeStripHideTimeout = undefined;
|
|
362
|
+
}, _BMWindowMinimzeStripHideDelay);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// If the pointer starts intersecting the retain visibility rect before the timer elapses,
|
|
367
|
+
// cancel it and require the mouse to exit again to hide the strip
|
|
368
|
+
if (BMWindow._minimizeStripHideTimeout) {
|
|
369
|
+
window.clearTimeout(BMWindow._minimizeStripHideTimeout);
|
|
370
|
+
BMWindow._minimizeStripHideTimeout = undefined;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Shows the minimize strip if it is hidden.
|
|
377
|
+
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
378
|
+
* @returns <Promise<void>> A promise that resolves when the associated animation completes.
|
|
379
|
+
*/
|
|
380
|
+
BMWindow.showMinimizeStripAnimated = async function (animated) {
|
|
381
|
+
// Wait for the entire timeout to hide the strip again
|
|
382
|
+
if (BMWindow._minimizeStripHideTimeout) {
|
|
383
|
+
window.clearTimeout(BMWindow._minimizeStripHideTimeout);
|
|
384
|
+
BMWindow._minimizeStripHideTimeout = undefined;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!BMWindow._minimizeStripHidden) return;
|
|
388
|
+
BMWindow._minimizeStripHidden = NO;
|
|
389
|
+
|
|
390
|
+
document.body.classList.remove('BMWindowMinimizeStripHidden');
|
|
391
|
+
document.body.classList.add('BMWindowMinimizeStripShown');
|
|
392
|
+
|
|
393
|
+
// After the shown animation ends, remove the animation class
|
|
394
|
+
if (!document.body.getAnimations) return;
|
|
395
|
+
|
|
396
|
+
if (BMWindow._minimizedWindows.length) {
|
|
397
|
+
// Immediately commit the animation
|
|
398
|
+
window.getComputedStyle(BMWindow._minimizedWindows[0]);
|
|
399
|
+
|
|
400
|
+
const animation = BMWindow._minimizedWindows[0].getAnimations()[0];
|
|
401
|
+
if (animation) {
|
|
402
|
+
await animation.finished;
|
|
403
|
+
document.body.classList.remove('BMWindowMinimizeStripShown');
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Hides the minimize strip if it is visible.
|
|
410
|
+
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
411
|
+
*/
|
|
412
|
+
BMWindow.hideMinimizeStripAnimated = function () {
|
|
413
|
+
if (BMWindow._minimizeStripHidden) return;
|
|
414
|
+
BMWindow._minimizeStripHidden = YES;
|
|
415
|
+
|
|
416
|
+
// Before hiding the minimize strip, add the appropraite classes to all minimize elements in order
|
|
417
|
+
// to play the minimize animation
|
|
418
|
+
for (const node of BMWindow._minimizedWindows) {
|
|
419
|
+
if (!node.classList.contains('BMWindowMinimizedControl')) {
|
|
420
|
+
node.classList.add('BMWindowMinimizedControl');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const window = BMWindow._minimizedWindowMap.get(node);
|
|
424
|
+
if (window && !window.node.classList.contains('BMWindowMinimizedThumbnail')) {
|
|
425
|
+
window.node.classList.add('BMWindowMinimizedThumbnail');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
document.body.classList.add('BMWindowMinimizeStripHidden');
|
|
430
|
+
document.body.classList.remove('BMWindowMinimizeStripShown');
|
|
431
|
+
}
|
|
432
|
+
|
|
172
433
|
/**
|
|
173
434
|
* Minimizes all non-minimized windows.
|
|
174
435
|
* @param animated <Boolean, nullable> Defaults to `YES`. If set to `YES`, this change will be animated.
|
|
@@ -610,6 +871,7 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
610
871
|
* A string that is used for when this window is minimized.
|
|
611
872
|
*/
|
|
612
873
|
_title: 'New Window', // <String>
|
|
874
|
+
|
|
613
875
|
get title() {
|
|
614
876
|
return this._title;
|
|
615
877
|
},
|
|
@@ -958,7 +1220,6 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
958
1220
|
dragHandle.style.cursor = 'nwse-resize';
|
|
959
1221
|
node.appendChild(dragHandle);
|
|
960
1222
|
|
|
961
|
-
|
|
962
1223
|
// Initialize dragging touch events for the drag handler
|
|
963
1224
|
dragHandle.addEventListener('mousedown', event => {
|
|
964
1225
|
|
|
@@ -1106,6 +1367,60 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1106
1367
|
}
|
|
1107
1368
|
},
|
|
1108
1369
|
|
|
1370
|
+
/**
|
|
1371
|
+
* @protected
|
|
1372
|
+
* Invoked whenever a drag operation began for this window.
|
|
1373
|
+
* @param position <BMPoint> The position where the drag began, relative to the viewport.
|
|
1374
|
+
* {
|
|
1375
|
+
* @param withEvent <MouseEvent | TouchEvent> The event that triggered this action.
|
|
1376
|
+
* }
|
|
1377
|
+
*/
|
|
1378
|
+
dragBeganAtPosition(position, {withEvent: event}) {
|
|
1379
|
+
event.preventDefault();
|
|
1380
|
+
},
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* @protected
|
|
1384
|
+
* Invoked whenever the pointer position changes during a drag event. Displaces this window's
|
|
1385
|
+
* frame by the amount the event pointer has moved.
|
|
1386
|
+
* @param fromPosition <BMPoint> The previous pointer position relative to the viewport.
|
|
1387
|
+
* {
|
|
1388
|
+
* @param toPosition <BMPoint> The current pointer position relative to the viewport.
|
|
1389
|
+
* @param event <MouseEvent | TouchEvent> The event that triggered this action.
|
|
1390
|
+
* }
|
|
1391
|
+
*/
|
|
1392
|
+
dragPositionDidChangeFromPosition(fromPosition, {toPosition, event}) {
|
|
1393
|
+
const currentPosition = BMPointMake(this.leftConstraint.constant, this.topConstraint.constant);
|
|
1394
|
+
const newPosition = BMPointMake(
|
|
1395
|
+
currentPosition.x + toPosition.x - fromPosition.x,
|
|
1396
|
+
currentPosition.y + toPosition.y - fromPosition.y
|
|
1397
|
+
);
|
|
1398
|
+
|
|
1399
|
+
// Check if the window can move
|
|
1400
|
+
let canMove = YES;
|
|
1401
|
+
if (this.delegate && this.delegate.windowShouldMove) {
|
|
1402
|
+
canMove = this.delegate.windowShouldMove(this, newPosition);
|
|
1403
|
+
}
|
|
1404
|
+
if (!canMove) return;
|
|
1405
|
+
|
|
1406
|
+
this.leftConstraint.constant = newPosition.x;
|
|
1407
|
+
this.topConstraint.constant = newPosition.y;
|
|
1408
|
+
this.layout();
|
|
1409
|
+
event.preventDefault();
|
|
1410
|
+
},
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* @protected
|
|
1414
|
+
* Invoked whenever a drag operation ended for this window.
|
|
1415
|
+
* @param position <BMPoint> The position where the drag began, relative to the viewport.
|
|
1416
|
+
* {
|
|
1417
|
+
* @param withEvent <MouseEvent | TouchEvent> The event that triggered this action.
|
|
1418
|
+
* }
|
|
1419
|
+
*/
|
|
1420
|
+
dragEndedAtPosition(position, {withEvent: event}) {
|
|
1421
|
+
// Intentionally blank
|
|
1422
|
+
},
|
|
1423
|
+
|
|
1109
1424
|
/**
|
|
1110
1425
|
* Starts a drag event from the specified `mousedown` event that will move this window.
|
|
1111
1426
|
* When this method is invoked, further mouse events will be blocked until the drag finishes.
|
|
@@ -1120,37 +1435,25 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1120
1435
|
|
|
1121
1436
|
this._dragged = YES;
|
|
1122
1437
|
|
|
1123
|
-
this._position = BMPointMake(this.node.offsetLeft, this.node.offsetTop);
|
|
1124
1438
|
let lastPosition = BMPointMake(event.clientX, event.clientY);
|
|
1125
1439
|
|
|
1126
1440
|
let mouseMoveEventListener = event => {
|
|
1127
1441
|
let position = BMPointMake(event.clientX, event.clientY);
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
// Check if the window can move
|
|
1131
|
-
let canMove = YES;
|
|
1132
|
-
if (this.delegate && this.delegate.windowShouldMove) {
|
|
1133
|
-
canMove = this.delegate.windowShouldMove(this, newPosition);
|
|
1134
|
-
}
|
|
1135
|
-
if (!canMove) return;
|
|
1136
|
-
|
|
1137
|
-
this.leftConstraint.constant = this._position.x + position.x - lastPosition.x;
|
|
1138
|
-
this.topConstraint.constant = this._position.y + position.y - lastPosition.y;
|
|
1139
|
-
this._position = newPosition;
|
|
1442
|
+
this.dragPositionDidChangeFromPosition(lastPosition, {toPosition: position, event});
|
|
1140
1443
|
lastPosition = position;
|
|
1141
|
-
this.layout();
|
|
1142
|
-
event.preventDefault();
|
|
1143
1444
|
};
|
|
1144
1445
|
|
|
1145
1446
|
let mouseUpEventListener = event => {
|
|
1146
1447
|
window.removeEventListener('mousemove', mouseMoveEventListener, YES);
|
|
1147
1448
|
window.removeEventListener('mouseup', mouseUpEventListener, YES);
|
|
1449
|
+
|
|
1450
|
+
this.dragEndedAtPosition(lastPosition.copy(), {withEvent: event});
|
|
1148
1451
|
}
|
|
1149
1452
|
|
|
1150
1453
|
window.addEventListener('mousemove', mouseMoveEventListener, YES);
|
|
1151
1454
|
window.addEventListener('mouseup', mouseUpEventListener, YES);
|
|
1152
1455
|
|
|
1153
|
-
|
|
1456
|
+
this.dragBeganAtPosition(lastPosition.copy(), {withEvent: event});
|
|
1154
1457
|
},
|
|
1155
1458
|
|
|
1156
1459
|
/**
|
|
@@ -1181,7 +1484,6 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1181
1484
|
this._touchDragPoint = event.changedTouches[0].identifier;
|
|
1182
1485
|
this._dragged = YES;
|
|
1183
1486
|
|
|
1184
|
-
this._position = BMPointMake(this.node.offsetLeft, this.node.offsetTop);
|
|
1185
1487
|
let lastPosition = BMPointMake(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
|
|
1186
1488
|
|
|
1187
1489
|
let mouseMoveEventListener = event => {
|
|
@@ -1198,21 +1500,8 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1198
1500
|
if (!touch) return;
|
|
1199
1501
|
|
|
1200
1502
|
let position = BMPointMake(touch.clientX, touch.clientY);
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
// Check if the window can move
|
|
1204
|
-
let canMove = YES;
|
|
1205
|
-
if (this.delegate && this.delegate.windowShouldMove) {
|
|
1206
|
-
canMove = this.delegate.windowShouldMove(this, newPosition);
|
|
1207
|
-
}
|
|
1208
|
-
if (!canMove) return;
|
|
1209
|
-
|
|
1210
|
-
this.leftConstraint.constant = this._position.x + position.x - lastPosition.x;
|
|
1211
|
-
this.topConstraint.constant = this._position.y + position.y - lastPosition.y;
|
|
1212
|
-
this._position = newPosition;
|
|
1503
|
+
this.dragPositionDidChangeFromPosition(lastPosition, {toPosition: position, event});
|
|
1213
1504
|
lastPosition = position;
|
|
1214
|
-
this.layout();
|
|
1215
|
-
event.preventDefault();
|
|
1216
1505
|
};
|
|
1217
1506
|
|
|
1218
1507
|
let mouseUpEventListener = event => {
|
|
@@ -1220,13 +1509,15 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1220
1509
|
window.removeEventListener('touchmove', mouseMoveEventListener);
|
|
1221
1510
|
window.removeEventListener('touchend', mouseUpEventListener);
|
|
1222
1511
|
window.removeEventListener('touchcancel', mouseUpEventListener);
|
|
1512
|
+
|
|
1513
|
+
this.dragEndedAtPosition(lastPosition.copy(), {withEvent: event});
|
|
1223
1514
|
}
|
|
1224
1515
|
|
|
1225
1516
|
window.addEventListener('touchmove', mouseMoveEventListener);
|
|
1226
1517
|
window.addEventListener('touchend', mouseUpEventListener);
|
|
1227
1518
|
window.removeEventListener('touchcancel', mouseUpEventListener);
|
|
1228
1519
|
|
|
1229
|
-
|
|
1520
|
+
this.dragBeganAtPosition(lastPosition.copy(), {withEvent: event});
|
|
1230
1521
|
},
|
|
1231
1522
|
|
|
1232
1523
|
// @override - BMView
|
|
@@ -1714,6 +2005,22 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1714
2005
|
|
|
1715
2006
|
},
|
|
1716
2007
|
|
|
2008
|
+
/**
|
|
2009
|
+
* The size that this should be when minimized.
|
|
2010
|
+
*/
|
|
2011
|
+
get _minimizedSize() { // <BMSize>
|
|
2012
|
+
const minimizedSize = BMSizeMake(BM_WINDOW_MINIMIZED_SIZE, BM_WINDOW_MINIMIZED_SIZE - BM_WINDOW_MINIMIZED_EDGE_SPACING);
|
|
2013
|
+
const aspectRatio = minimizedSize.width / minimizedSize.height;
|
|
2014
|
+
if (this.frame.size.width / this.frame.size.height > aspectRatio) {
|
|
2015
|
+
minimizedSize.height = minimizedSize.height * (this.frame.size.height / this.frame.size.width) * aspectRatio;
|
|
2016
|
+
}
|
|
2017
|
+
else {
|
|
2018
|
+
minimizedSize.width = minimizedSize.width * (this.frame.size.width / this.frame.size.height) / aspectRatio;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
return minimizedSize;
|
|
2022
|
+
},
|
|
2023
|
+
|
|
1717
2024
|
/**
|
|
1718
2025
|
* Minimizes this window. This method will raise an error if this window is modal.
|
|
1719
2026
|
* If this window is already minimized, this method will have no effect.
|
|
@@ -1731,10 +2038,30 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1731
2038
|
this.delegate.windowWillMinimize(this);
|
|
1732
2039
|
}
|
|
1733
2040
|
|
|
2041
|
+
this.resignKeyWindow();
|
|
2042
|
+
|
|
2043
|
+
// Temporarily show the minimize strip while minimizing a window
|
|
2044
|
+
BMWindow.showMinimizeStripAnimated();
|
|
2045
|
+
|
|
2046
|
+
this.node.classList.add('BMWindowMinimizedPreview');
|
|
2047
|
+
|
|
2048
|
+
// Determine the minimized size
|
|
2049
|
+
const minimizedSize = this._minimizedSize;
|
|
2050
|
+
|
|
1734
2051
|
let minimizedWindow = document.createElement('div');
|
|
1735
2052
|
minimizedWindow.className = 'BMWindowMinimized';
|
|
1736
|
-
|
|
1737
|
-
|
|
2053
|
+
|
|
2054
|
+
const minimizedWindowTitle = document.createElement('span');
|
|
2055
|
+
minimizedWindowTitle.innerText = this.title;
|
|
2056
|
+
minimizedWindow.appendChild(minimizedWindowTitle);
|
|
2057
|
+
|
|
2058
|
+
minimizedWindow.style.left =
|
|
2059
|
+
// Move to the center of the screen
|
|
2060
|
+
(window.innerWidth / 2) +
|
|
2061
|
+
// At the end of the current minimized windows
|
|
2062
|
+
(BMWindow._minimizedWindows.length * BM_WINDOW_MINIMIZED_SIZE + (BMWindow._minimizedWindows.length - 1) * BM_WINDOW_MINIMIZED_SPACING) / 2 +
|
|
2063
|
+
// Then displace current minimized windows by half the current window size and add in the spacing
|
|
2064
|
+
BM_WINDOW_MINIMIZED_SPACING / 2 - BM_WINDOW_MINIMIZED_SIZE / 2 + 'px';
|
|
1738
2065
|
minimizedWindow.addEventListener('click', event => {
|
|
1739
2066
|
if (event.altKey) {
|
|
1740
2067
|
BMWindow.restoreAllAnimated(YES);
|
|
@@ -1745,6 +2072,9 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1745
2072
|
});
|
|
1746
2073
|
|
|
1747
2074
|
BMWindow._minimizedWindows.push(minimizedWindow);
|
|
2075
|
+
BMWindow._minimizedWindowMap.set(minimizedWindow, this);
|
|
2076
|
+
|
|
2077
|
+
BMWindow._updateMinimizedWindowsAnimated();
|
|
1748
2078
|
|
|
1749
2079
|
document.body.appendChild(minimizedWindow);
|
|
1750
2080
|
|
|
@@ -1757,50 +2087,43 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1757
2087
|
var node = minimizedWindow;
|
|
1758
2088
|
|
|
1759
2089
|
var rect = BMRectMakeWithNodeFrame(node);
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
animationNode.style.position = 'fixed';
|
|
1763
|
-
animationNode.style.zIndex = BM_WINDOW_Z_INDEX_MAX + 1;
|
|
1764
|
-
animationNode.style.transform = '';
|
|
1765
|
-
BMCopyProperties(animationNode.style, {left: rect.origin.x + 'px', top: rect.origin.y + 'px', width: rect.size.width + 'px', height: rect.size.height + 'px', margin: 0});
|
|
1766
|
-
|
|
1767
|
-
// Disable pointer events on the new view so that hover states do not cause the layer to be redrawn mid-animation
|
|
1768
|
-
animationNode.style.pointerEvents = 'none';
|
|
2090
|
+
rect.size.height -= BM_WINDOW_MINIMIZED_EDGE_SPACING;
|
|
1769
2091
|
|
|
1770
2092
|
// Compute the transform and apply it first to the window
|
|
1771
2093
|
// and then to the animation node, but inversed
|
|
1772
2094
|
var frame = self._fullScreen ? BMRectMake(0, 0, window.innerWidth, window.innerHeight) : self.frame;
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
scaleX: transformRect.size.width, scaleY: transformRect.size.height}, {
|
|
1783
|
-
duration: _BMWindowAnimationDurationDefault,
|
|
1784
|
-
easing: _BMWindowAnimationEasingDefault,
|
|
1785
|
-
display: 'none'
|
|
1786
|
-
}, YES);
|
|
2095
|
+
const targetFrame = BMRectMakeWithOrigin(BMPointMake(0, 0), {size: minimizedSize});
|
|
2096
|
+
targetFrame.center = rect.center;
|
|
2097
|
+
targetFrame.origin.y = window.innerHeight - BM_WINDOW_MINIMIZED_EDGE_SPACING - targetFrame.size.height;
|
|
2098
|
+
var transformRect = frame.rectWithTransformToRect(targetFrame);
|
|
2099
|
+
|
|
2100
|
+
BMHook(node, {opacity: 0});
|
|
2101
|
+
|
|
2102
|
+
this.node.style.pointerEvents = 'none';
|
|
2103
|
+
this.node.inert = YES;
|
|
1787
2104
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2105
|
+
__BMVelocityAnimate(this._window, {
|
|
2106
|
+
translateX: transformRect.origin.x + 'px',
|
|
2107
|
+
translateY: transformRect.origin.y + 'px',
|
|
2108
|
+
scaleX: transformRect.size.width,
|
|
2109
|
+
scaleY: transformRect.size.height
|
|
2110
|
+
}, {
|
|
2111
|
+
duration: _BMWindowAnimationDurationDefault,
|
|
2112
|
+
easing: _BMWindowAnimationEasingDefault,
|
|
2113
|
+
complete: function () {
|
|
1796
2114
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2115
|
+
if (self.delegate && self.delegate.windowDidMinimize) {
|
|
2116
|
+
self.delegate.windowDidMinimize(self);
|
|
2117
|
+
}
|
|
1800
2118
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
2119
|
+
if (args && args.completionHandler) args.completionHandler();
|
|
2120
|
+
}
|
|
2121
|
+
}, YES);
|
|
2122
|
+
|
|
2123
|
+
const title = minimizedWindow.querySelector('span');
|
|
2124
|
+
__BMVelocityAnimate(title, {translateX: ['0px', '-30px']}, {duration: _BMWindowAnimationDurationDefault, easing: _BMWindowAnimationEasingDefault}, YES);
|
|
2125
|
+
|
|
2126
|
+
__BMVelocityAnimate(node, {opacity: 1}, {duration: _BMWindowAnimationDurationDefault, easing: _BMWindowAnimationEasingDefault}, YES);
|
|
1804
2127
|
}
|
|
1805
2128
|
},
|
|
1806
2129
|
|
|
@@ -1821,71 +2144,82 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
1821
2144
|
this.delegate.windowWillRestore(this);
|
|
1822
2145
|
}
|
|
1823
2146
|
|
|
2147
|
+
// Prevent this window from reacting to minimize strip autohide animations
|
|
2148
|
+
this.node.classList.remove('BMWindowMinimizedThumbnail');
|
|
2149
|
+
|
|
2150
|
+
// Temporarily show the minimize strip while minimizing a window
|
|
2151
|
+
BMWindow.showMinimizeStripAnimated();
|
|
2152
|
+
|
|
1824
2153
|
let self = this;
|
|
1825
2154
|
|
|
1826
2155
|
if (animated) {
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
this._minimizedWindow = undefined;
|
|
1833
|
-
let removedWindow = NO;
|
|
1834
|
-
for (let i = 0, length = BMWindow._minimizedWindows.length, windows = BMWindow._minimizedWindows; i < length; i++) {
|
|
1835
|
-
if (windows[i] == node) {
|
|
1836
|
-
windows.splice(i, 1);
|
|
1837
|
-
i--;
|
|
1838
|
-
length--;
|
|
1839
|
-
removedWindow = YES;
|
|
1840
|
-
continue;
|
|
1841
|
-
}
|
|
2156
|
+
// Create a copy of the given node which will be used in the animation in place of the original node
|
|
2157
|
+
var node = this._minimizedWindow;
|
|
2158
|
+
var rect = BMRectMakeWithNodeFrame(node);
|
|
2159
|
+
var animationNode = node.cloneNode(YES);
|
|
1842
2160
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
2161
|
+
this._minimizedWindow = undefined;
|
|
2162
|
+
let removedWindow = NO;
|
|
2163
|
+
for (let i = 0, length = BMWindow._minimizedWindows.length, windows = BMWindow._minimizedWindows; i < length; i++) {
|
|
2164
|
+
if (windows[i] == node) {
|
|
2165
|
+
windows.splice(i, 1);
|
|
2166
|
+
i--;
|
|
2167
|
+
length--;
|
|
2168
|
+
removedWindow = YES;
|
|
2169
|
+
break;
|
|
1846
2170
|
}
|
|
2171
|
+
}
|
|
1847
2172
|
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2173
|
+
BMWindow._updateMinimizedWindowsAnimated();
|
|
2174
|
+
|
|
2175
|
+
if (!this.isKeyWindow) this.becomeKeyWindow();
|
|
2176
|
+
|
|
2177
|
+
animationNode.style.position = 'fixed';
|
|
2178
|
+
animationNode.style.zIndex = BM_WINDOW_Z_INDEX_MAX + 1;
|
|
2179
|
+
animationNode.style.transform = '';
|
|
2180
|
+
BMCopyProperties(animationNode.style, {left: rect.origin.x + 'px', top: rect.origin.y + 'px', width: rect.size.width + 'px', height: rect.size.height + 'px', margin: 0});
|
|
2181
|
+
|
|
2182
|
+
// Disable pointer events on the new view so that hover states do not cause the layer to be redrawn mid-animation
|
|
2183
|
+
animationNode.style.pointerEvents = 'none';
|
|
2184
|
+
|
|
2185
|
+
document.body.appendChild(animationNode);
|
|
2186
|
+
node.style.display = 'none';
|
|
2187
|
+
|
|
2188
|
+
__BMVelocityAnimate(this._window, {
|
|
2189
|
+
opacity: 1,
|
|
2190
|
+
translateX: '0px',
|
|
2191
|
+
translateY: '0px',
|
|
2192
|
+
scaleX: 1,
|
|
2193
|
+
scaleY: 1
|
|
2194
|
+
}, {
|
|
2195
|
+
duration: _BMWindowAnimationDurationDefault,
|
|
2196
|
+
easing: _BMWindowAnimationEasingDefault,
|
|
2197
|
+
display: 'block'
|
|
2198
|
+
}, YES);
|
|
2199
|
+
|
|
2200
|
+
const title = animationNode.querySelector('span');
|
|
2201
|
+
__BMVelocityAnimate(title, {translateX: ['30px', '0px']}, {duration: _BMWindowAnimationDurationDefault, easing: _BMWindowAnimationEasingDefault}, YES);
|
|
2202
|
+
|
|
2203
|
+
// After finishing the animation, remove the temporary node and restore the original node's display
|
|
2204
|
+
__BMVelocityAnimate(animationNode, {opacity: [0, 1]}, {
|
|
2205
|
+
duration: _BMWindowAnimationDurationDefault,
|
|
2206
|
+
easing: _BMWindowAnimationEasingDefault,
|
|
2207
|
+
complete: function () {
|
|
2208
|
+
animationNode.remove();
|
|
2209
|
+
node.remove();
|
|
2210
|
+
|
|
2211
|
+
self.node.classList.remove('BMWindowMinimizedPreview');
|
|
2212
|
+
|
|
2213
|
+
self.node.style.pointerEvents = '';
|
|
2214
|
+
self.node.inert = NO;
|
|
2215
|
+
|
|
2216
|
+
if (self.delegate && self.delegate.windowDidRestore) {
|
|
2217
|
+
self.delegate.windowDidRestore(self);
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
if (args && args.completionHandler) args.completionHandler();
|
|
2221
|
+
}
|
|
2222
|
+
}, YES);
|
|
1889
2223
|
}
|
|
1890
2224
|
},
|
|
1891
2225
|
|
|
@@ -2135,6 +2469,8 @@ BMWindow.prototype = BMExtend(Object.create(BMView.prototype), {
|
|
|
2135
2469
|
}
|
|
2136
2470
|
this._minimizedWindow.remove();
|
|
2137
2471
|
}
|
|
2472
|
+
|
|
2473
|
+
BMWindow._updateMinimizedWindowsAnimated(NO);
|
|
2138
2474
|
}
|
|
2139
2475
|
|
|
2140
2476
|
BMView.prototype.release.call(this);
|