OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.exportPath('options'); | 5 cr.exportPath('options'); |
6 | 6 |
7 /** | 7 /** |
8 * Enumeration of display layout. These values must match the C++ values in | |
9 * ash::DisplayController. | |
10 * @enum {number} | |
11 */ | |
12 options.DisplayLayoutType = { | |
13 TOP: 0, | |
14 RIGHT: 1, | |
15 BOTTOM: 2, | |
16 LEFT: 3 | |
17 }; | |
18 | |
19 /** | |
20 * Enumeration of multi display mode. These values must match the C++ values in | 8 * Enumeration of multi display mode. These values must match the C++ values in |
21 * ash::DisplayManager. | 9 * ash::DisplayManager. |
22 * @enum {number} | 10 * @enum {number} |
23 */ | 11 */ |
24 options.MultiDisplayMode = { | 12 options.MultiDisplayMode = { |
25 EXTENDED: 0, | 13 EXTENDED: 0, |
26 MIRRORING: 1, | 14 MIRRORING: 1, |
27 UNIFIED: 2, | 15 UNIFIED: 2, |
28 }; | 16 }; |
29 | 17 |
30 /** | 18 /** |
31 * @typedef {{ | 19 * @typedef {{ |
32 * left: number, | |
33 * top: number, | |
34 * width: number, | |
35 * height: number | |
36 * }} | |
37 */ | |
38 options.DisplayBounds; | |
39 | |
40 /** | |
41 * @typedef {{ | |
42 * x: number, | |
43 * y: number | |
44 * }} | |
45 */ | |
46 options.DisplayPosition; | |
47 | |
48 /** | |
49 * @typedef {{ | |
50 * width: number, | 20 * width: number, |
51 * height: number, | 21 * height: number, |
52 * originalWidth: number, | 22 * originalWidth: number, |
53 * originalHeight: number, | 23 * originalHeight: number, |
54 * deviceScaleFactor: number, | 24 * deviceScaleFactor: number, |
55 * scale: number, | 25 * scale: number, |
56 * refreshRate: number, | 26 * refreshRate: number, |
57 * isBest: boolean, | 27 * isBest: boolean, |
58 * selected: boolean | 28 * selected: boolean |
59 * }} | 29 * }} |
(...skipping 16 matching lines...) Expand all Loading... | |
76 * id: string, | 46 * id: string, |
77 * isInternal: boolean, | 47 * isInternal: boolean, |
78 * isPrimary: boolean, | 48 * isPrimary: boolean, |
79 * resolutions: !Array<!options.DisplayMode>, | 49 * resolutions: !Array<!options.DisplayMode>, |
80 * name: string, | 50 * name: string, |
81 * rotation: number | 51 * rotation: number |
82 * }} | 52 * }} |
83 */ | 53 */ |
84 options.DisplayInfo; | 54 options.DisplayInfo; |
85 | 55 |
86 /** | |
87 * @typedef {{ | |
88 * bounds: !options.DisplayBounds, | |
89 * div: ?HTMLElement, | |
90 * id: string, | |
91 * isPrimary: boolean, | |
92 * layoutType: options.DisplayLayoutType, | |
93 * name: string, | |
94 * originalPosition: !options.DisplayPosition | |
95 * }} | |
96 */ | |
97 options.DisplayLayout; | |
98 | |
99 cr.define('options', function() { | 56 cr.define('options', function() { |
100 var Page = cr.ui.pageManager.Page; | 57 var Page = cr.ui.pageManager.Page; |
101 var PageManager = cr.ui.pageManager.PageManager; | 58 var PageManager = cr.ui.pageManager.PageManager; |
102 | 59 |
103 // The scale ratio of the display rectangle to its original size. | 60 // The scale ratio of the display rectangle to its original size. |
104 /** @const */ var VISUAL_SCALE = 1 / 10; | 61 /** @const */ var VISUAL_SCALE = 1 / 10; |
105 | 62 |
106 // The number of pixels to share the edges between displays. | |
107 /** @const */ var MIN_OFFSET_OVERLAP = 5; | |
108 | |
109 /** | |
110 * Gets the layout type of |point| relative to |rect|. | |
111 * @param {!options.DisplayBounds} rect The base rectangle. | |
112 * @param {!options.DisplayPosition} point The point to check the position. | |
113 * @return {options.DisplayLayoutType} The position of the calculated point. | |
114 */ | |
115 function getPositionToRectangle(rect, point) { | |
116 // Separates the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of | |
117 // the rect, and decides which area the display should reside. | |
118 var diagonalSlope = rect.height / rect.width; | |
119 var topDownIntercept = rect.top - rect.left * diagonalSlope; | |
120 var bottomUpIntercept = rect.top + rect.height + rect.left * diagonalSlope; | |
121 | |
122 if (point.y > topDownIntercept + point.x * diagonalSlope) { | |
123 if (point.y > bottomUpIntercept - point.x * diagonalSlope) | |
124 return options.DisplayLayoutType.BOTTOM; | |
125 else | |
126 return options.DisplayLayoutType.LEFT; | |
127 } else { | |
128 if (point.y > bottomUpIntercept - point.x * diagonalSlope) | |
129 return options.DisplayLayoutType.RIGHT; | |
130 else | |
131 return options.DisplayLayoutType.TOP; | |
132 } | |
133 } | |
134 | |
135 /** | 63 /** |
136 * Snaps the region [point, width] to [basePoint, baseWidth] if | 64 * Snaps the region [point, width] to [basePoint, baseWidth] if |
137 * the [point, width] is close enough to the base's edge. | 65 * the [point, width] is close enough to the base's edge. |
138 * @param {number} point The starting point of the region. | 66 * @param {number} point The starting point of the region. |
139 * @param {number} width The width of the region. | 67 * @param {number} width The width of the region. |
140 * @param {number} basePoint The starting point of the base region. | 68 * @param {number} basePoint The starting point of the base region. |
141 * @param {number} baseWidth The width of the base region. | 69 * @param {number} baseWidth The width of the base region. |
142 * @return {number} The moved point. Returns the point itself if it doesn't | 70 * @return {number} The moved point. Returns the point itself if it doesn't |
143 * need to snap to the edge. | 71 * need to snap to the edge. |
144 * @private | 72 * @private |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
198 | 126 |
199 /** | 127 /** |
200 * The array of current output displays. It also contains the display | 128 * The array of current output displays. It also contains the display |
201 * rectangles currently rendered on screen. | 129 * rectangles currently rendered on screen. |
202 * @type {!Array<!options.DisplayInfo>} | 130 * @type {!Array<!options.DisplayInfo>} |
203 * @private | 131 * @private |
204 */ | 132 */ |
205 displays_: [], | 133 displays_: [], |
206 | 134 |
207 /** | 135 /** |
208 * An object containing DisplayLayout objects for each entry in |displays_|. | 136 * Manages the display layout. |
209 * @type {!Object<!options.DisplayLayout>} | 137 * @type {?options.DisplayLayoutManager} |
210 * @private | 138 * @private |
211 */ | 139 */ |
212 displayLayoutMap_: {}, | 140 displayLayoutManager_: null, |
213 | 141 |
214 /** | 142 /** |
215 * The id of the currently focused display, or empty for none. | 143 * The id of the currently focused display, or empty for none. |
216 * @type {string} | 144 * @type {string} |
217 * @private | 145 * @private |
218 */ | 146 */ |
219 focusedId_: '', | 147 focusedId_: '', |
220 | 148 |
221 /** | 149 /** |
222 * The primary display id. | |
223 * @type {string} | |
224 * @private | |
225 */ | |
226 primaryDisplayId_: '', | |
227 | |
228 /** | |
229 * The secondary display id. | |
230 * @type {string} | |
231 * @private | |
232 */ | |
233 secondaryDisplayId_: '', | |
234 | |
235 /** | |
236 * Drag info. | 150 * Drag info. |
237 * @type {?{displayId: string, | 151 * @type {?{displayId: string, |
238 * originalLocation: !options.DisplayPosition, | 152 * originalLocation: !options.DisplayPosition, |
239 * eventLocation: !options.DisplayPosition}} | 153 * eventLocation: !options.DisplayPosition}} |
240 * @private | 154 * @private |
241 */ | 155 */ |
242 dragInfo_: null, | 156 dragInfo_: null, |
243 | 157 |
244 /** | 158 /** |
245 * The container div element which contains all of the display rectangles. | 159 * The container div element which contains all of the display rectangles. |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
432 * @param {string} id | 346 * @param {string} id |
433 * @return {options.DisplayInfo} | 347 * @return {options.DisplayInfo} |
434 */ | 348 */ |
435 getDisplayInfoFromId(id) { | 349 getDisplayInfoFromId(id) { |
436 return this.displays_.find(function(display) { | 350 return this.displays_.find(function(display) { |
437 return display.id == id; | 351 return display.id == id; |
438 }); | 352 }); |
439 }, | 353 }, |
440 | 354 |
441 /** | 355 /** |
442 * Collects the current data and sends it to Chrome. | 356 * Sends the display layout for the secondary display to Chrome. |
443 * @private | 357 * @private |
444 */ | 358 */ |
445 sendDragResult_: function() { | 359 sendDragResult_: function() { |
446 // Offset is calculated from top or left edge. | 360 // The first non-primary display is the secondary display. |
447 var primary = this.displayLayoutMap_[this.primaryDisplayId_]; | 361 var secondaryId; |
448 var secondary = this.displayLayoutMap_[this.secondaryDisplayId_]; | 362 for (var i = 0; i < this.displays_.length; i++) { |
449 var layoutType = secondary.layoutType; | 363 if (!this.displays_[i].isPrimary) { |
450 var offset; | 364 secondaryId = this.displays_[i].id; |
451 if (layoutType == options.DisplayLayoutType.LEFT || | 365 break; |
452 layoutType == options.DisplayLayoutType.RIGHT) { | 366 } |
453 offset = secondary.div.offsetTop - primary.div.offsetTop; | |
454 } else { | |
455 offset = secondary.div.offsetLeft - primary.div.offsetLeft; | |
456 } | 367 } |
457 offset = Math.floor(offset / this.visualScale_); | 368 assert(!!secondaryId); |
458 chrome.send('setDisplayLayout', [secondary.id, layoutType, offset]); | 369 var displayLayout = |
370 this.displayLayoutManager_.getDisplayLayout(secondaryId); | |
371 chrome.send( | |
372 'setDisplayLayout', | |
373 [secondaryId, displayLayout.layoutType, displayLayout.offset]); | |
459 }, | 374 }, |
460 | 375 |
461 /** | 376 /** |
462 * Processes the actual dragging of display rectangle. | 377 * Processes the actual dragging of display rectangle. |
463 * @param {Event} e The event which triggers this drag. | 378 * @param {Event} e The event which triggers this drag. |
464 * @param {options.DisplayPosition} eventLocation The location where the | 379 * @param {options.DisplayPosition} eventLocation The location where the |
465 * event happens. | 380 * event happens. |
466 * @private | 381 * @private |
467 */ | 382 */ |
468 processDragging_: function(e, eventLocation) { | 383 processDragging_: function(e, eventLocation) { |
469 if (!this.dragInfo_) | 384 if (!this.dragInfo_) |
470 return true; | 385 return true; |
471 | 386 |
472 e.preventDefault(); | 387 e.preventDefault(); |
473 | 388 |
474 // Note that current code of moving display-rectangles doesn't work | |
475 // if there are >=3 displays. This is our assumption for M21. | |
476 // TODO(mukai): Fix the code to allow >=3 displays. | |
477 var dragInfo = this.dragInfo_; | 389 var dragInfo = this.dragInfo_; |
390 | |
478 /** @type {options.DisplayPosition} */ var newPosition = { | 391 /** @type {options.DisplayPosition} */ var newPosition = { |
479 x: dragInfo.originalLocation.x + | 392 x: dragInfo.originalLocation.x + |
480 (eventLocation.x - dragInfo.eventLocation.x), | 393 (eventLocation.x - dragInfo.eventLocation.x), |
481 y: dragInfo.originalLocation.y + | 394 y: dragInfo.originalLocation.y + |
482 (eventLocation.y - dragInfo.eventLocation.y) | 395 (eventLocation.y - dragInfo.eventLocation.y) |
483 }; | 396 }; |
484 | 397 |
485 var dragLayout = this.displayLayoutMap_[dragInfo.displayId]; | 398 this.displayLayoutManager_.updatePosition( |
486 var draggingDiv = dragLayout.div; | 399 this.dragInfo_.displayId, newPosition); |
487 | |
488 var baseDisplayId = dragLayout.isPrimary ? this.secondaryDisplayId_ : | |
489 this.primaryDisplayId_; | |
490 var baseLayout = this.displayLayoutMap_[baseDisplayId]; | |
491 var baseDiv = baseLayout.div; | |
492 | |
493 newPosition.x = snapToEdge( | |
494 newPosition.x, draggingDiv.offsetWidth, baseDiv.offsetLeft, | |
495 baseDiv.offsetWidth); | |
496 newPosition.y = snapToEdge( | |
497 newPosition.y, draggingDiv.offsetHeight, baseDiv.offsetTop, | |
498 baseDiv.offsetHeight); | |
499 | |
500 /** @type {!options.DisplayPosition} */ var newCenter = { | |
501 x: newPosition.x + draggingDiv.offsetWidth / 2, | |
502 y: newPosition.y + draggingDiv.offsetHeight / 2 | |
503 }; | |
504 | |
505 /** @type {!options.DisplayBounds} */ var baseBounds = { | |
506 left: baseDiv.offsetLeft, | |
507 top: baseDiv.offsetTop, | |
508 width: baseDiv.offsetWidth, | |
509 height: baseDiv.offsetHeight | |
510 }; | |
511 | |
512 var isPrimary = dragLayout.isPrimary; | |
513 // layoutType is always stored in the secondary layout. | |
514 var layoutType = | |
515 isPrimary ? baseLayout.layoutType : dragLayout.layoutType; | |
516 | |
517 switch (getPositionToRectangle(baseBounds, newCenter)) { | |
518 case options.DisplayLayoutType.RIGHT: | |
519 layoutType = isPrimary ? options.DisplayLayoutType.LEFT : | |
520 options.DisplayLayoutType.RIGHT; | |
521 break; | |
522 case options.DisplayLayoutType.LEFT: | |
523 layoutType = isPrimary ? options.DisplayLayoutType.RIGHT : | |
524 options.DisplayLayoutType.LEFT; | |
525 break; | |
526 case options.DisplayLayoutType.TOP: | |
527 layoutType = isPrimary ? options.DisplayLayoutType.BOTTOM : | |
528 options.DisplayLayoutType.TOP; | |
529 break; | |
530 case options.DisplayLayoutType.BOTTOM: | |
531 layoutType = isPrimary ? options.DisplayLayoutType.TOP : | |
532 options.DisplayLayoutType.BOTTOM; | |
533 break; | |
534 } | |
535 | |
536 if (layoutType == options.DisplayLayoutType.LEFT || | |
537 layoutType == options.DisplayLayoutType.RIGHT) { | |
538 if (newPosition.y > baseDiv.offsetTop + baseDiv.offsetHeight) | |
539 layoutType = isPrimary ? options.DisplayLayoutType.TOP : | |
540 options.DisplayLayoutType.BOTTOM; | |
541 else if (newPosition.y + draggingDiv.offsetHeight < baseDiv.offsetTop) | |
542 layoutType = isPrimary ? options.DisplayLayoutType.BOTTOM : | |
543 options.DisplayLayoutType.TOP; | |
544 } else { | |
545 if (newPosition.x > baseDiv.offsetLeft + baseDiv.offsetWidth) | |
546 layoutType = isPrimary ? options.DisplayLayoutType.LEFT : | |
547 options.DisplayLayoutType.RIGHT; | |
548 else if (newPosition.x + draggingDiv.offsetWidth < baseDiv.offsetLeft) | |
549 layoutType = isPrimary ? options.DisplayLayoutType.RIGHT : | |
550 options.DisplayLayoutType.LEFT; | |
551 } | |
552 | |
553 var layoutToBase; | |
554 if (!isPrimary) { | |
555 dragLayout.layoutType = layoutType; | |
556 layoutToBase = layoutType; | |
557 } else { | |
558 baseLayout.layoutType = layoutType; | |
559 switch (layoutType) { | |
560 case options.DisplayLayoutType.RIGHT: | |
561 layoutToBase = options.DisplayLayoutType.LEFT; | |
562 break; | |
563 case options.DisplayLayoutType.LEFT: | |
564 layoutToBase = options.DisplayLayoutType.RIGHT; | |
565 break; | |
566 case options.DisplayLayoutType.TOP: | |
567 layoutToBase = options.DisplayLayoutType.BOTTOM; | |
568 break; | |
569 case options.DisplayLayoutType.BOTTOM: | |
570 layoutToBase = options.DisplayLayoutType.TOP; | |
571 break; | |
572 } | |
573 } | |
574 | |
575 switch (layoutToBase) { | |
576 case options.DisplayLayoutType.RIGHT: | |
577 draggingDiv.style.left = | |
578 baseDiv.offsetLeft + baseDiv.offsetWidth + 'px'; | |
579 draggingDiv.style.top = newPosition.y + 'px'; | |
580 break; | |
581 case options.DisplayLayoutType.LEFT: | |
582 draggingDiv.style.left = | |
583 baseDiv.offsetLeft - draggingDiv.offsetWidth + 'px'; | |
584 draggingDiv.style.top = newPosition.y + 'px'; | |
585 break; | |
586 case options.DisplayLayoutType.TOP: | |
587 draggingDiv.style.top = | |
588 baseDiv.offsetTop - draggingDiv.offsetHeight + 'px'; | |
589 draggingDiv.style.left = newPosition.x + 'px'; | |
590 break; | |
591 case options.DisplayLayoutType.BOTTOM: | |
592 draggingDiv.style.top = | |
593 baseDiv.offsetTop + baseDiv.offsetHeight + 'px'; | |
594 draggingDiv.style.left = newPosition.x + 'px'; | |
595 break; | |
596 } | |
597 | 400 |
598 return false; | 401 return false; |
599 }, | 402 }, |
600 | 403 |
601 /** | 404 /** |
602 * Start dragging of a display rectangle. | 405 * Start dragging of a display rectangle. |
603 * @param {!HTMLElement} target The event target. | 406 * @param {!HTMLElement} target The event target. |
604 * @param {!options.DisplayPosition} eventLocation The event location. | 407 * @param {!options.DisplayPosition} eventLocation The event location. |
605 * @private | 408 * @private |
606 */ | 409 */ |
607 startDragging_: function(target, eventLocation) { | 410 startDragging_: function(target, eventLocation) { |
608 var oldFocusedId = this.focusedId_; | 411 var oldFocusedId = this.focusedId_; |
609 var newFocusedId; | 412 var newFocusedId; |
610 var willUpdateDisplayDescription = false; | 413 var willUpdateDisplayDescription = false; |
611 for (var i = 0; i < this.displays_.length; i++) { | 414 for (var i = 0; i < this.displays_.length; i++) { |
612 var displayLayout = this.displayLayoutMap_[this.displays_[i].id]; | 415 var displayLayout = |
416 this.displayLayoutManager_.getDisplayLayout(this.displays_[i].id); | |
613 if (displayLayout.div == target || | 417 if (displayLayout.div == target || |
614 (target.offsetParent && target.offsetParent == displayLayout.div)) { | 418 (target.offsetParent && target.offsetParent == displayLayout.div)) { |
615 newFocusedId = displayLayout.id; | 419 newFocusedId = displayLayout.id; |
616 break; | 420 break; |
617 } | 421 } |
618 } | 422 } |
619 if (!newFocusedId) | 423 if (!newFocusedId) |
620 return false; | 424 return false; |
621 | 425 |
622 this.focusedId_ = newFocusedId; | 426 this.focusedId_ = newFocusedId; |
623 willUpdateDisplayDescription = newFocusedId != oldFocusedId; | 427 willUpdateDisplayDescription = newFocusedId != oldFocusedId; |
624 | 428 |
625 for (var i = 0; i < this.displays_.length; i++) { | 429 for (var i = 0; i < this.displays_.length; i++) { |
626 var displayLayout = this.displayLayoutMap_[this.displays_[i].id]; | 430 var displayLayout = |
431 this.displayLayoutManager_.getDisplayLayout(this.displays_[i].id); | |
627 displayLayout.div.className = 'displays-display'; | 432 displayLayout.div.className = 'displays-display'; |
628 if (displayLayout.id != this.focusedId_) | 433 if (displayLayout.id != this.focusedId_) |
629 continue; | 434 continue; |
630 | 435 |
631 displayLayout.div.classList.add('displays-focused'); | 436 displayLayout.div.classList.add('displays-focused'); |
632 if (this.displays_.length > 1) { | 437 if (this.displays_.length > 1) { |
633 this.dragInfo_ = { | 438 this.dragInfo_ = { |
634 displayId: displayLayout.id, | 439 displayId: displayLayout.id, |
635 originalLocation: { | 440 originalLocation: { |
636 x: displayLayout.div.offsetLeft, | 441 x: displayLayout.div.offsetLeft, |
(...skipping 12 matching lines...) Expand all Loading... | |
649 /** | 454 /** |
650 * finish the current dragging of displays. | 455 * finish the current dragging of displays. |
651 * @param {Event} e The event which triggers this. | 456 * @param {Event} e The event which triggers this. |
652 * @private | 457 * @private |
653 */ | 458 */ |
654 endDragging_: function(e) { | 459 endDragging_: function(e) { |
655 this.lastTouchLocation_ = null; | 460 this.lastTouchLocation_ = null; |
656 if (!this.dragInfo_) | 461 if (!this.dragInfo_) |
657 return false; | 462 return false; |
658 | 463 |
659 // Make sure the dragging location is connected. | 464 if (this.displayLayoutManager_.finalizePosition(this.dragInfo_.displayId)) |
660 var dragLayout = this.displayLayoutMap_[this.dragInfo_.displayId]; | |
661 var baseDisplayId = dragLayout.isPrimary ? this.secondaryDisplayId_ : | |
662 this.primaryDisplayId_; | |
663 | |
664 var baseLayout = this.displayLayoutMap_[baseDisplayId]; | |
665 var baseDiv = baseLayout.div; | |
666 var draggingDiv = dragLayout.div; | |
667 | |
668 // layoutType is always stored in the secondary layout. | |
669 var layoutType = | |
670 dragLayout.isPrimary ? baseLayout.layoutType : dragLayout.layoutType; | |
671 | |
672 if (layoutType == options.DisplayLayoutType.LEFT || | |
673 layoutType == options.DisplayLayoutType.RIGHT) { | |
674 var top = Math.max( | |
675 draggingDiv.offsetTop, | |
676 baseDiv.offsetTop - draggingDiv.offsetHeight + MIN_OFFSET_OVERLAP); | |
677 top = Math.min( | |
678 top, baseDiv.offsetTop + baseDiv.offsetHeight - MIN_OFFSET_OVERLAP); | |
679 draggingDiv.style.top = top + 'px'; | |
680 } else { | |
681 var left = Math.max( | |
682 draggingDiv.offsetLeft, | |
683 baseDiv.offsetLeft - draggingDiv.offsetWidth + MIN_OFFSET_OVERLAP); | |
684 left = Math.min( | |
685 left, | |
686 baseDiv.offsetLeft + baseDiv.offsetWidth - MIN_OFFSET_OVERLAP); | |
687 draggingDiv.style.left = left + 'px'; | |
688 } | |
689 if (dragLayout.originalPosition.x != draggingDiv.offsetLeft || | |
690 dragLayout.originalPosition.y != draggingDiv.offsetTop) { | |
691 this.sendDragResult_(); | 465 this.sendDragResult_(); |
692 } | |
693 | 466 |
694 this.dragInfo_ = null; | 467 this.dragInfo_ = null; |
695 | 468 |
696 return false; | 469 return false; |
697 }, | 470 }, |
698 | 471 |
699 /** | 472 /** |
700 * Updates the description of selected display section for mirroring mode. | 473 * Updates the description of selected display section for mirroring mode. |
701 * @private | 474 * @private |
702 */ | 475 */ |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
738 resolution.disabled = true; | 511 resolution.disabled = true; |
739 }, | 512 }, |
740 | 513 |
741 /** | 514 /** |
742 * Updates the description of selected display section for the selected | 515 * Updates the description of selected display section for the selected |
743 * display. | 516 * display. |
744 * @param {options.DisplayInfo} display The selected display object. | 517 * @param {options.DisplayInfo} display The selected display object. |
745 * @private | 518 * @private |
746 */ | 519 */ |
747 updateSelectedDisplaySectionForDisplay_: function(display) { | 520 updateSelectedDisplaySectionForDisplay_: function(display) { |
748 var displayLayout = this.displayLayoutMap_[display.id]; | 521 var displayLayout = |
522 this.displayLayoutManager_.getDisplayLayout(display.id); | |
749 var arrow = $('display-configuration-arrow'); | 523 var arrow = $('display-configuration-arrow'); |
750 arrow.hidden = false; | 524 arrow.hidden = false; |
751 // Adding 1 px to the position to fit the border line and the border in | 525 // Adding 1 px to the position to fit the border line and the border in |
752 // arrow precisely. | 526 // arrow precisely. |
753 arrow.style.top = $('display-configurations').offsetTop - | 527 arrow.style.top = $('display-configurations').offsetTop - |
754 arrow.offsetHeight / 2 + 'px'; | 528 arrow.offsetHeight / 2 + 'px'; |
755 arrow.style.left = displayLayout.div.offsetLeft + | 529 arrow.style.left = displayLayout.div.offsetLeft + |
756 displayLayout.div.offsetWidth / 2 - arrow.offsetWidth / 2 + 'px'; | 530 displayLayout.div.offsetWidth / 2 - arrow.offsetWidth / 2 + 'px'; |
757 | 531 |
758 $('display-options-set-primary').disabled = display.isPrimary; | 532 $('display-options-set-primary').disabled = display.isPrimary; |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
897 if (i != numDisplays - 1) | 671 if (i != numDisplays - 1) |
898 div.classList.add('display-mirrored'); | 672 div.classList.add('display-mirrored'); |
899 this.displaysView_.appendChild(div); | 673 this.displaysView_.appendChild(div); |
900 } | 674 } |
901 }, | 675 }, |
902 | 676 |
903 /** | 677 /** |
904 * Creates a DisplayLayout object representing the display. | 678 * Creates a DisplayLayout object representing the display. |
905 * @param {!options.DisplayInfo} display | 679 * @param {!options.DisplayInfo} display |
906 * @param {!options.DisplayLayoutType} layoutType | 680 * @param {!options.DisplayLayoutType} layoutType |
681 * @param {string} parentId | |
907 * @return {!options.DisplayLayout} | 682 * @return {!options.DisplayLayout} |
908 * @private | 683 * @private |
909 */ | 684 */ |
910 createDisplayLayout_: function(display, layoutType) { | 685 createDisplayLayout_: function(display, layoutType, parentId) { |
911 return { | 686 return { |
912 bounds: display.bounds, | 687 bounds: display.bounds, |
913 div: null, | 688 div: null, |
914 id: display.id, | 689 id: display.id, |
915 isPrimary: display.isPrimary, | |
916 layoutType: layoutType, | 690 layoutType: layoutType, |
917 name: display.name, | 691 name: display.name, |
918 originalPosition: {x: 0, y: 0} | 692 offset: 0, |
693 originalPosition: {x: 0, y: 0}, | |
694 parentId: parentId | |
919 }; | 695 }; |
920 }, | 696 }, |
921 | 697 |
922 /** | 698 /** |
923 * Creates a div element representing the specified display. | |
924 * @param {!options.DisplayLayout} displayLayout | |
925 * @param {options.DisplayLayoutType} layoutType The layout type for the | |
926 * secondary display. | |
927 * @param {!options.DisplayPosition} offset The offset to the center of the | |
928 * display area. | |
929 * @private | |
930 */ | |
931 createDisplayLayoutDiv_: function(displayLayout, layoutType, offset) { | |
932 var div = /** @type {!HTMLElement} */ (document.createElement('div')); | |
933 div.className = 'displays-display'; | |
934 div.classList.toggle( | |
935 'displays-focused', displayLayout.id == this.focusedId_); | |
936 | |
937 // div needs to be added to the DOM tree first, otherwise offsetHeight for | |
938 // nameContainer below cannot be computed. | |
939 this.displaysView_.appendChild(div); | |
940 | |
941 var nameContainer = document.createElement('div'); | |
942 nameContainer.textContent = displayLayout.name; | |
943 div.appendChild(nameContainer); | |
944 | |
945 var bounds = displayLayout.bounds; | |
946 div.style.width = Math.floor(bounds.width * this.visualScale_) + 'px'; | |
947 var newHeight = Math.floor(bounds.height * this.visualScale_); | |
948 div.style.height = newHeight + 'px'; | |
949 nameContainer.style.marginTop = | |
950 (newHeight - nameContainer.offsetHeight) / 2 + 'px'; | |
951 | |
952 div.onmousedown = this.onMouseDown_.bind(this); | |
953 div.ontouchstart = this.onTouchStart_.bind(this); | |
954 | |
955 if (displayLayout.isPrimary) { | |
956 div.style.left = | |
957 Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; | |
958 div.style.top = | |
959 Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; | |
960 } else { | |
961 // Don't trust the secondary display's x or y, because it may cause a | |
962 // 1px gap due to rounding, which will create a fake update on end | |
963 // dragging. See crbug.com/386401 | |
964 var primaryDiv = this.displayLayoutMap_[this.primaryDisplayId_].div; | |
965 switch (layoutType) { | |
966 case options.DisplayLayoutType.TOP: | |
967 div.style.left = | |
968 Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; | |
969 div.style.top = primaryDiv.offsetTop - div.offsetHeight + 'px'; | |
970 break; | |
971 case options.DisplayLayoutType.RIGHT: | |
972 div.style.left = | |
973 primaryDiv.offsetLeft + primaryDiv.offsetWidth + 'px'; | |
974 div.style.top = | |
975 Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; | |
976 break; | |
977 case options.DisplayLayoutType.BOTTOM: | |
978 div.style.left = | |
979 Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; | |
980 div.style.top = | |
981 primaryDiv.offsetTop + primaryDiv.offsetHeight + 'px'; | |
982 break; | |
983 case options.DisplayLayoutType.LEFT: | |
984 div.style.left = primaryDiv.offsetLeft - div.offsetWidth + 'px'; | |
985 div.style.top = | |
986 Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; | |
987 break; | |
988 } | |
989 } | |
990 | |
991 displayLayout.div = div; | |
992 displayLayout.originalPosition.x = div.offsetLeft; | |
993 displayLayout.originalPosition.y = div.offsetTop; | |
994 }, | |
995 | |
996 /** | |
997 * Layouts the display rectangles according to the current layout_. | 699 * Layouts the display rectangles according to the current layout_. |
998 * @param {options.DisplayLayoutType} layoutType | 700 * @param {options.DisplayLayoutType} layoutType |
999 * @private | 701 * @private |
1000 */ | 702 */ |
1001 layoutDisplays_: function(layoutType) { | 703 layoutDisplays_: function(layoutType) { |
1002 var maxWidth = 0; | 704 var maxWidth = 0; |
1003 var maxHeight = 0; | 705 var maxHeight = 0; |
1004 var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; | 706 var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; |
1005 this.primaryDisplayId_ = ''; | 707 var primaryDisplayId = ''; |
1006 this.secondaryDisplayId_ = ''; | |
1007 for (var i = 0; i < this.displays_.length; i++) { | 708 for (var i = 0; i < this.displays_.length; i++) { |
1008 var display = this.displays_[i]; | 709 var display = this.displays_[i]; |
1009 if (display.isPrimary) | 710 if (display.isPrimary && primaryDisplayId == '') |
1010 this.primaryDisplayId_ = display.id; | 711 primaryDisplayId = display.id; |
1011 else if (this.secondaryDisplayId_ == '') | |
1012 this.secondaryDisplayId_ = display.id; | |
1013 | 712 |
1014 var bounds = display.bounds; | 713 var bounds = display.bounds; |
1015 boundingBox.left = Math.min(boundingBox.left, bounds.left); | 714 boundingBox.left = Math.min(boundingBox.left, bounds.left); |
1016 boundingBox.right = | 715 boundingBox.right = |
1017 Math.max(boundingBox.right, bounds.left + bounds.width); | 716 Math.max(boundingBox.right, bounds.left + bounds.width); |
1018 boundingBox.top = Math.min(boundingBox.top, bounds.top); | 717 boundingBox.top = Math.min(boundingBox.top, bounds.top); |
1019 boundingBox.bottom = | 718 boundingBox.bottom = |
1020 Math.max(boundingBox.bottom, bounds.top + bounds.height); | 719 Math.max(boundingBox.bottom, bounds.top + bounds.height); |
1021 maxWidth = Math.max(maxWidth, bounds.width); | 720 maxWidth = Math.max(maxWidth, bounds.width); |
1022 maxHeight = Math.max(maxHeight, bounds.height); | 721 maxHeight = Math.max(maxHeight, bounds.height); |
1023 } | 722 } |
1024 if (this.primaryDisplayId_ == '') | 723 if (primaryDisplayId == '') |
1025 return; | 724 return; |
1026 | 725 |
1027 // Make the margin around the bounding box. | 726 // Make the margin around the bounding box. |
1028 var areaWidth = boundingBox.right - boundingBox.left + maxWidth; | 727 var areaWidth = boundingBox.right - boundingBox.left + maxWidth; |
1029 var areaHeight = boundingBox.bottom - boundingBox.top + maxHeight; | 728 var areaHeight = boundingBox.bottom - boundingBox.top + maxHeight; |
1030 | 729 |
1031 // Calculates the scale by the width since horizontal size is more strict. | 730 // Calculates the scale by the width since horizontal size is more strict. |
1032 // TODO(mukai): Adds the check of vertical size in case. | 731 // TODO(mukai): Adds the check of vertical size in case. |
1033 this.visualScale_ = Math.min( | 732 this.visualScale_ = Math.min( |
1034 VISUAL_SCALE, this.displaysView_.offsetWidth / areaWidth); | 733 VISUAL_SCALE, this.displaysView_.offsetWidth / areaWidth); |
1035 | 734 |
1036 // Prepare enough area for thisplays_view by adding the maximum height. | 735 // Prepare enough area for thisplays_view by adding the maximum height. |
1037 this.displaysView_.style.height = | 736 this.displaysView_.style.height = |
1038 Math.ceil(areaHeight * this.visualScale_) + 'px'; | 737 Math.ceil(areaHeight * this.visualScale_) + 'px'; |
1039 | 738 |
1040 // Centering the bounding box of the display rectangles. | 739 // Centering the bounding box of the display rectangles. |
1041 var offset = { | 740 var offset = { |
1042 x: Math.floor( | 741 x: Math.floor( |
1043 this.displaysView_.offsetWidth / 2 - | 742 this.displaysView_.offsetWidth / 2 - |
1044 (boundingBox.right + boundingBox.left) * this.visualScale_ / 2), | 743 (boundingBox.right + boundingBox.left) * this.visualScale_ / 2), |
1045 y: Math.floor( | 744 y: Math.floor( |
1046 this.displaysView_.offsetHeight / 2 - | 745 this.displaysView_.offsetHeight / 2 - |
1047 (boundingBox.bottom + boundingBox.top) * this.visualScale_ / 2) | 746 (boundingBox.bottom + boundingBox.top) * this.visualScale_ / 2) |
1048 }; | 747 }; |
1049 | 748 |
1050 // Layout the display rectangles. First layout the primary display and | 749 // Create the layout manager. |
1051 // then layout any secondary displays. | 750 this.displayLayoutManager_ = |
1052 this.createDisplayLayoutDiv_( | 751 new options.DisplayLayoutManager(this.visualScale_); |
1053 this.displayLayoutMap_[this.primaryDisplayId_], layoutType, offset); | 752 |
753 // Create the display layouts. | |
1054 for (var i = 0; i < this.displays_.length; i++) { | 754 for (var i = 0; i < this.displays_.length; i++) { |
1055 if (!this.displays_[i].isPrimary) { | 755 var display = this.displays_[i]; |
1056 this.createDisplayLayoutDiv_( | 756 var parentId = display.isPrimary ? '' : primaryDisplayId; |
1057 this.displayLayoutMap_[this.displays_[i].id], layoutType, offset); | 757 var layout = this.createDisplayLayout_(display, layoutType, parentId); |
1058 } | 758 this.displayLayoutManager_.addDisplayLayout(layout); |
759 } | |
760 | |
761 // Create the display divs. | |
762 this.displayLayoutManager_.createDisplayLayoutDivs( | |
763 this.displaysView_, offset); | |
764 | |
765 // Set the div callbacks and highlight the focused div. | |
766 for (var i = 0; i < this.displays_.length; i++) { | |
767 var id = this.displays_[i].id; | |
768 var div = this.displayLayoutManager_.getDisplayLayout(id).div; | |
769 div.onmousedown = this.onMouseDown_.bind(this); | |
770 div.ontouchstart = this.onTouchStart_.bind(this); | |
771 if (id == this.focusedId_) | |
772 div.classList.toggle('displays-focused', true); | |
xiyuan
2016/01/26 18:06:53
nit: Can we get rid of the "if" and just do
div.c
stevenjb
2016/01/26 19:36:15
Done.
| |
1059 } | 773 } |
1060 }, | 774 }, |
1061 | 775 |
1062 /** | 776 /** |
1063 * Called when the display arrangement has changed. | 777 * Called when the display arrangement has changed. |
1064 * @param {options.MultiDisplayMode} mode multi display mode. | 778 * @param {options.MultiDisplayMode} mode multi display mode. |
1065 * @param {!Array<!options.DisplayInfo>} displays The list of the display | 779 * @param {!Array<!options.DisplayInfo>} displays The list of the display |
1066 * information. | 780 * information. |
1067 * @param {options.DisplayLayoutType} layoutType The layout strategy. | 781 * @param {options.DisplayLayoutType} layoutType The layout type for the |
782 * secondary display. | |
1068 * @param {number} offset The offset of the secondary display. | 783 * @param {number} offset The offset of the secondary display. |
1069 * @private | 784 * @private |
1070 */ | 785 */ |
1071 onDisplayChanged_: function(mode, displays, layoutType, offset) { | 786 onDisplayChanged_: function(mode, displays, layoutType, offset) { |
1072 if (!this.visible) | 787 if (!this.visible) |
1073 return; | 788 return; |
1074 | 789 |
1075 this.displays_ = displays; | 790 this.displays_ = displays; |
1076 this.displayLayoutMap_ = {}; | |
1077 for (var i = 0; i < displays.length; i++) { | |
1078 var display = displays[i]; | |
1079 this.displayLayoutMap_[display.id] = | |
1080 this.createDisplayLayout_(display, layoutType); | |
1081 } | |
1082 | 791 |
1083 var mirroring = mode == options.MultiDisplayMode.MIRRORING; | 792 var mirroring = mode == options.MultiDisplayMode.MIRRORING; |
1084 var unifiedDesktopEnabled = mode == options.MultiDisplayMode.UNIFIED; | 793 var unifiedDesktopEnabled = mode == options.MultiDisplayMode.UNIFIED; |
1085 | 794 |
1086 // Focus to the first display next to the primary one when |displays_| | 795 // Focus to the first display next to the primary one when |displays_| |
1087 // is updated. | 796 // is updated. |
1088 if (mirroring) { | 797 if (mirroring) { |
1089 this.focusedId_ = ''; | 798 this.focusedId_ = ''; |
1090 } else if ( | 799 } else if ( |
1091 this.focusedId_ == '' || this.mirroring_ != mirroring || | 800 this.focusedId_ == '' || this.mirroring_ != mirroring || |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1128 mode, displays, layoutType, offset) { | 837 mode, displays, layoutType, offset) { |
1129 DisplayOptions.getInstance().onDisplayChanged_( | 838 DisplayOptions.getInstance().onDisplayChanged_( |
1130 mode, displays, layoutType, offset); | 839 mode, displays, layoutType, offset); |
1131 }; | 840 }; |
1132 | 841 |
1133 // Export | 842 // Export |
1134 return { | 843 return { |
1135 DisplayOptions: DisplayOptions | 844 DisplayOptions: DisplayOptions |
1136 }; | 845 }; |
1137 }); | 846 }); |
OLD | NEW |