Index: chrome/browser/resources/options/chromeos/display_layout_manager.js |
diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_layout_manager.js |
similarity index 23% |
copy from chrome/browser/resources/options/chromeos/display_options.js |
copy to chrome/browser/resources/options/chromeos/display_layout_manager.js |
index 961fc9d5aa6814a0143a0766468aaf1f5ad3c202..f9eb31c507b005e98a0cc871376e6176cf994722 100644 |
--- a/chrome/browser/resources/options/chromeos/display_options.js |
+++ b/chrome/browser/resources/options/chromeos/display_layout_manager.js |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -17,17 +17,6 @@ options.DisplayLayoutType = { |
}; |
/** |
- * Enumeration of multi display mode. These values must match the C++ values in |
- * ash::DisplayManager. |
- * @enum {number} |
- */ |
-options.MultiDisplayMode = { |
- EXTENDED: 0, |
- MIRRORING: 1, |
- UNIFIED: 2, |
-}; |
- |
-/** |
* @typedef {{ |
* left: number, |
* top: number, |
@@ -47,70 +36,26 @@ options.DisplayPosition; |
/** |
* @typedef {{ |
- * width: number, |
- * height: number, |
- * originalWidth: number, |
- * originalHeight: number, |
- * deviceScaleFactor: number, |
- * scale: number, |
- * refreshRate: number, |
- * isBest: boolean, |
- * selected: boolean |
- * }} |
- */ |
-options.DisplayMode; |
- |
-/** |
- * @typedef {{ |
- * profileId: number, |
- * name: string |
- * }} |
- */ |
-options.ColorProfile; |
- |
-/** |
- * @typedef {{ |
- * availableColorProfiles: !Array<!options.ColorProfile>, |
- * bounds: !options.DisplayBounds, |
- * colorProfileId: number, |
- * id: string, |
- * isInternal: boolean, |
- * isPrimary: boolean, |
- * resolutions: !Array<!options.DisplayMode>, |
- * name: string, |
- * rotation: number |
- * }} |
- */ |
-options.DisplayInfo; |
- |
-/** |
- * @typedef {{ |
* bounds: !options.DisplayBounds, |
* div: ?HTMLElement, |
* id: string, |
- * isPrimary: boolean, |
* layoutType: options.DisplayLayoutType, |
* name: string, |
- * originalPosition: !options.DisplayPosition |
+ * offset: number, |
+ * originalPosition: !options.DisplayPosition, |
+ * parentId: string |
* }} |
*/ |
options.DisplayLayout; |
cr.define('options', function() { |
- var Page = cr.ui.pageManager.Page; |
- var PageManager = cr.ui.pageManager.PageManager; |
- |
- // The scale ratio of the display rectangle to its original size. |
- /** @const */ var VISUAL_SCALE = 1 / 10; |
- |
- // The number of pixels to share the edges between displays. |
- /** @const */ var MIN_OFFSET_OVERLAP = 5; |
+ 'use strict'; |
/** |
* Gets the layout type of |point| relative to |rect|. |
* @param {!options.DisplayBounds} rect The base rectangle. |
* @param {!options.DisplayPosition} point The point to check the position. |
- * @return {options.DisplayLayoutType} The position of the calculated point. |
+ * @return {options.DisplayLayoutType} |
*/ |
function getPositionToRectangle(rect, point) { |
// Separates the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of |
@@ -160,346 +105,87 @@ cr.define('options', function() { |
} |
/** |
- * Encapsulated handling of the 'Display' page. |
+ * @param {number} visualScale |
* @constructor |
- * @extends {cr.ui.pageManager.Page} |
*/ |
- function DisplayOptions() { |
- Page.call(this, 'display', |
- loadTimeData.getString('displayOptionsPageTabTitle'), |
- 'display-options-page'); |
+ function DisplayLayoutManager(visualScale) { |
+ this.visualScale_ = visualScale; |
} |
- cr.addSingletonGetter(DisplayOptions); |
- |
- DisplayOptions.prototype = { |
- __proto__: Page.prototype, |
- |
+ // Helper class for display layout management. Implements logic for laying |
+ // out two displays. |
+ DisplayLayoutManager.prototype = { |
/** |
- * Whether the current output status is mirroring displays or not. |
- * @type {boolean} |
- * @private |
- */ |
- mirroring_: false, |
- |
- /** |
- * Whether the unified desktop is enable or not. |
- * @type {boolean} |
- * @private |
- */ |
- unifiedDesktopEnabled_: false, |
- |
- /** |
- * Whether the unified desktop option should be present. |
- * @type {boolean} |
- * @private |
- */ |
- showUnifiedDesktopOption_: false, |
- |
- /** |
- * The array of current output displays. It also contains the display |
- * rectangles currently rendered on screen. |
- * @type {!Array<!options.DisplayInfo>} |
- * @private |
- */ |
- displays_: [], |
- |
- /** |
- * An object containing DisplayLayout objects for each entry in |displays_|. |
+ * An object containing DisplayLayout objects for each entry in |
+ * |displays_|. |
* @type {!Object<!options.DisplayLayout>} |
* @private |
*/ |
displayLayoutMap_: {}, |
/** |
- * The id of the currently focused display, or empty for none. |
- * @type {string} |
- * @private |
- */ |
- focusedId_: '', |
- |
- /** |
- * The primary display id. |
- * @type {string} |
- * @private |
- */ |
- primaryDisplayId_: '', |
- |
- /** |
- * The secondary display id. |
- * @type {string} |
- * @private |
- */ |
- secondaryDisplayId_: '', |
- |
- /** |
- * Drag info. |
- * @type {?{displayId: string, |
- * originalLocation: !options.DisplayPosition, |
- * eventLocation: !options.DisplayPosition}} |
- * @private |
- */ |
- dragInfo_: null, |
- |
- /** |
- * The container div element which contains all of the display rectangles. |
- * @type {?Element} |
- * @private |
- */ |
- displaysView_: null, |
- |
- /** |
* The scale factor of the actual display size to the drawn display |
- * rectangle size. |
+ * rectangle size. Set to the correct value for the UI in the constructor. |
* @type {number} |
* @private |
*/ |
- visualScale_: VISUAL_SCALE, |
- |
- /** |
- * The location where the last touch event happened. This is used to |
- * prevent unnecessary dragging events happen. Set to null unless it's |
- * during touch events. |
- * @type {?options.DisplayPosition} |
- * @private |
- */ |
- lastTouchLocation_: null, |
+ visualScale_: 1, |
/** |
- * Whether the display settings can be shown. |
- * @type {boolean} |
- * @private |
+ * Adds a display to the layout map. |
+ * @param {options.DisplayLayout} displayLayout |
*/ |
- enabled_: true, |
- |
- /** @override */ |
- initializePage: function() { |
- Page.prototype.initializePage.call(this); |
- |
- $('display-options-select-mirroring').onchange = (function() { |
- this.mirroring_ = |
- $('display-options-select-mirroring').value == 'mirroring'; |
- chrome.send('setMirroring', [this.mirroring_]); |
- }).bind(this); |
- |
- var container = $('display-options-displays-view-host'); |
- container.onmousemove = this.onMouseMove_.bind(this); |
- window.addEventListener('mouseup', this.endDragging_.bind(this), true); |
- container.ontouchmove = this.onTouchMove_.bind(this); |
- container.ontouchend = this.endDragging_.bind(this); |
- |
- $('display-options-set-primary').onclick = (function() { |
- chrome.send('setPrimary', [this.focusedId_]); |
- }).bind(this); |
- $('display-options-resolution-selection').onchange = (function(ev) { |
- var display = this.getDisplayInfoFromId(this.focusedId_); |
- var resolution = display.resolutions[ev.target.value]; |
- chrome.send('setDisplayMode', [this.focusedId_, resolution]); |
- }).bind(this); |
- $('display-options-orientation-selection').onchange = (function(ev) { |
- var rotation = parseInt(ev.target.value, 10); |
- chrome.send('setRotation', [this.focusedId_, rotation]); |
- }).bind(this); |
- $('display-options-color-profile-selection').onchange = (function(ev) { |
- chrome.send('setColorProfile', [this.focusedId_, ev.target.value]); |
- }).bind(this); |
- $('selected-display-start-calibrating-overscan').onclick = (function() { |
- // Passes the target display ID. Do not specify it through URL hash, |
- // we do not care back/forward. |
- var displayOverscan = options.DisplayOverscan.getInstance(); |
- displayOverscan.setDisplayId(this.focusedId_); |
- PageManager.showPageByName('displayOverscan'); |
- chrome.send('coreOptionsUserMetricsAction', |
- ['Options_DisplaySetOverscan']); |
- }).bind(this); |
- |
- $('display-options-done').onclick = function() { |
- PageManager.closeOverlay(); |
- }; |
- |
- $('display-options-toggle-unified-desktop').onclick = (function() { |
- this.unifiedDesktopEnabled_ = !this.unifiedDesktopEnabled_; |
- chrome.send('setUnifiedDesktopEnabled', |
- [this.unifiedDesktopEnabled_]); |
- }).bind(this); |
- }, |
- |
- /** @override */ |
- didShowPage: function() { |
- var optionTitles = document.getElementsByClassName( |
- 'selected-display-option-title'); |
- var maxSize = 0; |
- for (var i = 0; i < optionTitles.length; i++) |
- maxSize = Math.max(maxSize, optionTitles[i].clientWidth); |
- for (var i = 0; i < optionTitles.length; i++) |
- optionTitles[i].style.width = maxSize + 'px'; |
- chrome.send('getDisplayInfo'); |
- }, |
- |
- /** @override */ |
- canShowPage: function() { return this.enabled_; }, |
- |
- /** |
- * Enables or disables the page. When disabled, the page will not be able to |
- * open, and will close if currently opened. |
- * @param {boolean} enabled Whether the page should be enabled. |
- * @param {boolean} showUnifiedDesktop Whether the unified desktop option |
- * should be present. |
- */ |
- setEnabled: function(enabled, showUnifiedDesktop) { |
- if (this.enabled_ == enabled && |
- this.showUnifiedDesktopOption_ == showUnifiedDesktop) { |
- return; |
- } |
- this.enabled_ = enabled; |
- this.showUnifiedDesktopOption_ = showUnifiedDesktop; |
- if (!enabled && this.visible) |
- PageManager.closeOverlay(); |
- }, |
- |
- /** |
- * Mouse move handler for dragging display rectangle. |
- * @param {Event} e The mouse move event. |
- * @private |
- */ |
- onMouseMove_: function(e) { |
- return this.processDragging_(e, {x: e.pageX, y: e.pageY}); |
- }, |
- |
- /** |
- * Touch move handler for dragging display rectangle. |
- * @param {Event} e The touch move event. |
- * @private |
- */ |
- onTouchMove_: function(e) { |
- if (e.touches.length != 1) |
- return true; |
- |
- var touchLocation = {x: e.touches[0].pageX, y: e.touches[0].pageY}; |
- // Touch move events happen even if the touch location doesn't change, but |
- // it doesn't need to process the dragging. Since sometimes the touch |
- // position changes slightly even though the user doesn't think to move |
- // the finger, very small move is just ignored. |
- /** @const */ var IGNORABLE_TOUCH_MOVE_PX = 1; |
- var xDiff = Math.abs(touchLocation.x - this.lastTouchLocation_.x); |
- var yDiff = Math.abs(touchLocation.y - this.lastTouchLocation_.y); |
- if (xDiff <= IGNORABLE_TOUCH_MOVE_PX && |
- yDiff <= IGNORABLE_TOUCH_MOVE_PX) { |
- return true; |
- } |
- |
- this.lastTouchLocation_ = touchLocation; |
- return this.processDragging_(e, touchLocation); |
- }, |
- |
- /** |
- * Mouse down handler for dragging display rectangle. |
- * @param {Event} e The mouse down event. |
- * @private |
- */ |
- onMouseDown_: function(e) { |
- if (this.mirroring_) |
- return true; |
- |
- if (e.button != 0) |
- return true; |
- |
- e.preventDefault(); |
- var target = assertInstanceof(e.target, HTMLElement); |
- return this.startDragging_(target, {x: e.pageX, y: e.pageY}); |
- }, |
- |
- /** |
- * Touch start handler for dragging display rectangle. |
- * @param {Event} e The touch start event. |
- * @private |
- */ |
- onTouchStart_: function(e) { |
- if (this.mirroring_) |
- return true; |
- |
- if (e.touches.length != 1) |
- return false; |
- |
- e.preventDefault(); |
- var touch = e.touches[0]; |
- this.lastTouchLocation_ = {x: touch.pageX, y: touch.pageY}; |
- var target = assertInstanceof(e.target, HTMLElement); |
- return this.startDragging_(target, this.lastTouchLocation_); |
+ addDisplayLayout: function(displayLayout) { |
+ this.displayLayoutMap_[displayLayout.id] = displayLayout; |
}, |
/** |
+ * Returns the layout type for |id|. |
* @param {string} id |
- * @return {options.DisplayInfo} |
+ * @return {options.DisplayLayout} |
*/ |
- getDisplayInfoFromId(id) { |
- return this.displays_.find(function(display) { |
- return display.id == id; |
- }); |
- }, |
+ getDisplayLayout: function(id) { return this.displayLayoutMap_[id]; }, |
/** |
- * Collects the current data and sends it to Chrome. |
- * @private |
+ * Creates a div for each entry in displayLayoutMap_. |
+ * @param {!Element} parentElement The parent element to contain the divs. |
+ * @param {!options.DisplayPosition} offset The offset to the center of |
+ * the display area. |
*/ |
- sendDragResult_: function() { |
- // Offset is calculated from top or left edge. |
- var primary = this.displayLayoutMap_[this.primaryDisplayId_]; |
- var secondary = this.displayLayoutMap_[this.secondaryDisplayId_]; |
- var layoutType = secondary.layoutType; |
- var offset; |
- if (layoutType == options.DisplayLayoutType.LEFT || |
- layoutType == options.DisplayLayoutType.RIGHT) { |
- offset = secondary.div.offsetTop - primary.div.offsetTop; |
- } else { |
- offset = secondary.div.offsetLeft - primary.div.offsetLeft; |
+ createDisplayLayoutDivs: function(parentElement, offset) { |
+ for (var id in this.displayLayoutMap_) { |
+ var layout = this.displayLayoutMap_[id]; |
+ if (layout.div) { |
+ // Parent divs must be created before children, so the div for this |
+ // entry may have already been created. |
+ continue; |
+ } |
+ this.createDisplayLayoutDiv_(id, parentElement, offset); |
} |
- offset = Math.floor(offset / this.visualScale_); |
- chrome.send('setDisplayLayout', [secondary.id, layoutType, offset]); |
}, |
/** |
- * Processes the actual dragging of display rectangle. |
- * @param {Event} e The event which triggers this drag. |
- * @param {options.DisplayPosition} eventLocation The location where the |
- * event happens. |
+ * Update the location of display |id| to |newPosition|. |
+ * @param {string} id |
+ * @param {options.DisplayPosition} newPosition |
* @private |
*/ |
- processDragging_: function(e, eventLocation) { |
- if (!this.dragInfo_) |
- return true; |
- |
- e.preventDefault(); |
- |
- // Note that current code of moving display-rectangles doesn't work |
- // if there are >=3 displays. This is our assumption for M21. |
- // TODO(mukai): Fix the code to allow >=3 displays. |
- var dragInfo = this.dragInfo_; |
- /** @type {options.DisplayPosition} */ var newPosition = { |
- x: dragInfo.originalLocation.x + |
- (eventLocation.x - dragInfo.eventLocation.x), |
- y: dragInfo.originalLocation.y + |
- (eventLocation.y - dragInfo.eventLocation.y) |
- }; |
- |
- var dragLayout = this.displayLayoutMap_[dragInfo.displayId]; |
- var draggingDiv = dragLayout.div; |
- |
- var baseDisplayId = dragLayout.isPrimary ? this.secondaryDisplayId_ : |
- this.primaryDisplayId_; |
- var baseLayout = this.displayLayoutMap_[baseDisplayId]; |
+ updatePosition: function(id, newPosition) { |
+ var displayLayout = this.displayLayoutMap_[id]; |
+ var div = displayLayout.div; |
+ var baseLayout = this.getBaseLayout_(displayLayout); |
var baseDiv = baseLayout.div; |
newPosition.x = snapToEdge( |
- newPosition.x, draggingDiv.offsetWidth, baseDiv.offsetLeft, |
+ newPosition.x, div.offsetWidth, baseDiv.offsetLeft, |
baseDiv.offsetWidth); |
newPosition.y = snapToEdge( |
- newPosition.y, draggingDiv.offsetHeight, baseDiv.offsetTop, |
+ newPosition.y, div.offsetHeight, baseDiv.offsetTop, |
baseDiv.offsetHeight); |
/** @type {!options.DisplayPosition} */ var newCenter = { |
- x: newPosition.x + draggingDiv.offsetWidth / 2, |
- y: newPosition.y + draggingDiv.offsetHeight / 2 |
+ x: newPosition.x + div.offsetWidth / 2, |
+ y: newPosition.y + div.offsetHeight / 2 |
}; |
/** @type {!options.DisplayBounds} */ var baseBounds = { |
@@ -509,10 +195,13 @@ cr.define('options', function() { |
height: baseDiv.offsetHeight |
}; |
- var isPrimary = dragLayout.isPrimary; |
- // layoutType is always stored in the secondary layout. |
+ // This implementation considers only the case of two displays, i.e |
+ // a single parent with one child. |
+ var isPrimary = displayLayout.parentId == ''; |
+ |
+ // layoutType is always stored in the child layout. |
var layoutType = |
- isPrimary ? baseLayout.layoutType : dragLayout.layoutType; |
+ isPrimary ? baseLayout.layoutType : displayLayout.layoutType; |
switch (getPositionToRectangle(baseBounds, newCenter)) { |
case options.DisplayLayoutType.RIGHT: |
@@ -538,21 +227,21 @@ cr.define('options', function() { |
if (newPosition.y > baseDiv.offsetTop + baseDiv.offsetHeight) |
layoutType = isPrimary ? options.DisplayLayoutType.TOP : |
options.DisplayLayoutType.BOTTOM; |
- else if (newPosition.y + draggingDiv.offsetHeight < baseDiv.offsetTop) |
+ else if (newPosition.y + div.offsetHeight < baseDiv.offsetTop) |
layoutType = isPrimary ? options.DisplayLayoutType.BOTTOM : |
options.DisplayLayoutType.TOP; |
} else { |
if (newPosition.x > baseDiv.offsetLeft + baseDiv.offsetWidth) |
layoutType = isPrimary ? options.DisplayLayoutType.LEFT : |
options.DisplayLayoutType.RIGHT; |
- else if (newPosition.x + draggingDiv.offsetWidth < baseDiv.offsetLeft) |
+ else if (newPosition.x + div.offsetWidth < baseDiv.offsetLeft) |
layoutType = isPrimary ? options.DisplayLayoutType.RIGHT : |
options.DisplayLayoutType.LEFT; |
} |
var layoutToBase; |
if (!isPrimary) { |
- dragLayout.layoutType = layoutType; |
+ displayLayout.layoutType = layoutType; |
layoutToBase = layoutType; |
} else { |
baseLayout.layoutType = layoutType; |
@@ -574,369 +263,93 @@ cr.define('options', function() { |
switch (layoutToBase) { |
case options.DisplayLayoutType.RIGHT: |
- draggingDiv.style.left = |
- baseDiv.offsetLeft + baseDiv.offsetWidth + 'px'; |
- draggingDiv.style.top = newPosition.y + 'px'; |
+ div.style.left = baseDiv.offsetLeft + baseDiv.offsetWidth + 'px'; |
+ div.style.top = newPosition.y + 'px'; |
break; |
case options.DisplayLayoutType.LEFT: |
- draggingDiv.style.left = |
- baseDiv.offsetLeft - draggingDiv.offsetWidth + 'px'; |
- draggingDiv.style.top = newPosition.y + 'px'; |
+ div.style.left = baseDiv.offsetLeft - div.offsetWidth + 'px'; |
+ div.style.top = newPosition.y + 'px'; |
break; |
case options.DisplayLayoutType.TOP: |
- draggingDiv.style.top = |
- baseDiv.offsetTop - draggingDiv.offsetHeight + 'px'; |
- draggingDiv.style.left = newPosition.x + 'px'; |
+ div.style.top = baseDiv.offsetTop - div.offsetHeight + 'px'; |
+ div.style.left = newPosition.x + 'px'; |
break; |
case options.DisplayLayoutType.BOTTOM: |
- draggingDiv.style.top = |
- baseDiv.offsetTop + baseDiv.offsetHeight + 'px'; |
- draggingDiv.style.left = newPosition.x + 'px'; |
+ div.style.top = baseDiv.offsetTop + baseDiv.offsetHeight + 'px'; |
+ div.style.left = newPosition.x + 'px'; |
break; |
} |
- |
- return false; |
}, |
/** |
- * Start dragging of a display rectangle. |
- * @param {!HTMLElement} target The event target. |
- * @param {!options.DisplayPosition} eventLocation The event location. |
- * @private |
- */ |
- startDragging_: function(target, eventLocation) { |
- var oldFocusedId = this.focusedId_; |
- var newFocusedId; |
- var willUpdateDisplayDescription = false; |
- for (var i = 0; i < this.displays_.length; i++) { |
- var displayLayout = this.displayLayoutMap_[this.displays_[i].id]; |
- if (displayLayout.div == target || |
- (target.offsetParent && target.offsetParent == displayLayout.div)) { |
- newFocusedId = displayLayout.id; |
- break; |
- } |
- } |
- if (!newFocusedId) |
- return false; |
- |
- this.focusedId_ = newFocusedId; |
- willUpdateDisplayDescription = newFocusedId != oldFocusedId; |
- |
- for (var i = 0; i < this.displays_.length; i++) { |
- var displayLayout = this.displayLayoutMap_[this.displays_[i].id]; |
- displayLayout.div.className = 'displays-display'; |
- if (displayLayout.id != this.focusedId_) |
- continue; |
- |
- displayLayout.div.classList.add('displays-focused'); |
- if (this.displays_.length > 1) { |
- this.dragInfo_ = { |
- displayId: displayLayout.id, |
- originalLocation: { |
- x: displayLayout.div.offsetLeft, |
- y: displayLayout.div.offsetTop |
- }, |
- eventLocation: {x: eventLocation.x, y: eventLocation.y} |
- }; |
- } |
- } |
- |
- if (willUpdateDisplayDescription) |
- this.updateSelectedDisplayDescription_(); |
- return false; |
- }, |
- |
- /** |
- * finish the current dragging of displays. |
- * @param {Event} e The event which triggers this. |
- * @private |
+ * Called from the UI to finalize the location of display |id| after updates |
+ * are complete (e.g. after a drag was completed). |
+ * @param {string} id |
+ * @return {boolean} True if the final position differs from the original. |
*/ |
- endDragging_: function(e) { |
- this.lastTouchLocation_ = null; |
- if (!this.dragInfo_) |
- return false; |
- |
+ finalizePosition: function(id) { |
// Make sure the dragging location is connected. |
- var dragLayout = this.displayLayoutMap_[this.dragInfo_.displayId]; |
- var baseDisplayId = dragLayout.isPrimary ? this.secondaryDisplayId_ : |
- this.primaryDisplayId_; |
- |
- var baseLayout = this.displayLayoutMap_[baseDisplayId]; |
+ var displayLayout = this.displayLayoutMap_[id]; |
+ var div = displayLayout.div; |
+ var baseLayout = this.getBaseLayout_(displayLayout); |
var baseDiv = baseLayout.div; |
- var draggingDiv = dragLayout.div; |
- // layoutType is always stored in the secondary layout. |
+ var isPrimary = displayLayout.parentId == ''; |
var layoutType = |
- dragLayout.isPrimary ? baseLayout.layoutType : dragLayout.layoutType; |
+ isPrimary ? baseLayout.layoutType : displayLayout.layoutType; |
+ |
+ // The number of pixels to share the edges between displays. |
+ /** @const */ var MIN_OFFSET_OVERLAP = 5; |
if (layoutType == options.DisplayLayoutType.LEFT || |
layoutType == options.DisplayLayoutType.RIGHT) { |
var top = Math.max( |
- draggingDiv.offsetTop, |
- baseDiv.offsetTop - draggingDiv.offsetHeight + MIN_OFFSET_OVERLAP); |
+ div.offsetTop, |
+ baseDiv.offsetTop - div.offsetHeight + MIN_OFFSET_OVERLAP); |
top = Math.min( |
top, baseDiv.offsetTop + baseDiv.offsetHeight - MIN_OFFSET_OVERLAP); |
- draggingDiv.style.top = top + 'px'; |
+ div.style.top = top + 'px'; |
} else { |
var left = Math.max( |
- draggingDiv.offsetLeft, |
- baseDiv.offsetLeft - draggingDiv.offsetWidth + MIN_OFFSET_OVERLAP); |
+ div.offsetLeft, |
+ baseDiv.offsetLeft - div.offsetWidth + MIN_OFFSET_OVERLAP); |
left = Math.min( |
left, |
baseDiv.offsetLeft + baseDiv.offsetWidth - MIN_OFFSET_OVERLAP); |
- draggingDiv.style.left = left + 'px'; |
- } |
- if (dragLayout.originalPosition.x != draggingDiv.offsetLeft || |
- dragLayout.originalPosition.y != draggingDiv.offsetTop) { |
- this.sendDragResult_(); |
+ div.style.left = left + 'px'; |
} |
- this.dragInfo_ = null; |
- |
- return false; |
- }, |
- |
- /** |
- * Updates the description of selected display section for mirroring mode. |
- * @private |
- */ |
- updateSelectedDisplaySectionMirroring_: function() { |
- $('display-configuration-arrow').hidden = true; |
- $('display-options-set-primary').disabled = true; |
- $('display-options-select-mirroring').disabled = false; |
- $('selected-display-start-calibrating-overscan').disabled = true; |
- var display = this.displays_[0]; |
- var orientation = $('display-options-orientation-selection'); |
- orientation.disabled = false; |
- var orientationOptions = orientation.getElementsByTagName('option'); |
- var orientationIndex = Math.floor(display.rotation / 90); |
- orientationOptions[orientationIndex].selected = true; |
- $('selected-display-name').textContent = |
- loadTimeData.getString('mirroringDisplay'); |
- var resolution = $('display-options-resolution-selection'); |
- var option = document.createElement('option'); |
- option.value = 'default'; |
- option.textContent = display.bounds.width + 'x' + display.bounds.height; |
- resolution.appendChild(option); |
- resolution.disabled = true; |
- }, |
- |
- /** |
- * Updates the description of selected display section when no display is |
- * selected. |
- * @private |
- */ |
- updateSelectedDisplaySectionNoSelected_: function() { |
- $('display-configuration-arrow').hidden = true; |
- $('display-options-set-primary').disabled = true; |
- $('display-options-select-mirroring').disabled = true; |
- $('selected-display-start-calibrating-overscan').disabled = true; |
- $('display-options-orientation-selection').disabled = true; |
- $('selected-display-name').textContent = ''; |
- var resolution = $('display-options-resolution-selection'); |
- resolution.appendChild(document.createElement('option')); |
- resolution.disabled = true; |
- }, |
- |
- /** |
- * Updates the description of selected display section for the selected |
- * display. |
- * @param {options.DisplayInfo} display The selected display object. |
- * @private |
- */ |
- updateSelectedDisplaySectionForDisplay_: function(display) { |
- var displayLayout = this.displayLayoutMap_[display.id]; |
- var arrow = $('display-configuration-arrow'); |
- arrow.hidden = false; |
- // Adding 1 px to the position to fit the border line and the border in |
- // arrow precisely. |
- arrow.style.top = $('display-configurations').offsetTop - |
- arrow.offsetHeight / 2 + 'px'; |
- arrow.style.left = displayLayout.div.offsetLeft + |
- displayLayout.div.offsetWidth / 2 - arrow.offsetWidth / 2 + 'px'; |
- |
- $('display-options-set-primary').disabled = display.isPrimary; |
- $('display-options-select-mirroring').disabled = |
- (this.displays_.length <= 1 && !this.unifiedDesktopEnabled_); |
- $('selected-display-start-calibrating-overscan').disabled = |
- display.isInternal; |
- |
- var orientation = $('display-options-orientation-selection'); |
- orientation.disabled = this.unifiedDesktopEnabled_; |
- |
- var orientationOptions = orientation.getElementsByTagName('option'); |
- var orientationIndex = Math.floor(display.rotation / 90); |
- orientationOptions[orientationIndex].selected = true; |
- |
- $('selected-display-name').textContent = display.name; |
- |
- var resolution = $('display-options-resolution-selection'); |
- if (display.resolutions.length <= 1) { |
- var option = document.createElement('option'); |
- option.value = 'default'; |
- option.textContent = display.bounds.width + 'x' + display.bounds.height; |
- option.selected = true; |
- resolution.appendChild(option); |
- resolution.disabled = true; |
- } else { |
- var previousOption; |
- for (var i = 0; i < display.resolutions.length; i++) { |
- var option = document.createElement('option'); |
- option.value = i; |
- option.textContent = display.resolutions[i].width + 'x' + |
- display.resolutions[i].height; |
- if (display.resolutions[i].isBest) { |
- option.textContent += ' ' + |
- loadTimeData.getString('annotateBest'); |
- } else if (display.resolutions[i].isNative) { |
- option.textContent += ' ' + |
- loadTimeData.getString('annotateNative'); |
- } |
- if (display.resolutions[i].deviceScaleFactor && previousOption && |
- previousOption.textContent == option.textContent) { |
- option.textContent += |
- ' (' + display.resolutions[i].deviceScaleFactor + 'x)'; |
- } |
- option.selected = display.resolutions[i].selected; |
- resolution.appendChild(option); |
- previousOption = option; |
- } |
- resolution.disabled = (display.resolutions.length <= 1); |
- } |
- |
- if (display.availableColorProfiles.length <= 1) { |
- $('selected-display-color-profile-row').hidden = true; |
- } else { |
- $('selected-display-color-profile-row').hidden = false; |
- var profiles = $('display-options-color-profile-selection'); |
- profiles.innerHTML = ''; |
- for (var i = 0; i < display.availableColorProfiles.length; i++) { |
- var option = document.createElement('option'); |
- var colorProfile = display.availableColorProfiles[i]; |
- option.value = colorProfile.profileId; |
- option.textContent = colorProfile.name; |
- option.selected = (display.colorProfileId == colorProfile.profileId); |
- profiles.appendChild(option); |
- } |
- } |
- }, |
- |
- /** |
- * Updates the description of the selected display section. |
- * @private |
- */ |
- updateSelectedDisplayDescription_: function() { |
- var resolution = $('display-options-resolution-selection'); |
- resolution.textContent = ''; |
- var orientation = $('display-options-orientation-selection'); |
- var orientationOptions = orientation.getElementsByTagName('option'); |
- for (var i = 0; i < orientationOptions.length; i++) |
- orientationOptions[i].selected = false; |
- |
- if (this.mirroring_) { |
- this.updateSelectedDisplaySectionMirroring_(); |
- } else if (this.focusedId_ == '') { |
- this.updateSelectedDisplaySectionNoSelected_(); |
- } else { |
- this.updateSelectedDisplaySectionForDisplay_( |
- this.getDisplayInfoFromId(this.focusedId_)); |
- } |
- }, |
+ // Calculate the offset of the child display. |
+ this.calculateOffset_(isPrimary ? baseLayout : displayLayout); |
- /** |
- * Clears the drawing area for display rectangles. |
- * @private |
- */ |
- resetDisplaysView_: function() { |
- var displaysViewHost = $('display-options-displays-view-host'); |
- displaysViewHost.removeChild(displaysViewHost.firstChild); |
- this.displaysView_ = document.createElement('div'); |
- this.displaysView_.id = 'display-options-displays-view'; |
- displaysViewHost.appendChild(this.displaysView_); |
+ return displayLayout.originalPosition.x != div.offsetLeft || |
+ displayLayout.originalPosition.y != div.offsetTop; |
}, |
/** |
- * Lays out the display rectangles for mirroring. |
- * @private |
- */ |
- layoutMirroringDisplays_: function() { |
- // Offset pixels for secondary display rectangles. The offset includes the |
- // border width. |
- /** @const */ var MIRRORING_OFFSET_PIXELS = 3; |
- // Always show two displays because there must be two displays when |
- // the display_options is enabled. Don't rely on displays_.length because |
- // there is only one display from chrome's perspective in mirror mode. |
- /** @const */ var MIN_NUM_DISPLAYS = 2; |
- /** @const */ var MIRRORING_VERTICAL_MARGIN = 20; |
- |
- // The width/height should be same as the first display: |
- var width = Math.ceil(this.displays_[0].bounds.width * this.visualScale_); |
- var height = |
- Math.ceil(this.displays_[0].bounds.height * this.visualScale_); |
- |
- var numDisplays = Math.max(MIN_NUM_DISPLAYS, this.displays_.length); |
- |
- var totalWidth = width + numDisplays * MIRRORING_OFFSET_PIXELS; |
- var totalHeight = height + numDisplays * MIRRORING_OFFSET_PIXELS; |
- |
- this.displaysView_.style.height = totalHeight + 'px'; |
- |
- // The displays should be centered. |
- var offsetX = |
- $('display-options-displays-view').offsetWidth / 2 - totalWidth / 2; |
- |
- for (var i = 0; i < numDisplays; i++) { |
- var div = /** @type {HTMLElement} */ (document.createElement('div')); |
- div.className = 'displays-display'; |
- div.style.top = i * MIRRORING_OFFSET_PIXELS + 'px'; |
- div.style.left = i * MIRRORING_OFFSET_PIXELS + offsetX + 'px'; |
- div.style.width = width + 'px'; |
- div.style.height = height + 'px'; |
- div.style.zIndex = i; |
- // set 'display-mirrored' class for the background display rectangles. |
- if (i != numDisplays - 1) |
- div.classList.add('display-mirrored'); |
- this.displaysView_.appendChild(div); |
+ * Creates a div element and assigns it to |displayLayout|. Returns the |
+ * created div for additional decoration. |
+ * @param {string} id |
+ * @param {!Element} parentElement The parent element to contain the div. |
+ * @param {!options.DisplayPosition} offset The offset to the center of |
+ * the display area. |
+ */ |
+ createDisplayLayoutDiv_: function(id, parentElement, offset) { |
+ var displayLayout = this.displayLayoutMap_[id]; |
+ var parentId = displayLayout.parentId; |
+ if (parentId) { |
+ // Ensure the parent div is created first. |
+ var parentLayout = this.displayLayoutMap_[parentId]; |
+ if (!parentLayout.div) |
+ this.createDisplayLayoutDiv_(parentId, parentElement, offset); |
} |
- }, |
- |
- /** |
- * Creates a DisplayLayout object representing the display. |
- * @param {!options.DisplayInfo} display |
- * @param {!options.DisplayLayoutType} layoutType |
- * @return {!options.DisplayLayout} |
- * @private |
- */ |
- createDisplayLayout_: function(display, layoutType) { |
- return { |
- bounds: display.bounds, |
- div: null, |
- id: display.id, |
- isPrimary: display.isPrimary, |
- layoutType: layoutType, |
- name: display.name, |
- originalPosition: {x: 0, y: 0} |
- }; |
- }, |
- /** |
- * Creates a div element representing the specified display. |
- * @param {!options.DisplayLayout} displayLayout |
- * @param {options.DisplayLayoutType} layoutType The layout type for the |
- * secondary display. |
- * @param {!options.DisplayPosition} offset The offset to the center of the |
- * display area. |
- * @private |
- */ |
- createDisplayLayoutDiv_: function(displayLayout, layoutType, offset) { |
var div = /** @type {!HTMLElement} */ (document.createElement('div')); |
div.className = 'displays-display'; |
- div.classList.toggle( |
- 'displays-focused', displayLayout.id == this.focusedId_); |
// div needs to be added to the DOM tree first, otherwise offsetHeight for |
// nameContainer below cannot be computed. |
- this.displaysView_.appendChild(div); |
+ parentElement.appendChild(div); |
var nameContainer = document.createElement('div'); |
nameContainer.textContent = displayLayout.name; |
@@ -949,39 +362,35 @@ cr.define('options', function() { |
nameContainer.style.marginTop = |
(newHeight - nameContainer.offsetHeight) / 2 + 'px'; |
- div.onmousedown = this.onMouseDown_.bind(this); |
- div.ontouchstart = this.onTouchStart_.bind(this); |
- |
- if (displayLayout.isPrimary) { |
+ if (displayLayout.parentId == '') { |
div.style.left = |
Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; |
div.style.top = |
Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; |
} else { |
- // Don't trust the secondary display's x or y, because it may cause a |
+ // Don't trust the child display's x or y, because it may cause a |
// 1px gap due to rounding, which will create a fake update on end |
// dragging. See crbug.com/386401 |
- var primaryDiv = this.displayLayoutMap_[this.primaryDisplayId_].div; |
- switch (layoutType) { |
+ var parentDiv = this.displayLayoutMap_[displayLayout.parentId].div; |
+ switch (displayLayout.layoutType) { |
case options.DisplayLayoutType.TOP: |
div.style.left = |
Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; |
- div.style.top = primaryDiv.offsetTop - div.offsetHeight + 'px'; |
+ div.style.top = parentDiv.offsetTop - div.offsetHeight + 'px'; |
break; |
case options.DisplayLayoutType.RIGHT: |
div.style.left = |
- primaryDiv.offsetLeft + primaryDiv.offsetWidth + 'px'; |
+ parentDiv.offsetLeft + parentDiv.offsetWidth + 'px'; |
div.style.top = |
Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; |
break; |
case options.DisplayLayoutType.BOTTOM: |
div.style.left = |
Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; |
- div.style.top = |
- primaryDiv.offsetTop + primaryDiv.offsetHeight + 'px'; |
+ div.style.top = parentDiv.offsetTop + parentDiv.offsetHeight + 'px'; |
break; |
case options.DisplayLayoutType.LEFT: |
- div.style.left = primaryDiv.offsetLeft - div.offsetWidth + 'px'; |
+ div.style.left = parentDiv.offsetLeft - div.offsetWidth + 'px'; |
div.style.top = |
Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; |
break; |
@@ -991,147 +400,51 @@ cr.define('options', function() { |
displayLayout.div = div; |
displayLayout.originalPosition.x = div.offsetLeft; |
displayLayout.originalPosition.y = div.offsetTop; |
+ |
+ this.calculateOffset_(displayLayout); |
}, |
/** |
- * Layouts the display rectangles according to the current layout_. |
- * @param {options.DisplayLayoutType} layoutType |
- * @private |
+ * Calculates the offset for display |id| relative to its parent. |
+ * @param {options.DisplayLayout} displayLayout |
*/ |
- layoutDisplays_: function(layoutType) { |
- var maxWidth = 0; |
- var maxHeight = 0; |
- var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; |
- this.primaryDisplayId_ = ''; |
- this.secondaryDisplayId_ = ''; |
- for (var i = 0; i < this.displays_.length; i++) { |
- var display = this.displays_[i]; |
- if (display.isPrimary) |
- this.primaryDisplayId_ = display.id; |
- else if (this.secondaryDisplayId_ == '') |
- this.secondaryDisplayId_ = display.id; |
- |
- var bounds = display.bounds; |
- boundingBox.left = Math.min(boundingBox.left, bounds.left); |
- boundingBox.right = |
- Math.max(boundingBox.right, bounds.left + bounds.width); |
- boundingBox.top = Math.min(boundingBox.top, bounds.top); |
- boundingBox.bottom = |
- Math.max(boundingBox.bottom, bounds.top + bounds.height); |
- maxWidth = Math.max(maxWidth, bounds.width); |
- maxHeight = Math.max(maxHeight, bounds.height); |
- } |
- if (this.primaryDisplayId_ == '') |
+ calculateOffset_: function(displayLayout) { |
+ // Offset is calculated from top or left edge. |
+ var parent = this.displayLayoutMap_[displayLayout.parentId]; |
+ if (!parent) { |
+ displayLayout.offset = 0; |
return; |
- |
- // Make the margin around the bounding box. |
- var areaWidth = boundingBox.right - boundingBox.left + maxWidth; |
- var areaHeight = boundingBox.bottom - boundingBox.top + maxHeight; |
- |
- // Calculates the scale by the width since horizontal size is more strict. |
- // TODO(mukai): Adds the check of vertical size in case. |
- this.visualScale_ = Math.min( |
- VISUAL_SCALE, this.displaysView_.offsetWidth / areaWidth); |
- |
- // Prepare enough area for thisplays_view by adding the maximum height. |
- this.displaysView_.style.height = |
- Math.ceil(areaHeight * this.visualScale_) + 'px'; |
- |
- // Centering the bounding box of the display rectangles. |
- var offset = { |
- x: Math.floor( |
- this.displaysView_.offsetWidth / 2 - |
- (boundingBox.right + boundingBox.left) * this.visualScale_ / 2), |
- y: Math.floor( |
- this.displaysView_.offsetHeight / 2 - |
- (boundingBox.bottom + boundingBox.top) * this.visualScale_ / 2) |
- }; |
- |
- // Layout the display rectangles. First layout the primary display and |
- // then layout any secondary displays. |
- this.createDisplayLayoutDiv_( |
- this.displayLayoutMap_[this.primaryDisplayId_], layoutType, offset); |
- for (var i = 0; i < this.displays_.length; i++) { |
- if (!this.displays_[i].isPrimary) { |
- this.createDisplayLayoutDiv_( |
- this.displayLayoutMap_[this.displays_[i].id], layoutType, offset); |
- } |
} |
+ var offset; |
+ if (displayLayout.layoutType == options.DisplayLayoutType.LEFT || |
+ displayLayout.layoutType == options.DisplayLayoutType.RIGHT) { |
+ offset = displayLayout.div.offsetTop - parent.div.offsetTop; |
+ } else { |
+ offset = displayLayout.div.offsetLeft - parent.div.offsetLeft; |
+ } |
+ displayLayout.offset = Math.floor(offset / this.visualScale_); |
}, |
/** |
- * Called when the display arrangement has changed. |
- * @param {options.MultiDisplayMode} mode multi display mode. |
- * @param {!Array<!options.DisplayInfo>} displays The list of the display |
- * information. |
- * @param {options.DisplayLayoutType} layoutType The layout strategy. |
- * @param {number} offset The offset of the secondary display. |
+ * Returns the display paired with |displayLayout|, either the parent |
+ * or a matching child (currently there will only be one). |
+ * @param {options.DisplayLayout} displayLayout |
+ * @return {?options.DisplayLayout} |
* @private |
*/ |
- onDisplayChanged_: function(mode, displays, layoutType, offset) { |
- if (!this.visible) |
- return; |
- |
- this.displays_ = displays; |
- this.displayLayoutMap_ = {}; |
- for (var i = 0; i < displays.length; i++) { |
- var display = displays[i]; |
- this.displayLayoutMap_[display.id] = |
- this.createDisplayLayout_(display, layoutType); |
+ getBaseLayout_(displayLayout) { |
+ if (displayLayout.parentId != '') |
+ return this.displayLayoutMap_[displayLayout.parentId]; |
+ for (var childId in this.displayLayoutMap_) { |
+ var child = this.displayLayoutMap_[childId]; |
+ if (child.parentId == displayLayout.id) |
+ return child; |
} |
- |
- var mirroring = mode == options.MultiDisplayMode.MIRRORING; |
- var unifiedDesktopEnabled = mode == options.MultiDisplayMode.UNIFIED; |
- |
- // Focus to the first display next to the primary one when |displays_| |
- // is updated. |
- if (mirroring) { |
- this.focusedId_ = ''; |
- } else if ( |
- this.focusedId_ == '' || this.mirroring_ != mirroring || |
- this.unifiedDesktopEnabled_ != unifiedDesktopEnabled || |
- this.displays_.length != displays.length) { |
- this.focusedId_ = displays.length > 0 ? displays[0].id : ''; |
- } |
- |
- this.mirroring_ = mirroring; |
- this.unifiedDesktopEnabled_ = unifiedDesktopEnabled; |
- |
- this.resetDisplaysView_(); |
- if (this.mirroring_) |
- this.layoutMirroringDisplays_(); |
- else |
- this.layoutDisplays_(layoutType); |
- |
- $('display-options-select-mirroring').value = |
- mirroring ? 'mirroring' : 'extended'; |
- |
- $('display-options-unified-desktop').hidden = |
- !this.showUnifiedDesktopOption_; |
- |
- $('display-options-toggle-unified-desktop').checked = |
- this.unifiedDesktopEnabled_; |
- |
- var disableUnifiedDesktopOption = |
- (this.mirroring_ || |
- (!this.unifiedDesktopEnabled_ && |
- this.displays_.length == 1)); |
- |
- $('display-options-toggle-unified-desktop').disabled = |
- disableUnifiedDesktopOption; |
- |
- this.updateSelectedDisplayDescription_(); |
+ assertNotReached(); |
+ return null; |
} |
}; |
- DisplayOptions.setDisplayInfo = function( |
- mode, displays, layoutType, offset) { |
- DisplayOptions.getInstance().onDisplayChanged_( |
- mode, displays, layoutType, offset); |
- }; |
- |
// Export |
- return { |
- DisplayOptions: DisplayOptions |
- }; |
+ return {DisplayLayoutManager: DisplayLayoutManager}; |
}); |