| Index: chrome/browser/resources/settings/device_page/display_layout.js
|
| diff --git a/chrome/browser/resources/settings/device_page/display_layout.js b/chrome/browser/resources/settings/device_page/display_layout.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5004a5db05345dd6436165912e10d3e70ea133d0
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/settings/device_page/display_layout.js
|
| @@ -0,0 +1,244 @@
|
| +// 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.
|
| +
|
| +/**
|
| + * @fileoverview
|
| + * 'display-layout' presents a visual representation of the layout of one or
|
| + * more displays and allows them to be arranged.
|
| + */
|
| +
|
| +(function() {
|
| +
|
| +/** @const {number} */ var MIN_VISUAL_SCALE = .01;
|
| +
|
| +Polymer({
|
| + is: 'display-layout',
|
| +
|
| + behaviors: [
|
| + Polymer.IronResizableBehavior,
|
| + ],
|
| +
|
| + properties: {
|
| + /**
|
| + * Array of displays.
|
| + * @type {!Array<!chrome.system.display.DisplayUnitInfo>}
|
| + */
|
| + displays: Array,
|
| +
|
| + /**
|
| + * Array of display layouts.
|
| + * @type {!Array<!chrome.system.display.DisplayLayout>}
|
| + */
|
| + layouts: Array,
|
| +
|
| + /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */
|
| + selectedDisplay: Object,
|
| +
|
| + /**
|
| + * The ratio of the display area div (in px) to DisplayUnitInfo.bounds.
|
| + * @type {number}
|
| + */
|
| + visualScale: 1,
|
| + },
|
| +
|
| + /** @private {!Object<chrome.system.display.DisplayUnitInfo>} */
|
| + displayMap_: {},
|
| +
|
| + /** @private {!Object<chrome.system.display.DisplayLayout>} */
|
| + layoutMap_: {},
|
| +
|
| + /** @private {!Object<chrome.system.display.Bounds>} */
|
| + boundsMap_: {},
|
| +
|
| + /** @private {!{left: number, top: number}} */
|
| + visualOffset_: {left: 0, top: 0},
|
| +
|
| + /** @override */
|
| + attached: function() {
|
| + // TODO(stevenjb): Remove retry once fixed:
|
| + // https://github.com/Polymer/polymer/issues/3629
|
| + var self = this;
|
| + var retry = 100; // ms
|
| + function tryCalcVisualScale() {
|
| + if (!self.calculateVisualScale_())
|
| + setTimeout(tryCalcVisualScale, retry);
|
| + }
|
| + tryCalcVisualScale();
|
| + },
|
| +
|
| + /**
|
| + * Called explicitly when |this.displays| and their associated |this.layouts|
|
| + * have been fetched from chrome.
|
| + * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays
|
| + * @param {!Array<!chrome.system.display.DisplayLayout>} layouts
|
| + */
|
| + updateDisplays: function(displays, layouts) {
|
| + this.displays = displays;
|
| + this.layouts = layouts;
|
| +
|
| + this.displayMap_ = {};
|
| + for (let display of this.displays)
|
| + this.displayMap_[display.id] = display;
|
| +
|
| + this.layoutMap_ = {};
|
| + for (let layout of this.layouts)
|
| + this.layoutMap_[layout.id] = layout;
|
| +
|
| + this.boundsMap_ = {};
|
| + for (let display of this.displays)
|
| + this.calcDisplayBounds_(display);
|
| +
|
| + this.calculateVisualScale_();
|
| + },
|
| +
|
| + /**
|
| + * Calculates the visual offset and scale for the display area
|
| + * (i.e. the ratio of the display area div size to the area required to
|
| + * contain the DisplayUnitInfo bounding boxes).
|
| + * @return {boolean} Whether the calculation was successful.
|
| + * @private
|
| + */
|
| + calculateVisualScale_() {
|
| + var displayAreaDiv = this.$.displayArea;
|
| + if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays ||
|
| + !this.displays.length) {
|
| + return false;
|
| + }
|
| +
|
| + var display = this.displays[0];
|
| + var bounds = this.boundsMap_[display.id];
|
| + var displayInfoBoundingBox = {
|
| + left: bounds.left,
|
| + right: bounds.left + bounds.width,
|
| + top: bounds.top,
|
| + bottom: bounds.top + bounds.height,
|
| + };
|
| + var maxWidth = bounds.width;
|
| + var maxHeight = bounds.height;
|
| + for (let i = 1; i < this.displays.length; ++i) {
|
| + display = this.displays[i];
|
| + bounds = this.boundsMap_[display.id];
|
| + displayInfoBoundingBox.left =
|
| + Math.min(displayInfoBoundingBox.left, bounds.left);
|
| + displayInfoBoundingBox.right =
|
| + Math.max(displayInfoBoundingBox.right, bounds.left + bounds.width);
|
| + displayInfoBoundingBox.top =
|
| + Math.min(displayInfoBoundingBox.top, bounds.top);
|
| + displayInfoBoundingBox.bottom =
|
| + Math.max(displayInfoBoundingBox.bottom, bounds.top + bounds.height);
|
| + maxWidth = Math.max(maxWidth, bounds.width);
|
| + maxHeight = Math.max(maxHeight, bounds.height);
|
| + }
|
| +
|
| + // Create a margin around the bounding box equal to the size of the
|
| + // largest displays.
|
| + var displayInfoBoundsWidth = displayInfoBoundingBox.right -
|
| + displayInfoBoundingBox.left + maxWidth * 2;
|
| + var displayInfoBoundsHeight = displayInfoBoundingBox.bottom -
|
| + displayInfoBoundingBox.top + maxHeight * 2;
|
| +
|
| + // Calculate the scale.
|
| + var horizontalScale = displayAreaDiv.offsetWidth / displayInfoBoundsWidth;
|
| + var verticalScale = displayAreaDiv.offsetHeight / displayInfoBoundsHeight;
|
| + var scale = Math.min(horizontalScale, verticalScale);
|
| +
|
| + // Calculate the offset.
|
| + this.visualOffset_.left = (-displayInfoBoundingBox.left + maxWidth) * scale;
|
| + this.visualOffset_.top = (-displayInfoBoundingBox.top + maxHeight) * scale;
|
| +
|
| + // Update the scale which will trigger calls to getDivStyle_.
|
| + this.visualScale = Math.max(MIN_VISUAL_SCALE, scale);
|
| +
|
| + return true;
|
| + },
|
| +
|
| + /**
|
| + * @param {!chrome.system.display.DisplayUnitInfo} display
|
| + * @param {number} visualScale
|
| + * @return {string} The style string for the div.
|
| + * @private
|
| + */
|
| + getDivStyle_: function(display, visualScale) {
|
| + // This matches the size of the box-shadow or border in CSS.
|
| + /** @const {number} */ var BORDER = 2;
|
| + var bounds = this.boundsMap_[display.id];
|
| + var height = Math.round(bounds.height * this.visualScale) - BORDER * 2;
|
| + var width = Math.round(bounds.width * this.visualScale) - BORDER * 2;
|
| + var left =
|
| + Math.round(this.visualOffset_.left + (bounds.left * this.visualScale));
|
| + var top =
|
| + Math.round(this.visualOffset_.top + (bounds.top * this.visualScale));
|
| + return `height: ${height}px; width: ${width}px;` +
|
| + ` left: ${left}px; top: ${top}px`;
|
| + },
|
| +
|
| + /**
|
| + * @param {!chrome.system.display.DisplayUnitInfo} display
|
| + * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay
|
| + * @return {boolean}
|
| + * @private
|
| + */
|
| + isSelected_: function(display, selectedDisplay) {
|
| + return display.id == selectedDisplay.id;
|
| + },
|
| +
|
| + /**
|
| + * @param {!{model: !{index: number}}} e
|
| + * @private
|
| + */
|
| + onSelectDisplayTap_: function(e) {
|
| + this.fire('select-display', e.model.index);
|
| + },
|
| +
|
| + /**
|
| + * Recursively calculate the bounds of a display relative to its parents.
|
| + * Caches the display bounds so that parent bounds are only calculated once.
|
| + * TODO(stevenjb): Move this function and the maps it requires to a separate
|
| + * behavior which will include snapping and collisions.
|
| + * @param {!chrome.system.display.DisplayUnitInfo} display
|
| + * @private
|
| + */
|
| + calcDisplayBounds_: function(display) {
|
| + if (display.id in this.boundsMap_)
|
| + return; // Already calculated (i.e. a parent of a previous display)
|
| + var left, top;
|
| + if (display.isPrimary) {
|
| + left = -display.bounds.width / 2;
|
| + top = -display.bounds.height / 2;
|
| + } else {
|
| + var layout = this.layoutMap_[display.id];
|
| + assert(layout.parentId);
|
| + var parentDisplay = this.displayMap_[layout.parentId];
|
| + var parentBounds;
|
| + if (!(parentDisplay.id in this.boundsMap_))
|
| + this.calcDisplayBounds_(parentDisplay);
|
| + parentBounds = this.boundsMap_[parentDisplay.id];
|
| + left = parentBounds.left;
|
| + top = parentBounds.top;
|
| + switch (layout.position) {
|
| + case chrome.system.display.LayoutPosition.TOP:
|
| + top -= display.bounds.height;
|
| + break;
|
| + case chrome.system.display.LayoutPosition.RIGHT:
|
| + left += parentBounds.width;
|
| + break;
|
| + case chrome.system.display.LayoutPosition.BOTTOM:
|
| + top += parentBounds.height;
|
| + break;
|
| + case chrome.system.display.LayoutPosition.LEFT:
|
| + left -= display.bounds.height;
|
| + break;
|
| + }
|
| + }
|
| + var result = {
|
| + left: left,
|
| + top: top,
|
| + width: display.bounds.width,
|
| + height: display.bounds.height
|
| + };
|
| + this.boundsMap_[display.id] = result;
|
| + }
|
| +});
|
| +
|
| +})();
|
|
|