Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7294)

Unified Diff: chrome/browser/resources/options/chromeos/display_options.js

Issue 11195036: Overscan calibration UI. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/options/chromeos/display_options.js
diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_options.js
index 65859856a5c830bc859e58cb57365b49c1a8295a..f98da68d4f51757be10c24baa90562c3af1ada53 100644
--- a/chrome/browser/resources/options/chromeos/display_options.js
+++ b/chrome/browser/resources/options/chromeos/display_options.js
@@ -11,6 +11,25 @@ cr.define('options', function() {
// The number of pixels to share the edges between displays.
/** @const */ var MIN_OFFSET_OVERLAP = 5;
+ // The border width of the display rectangles for the focused one.
+ /** @const */ var FOCUSED_BORDER_WIDTH_PX = 2;
+ // The border width of the display rectangles for the normal one.
+ /** @const */ var NORMAL_BORDER_WIDTH_PX = 1;
+
+ // The constant values for the overscan calibration settings.
+ // The height of an arrow.
+ /** @const */ var ARROW_SIZE_PX = 10;
+
+ // The gap from the boundary of the display rectangle and the arrow.
+ /** @const */ var ARROW_GAP_PX = 2;
+
+ // The margin size to handle events outside the target display.
+ /** @const */ var ARROW_CONTAINER_MARGIN_PX = ARROW_SIZE_PX * 3;
+
+ // The interval times to update the overscan while the user keeps pressing
+ // the mouse button or touching.
+ /** @const */ var OVERSCAN_TIC_INTERVAL_MS = 100;
+
/**
* Enumeration of secondary display layout. The value has to be same as the
* values in ash/display/display_controller.cc.
@@ -24,6 +43,330 @@ cr.define('options', function() {
};
/**
+ * Enumeration of the direction for the calibrating overscan settings.
+ * @enum {number}
+ */
+ var CalibrationDirection = {
+ INNER: 1,
+ OUTER: -1
+ };
+
+ /**
+ * Calculates the bounds of |element| relative to the page.
+ * @param {HTMLElement} element The element to be known.
+ * @return {Object} The object for the bounds, with x, y, width, and height.
+ */
+ function getBoundsInPage(element) {
+ if (element == document.body)
+ return {x: 0, y: 0};
+
+ var point = getBoundsInPage(element.parentNode);
xiyuan 2012/10/22 19:54:44 Should we use element.offsetParent instead? Also
Jun Mukai 2012/10/22 22:35:30 Done.
+ point.x += element.offsetLeft;
+ point.y += element.offsetTop;
+ point.width = element.offsetWidth;
+ point.height = element.offsetHeight;
+ return point;
xiyuan 2012/10/22 19:54:44 nit: How about return { x: point.x + element
Jun Mukai 2012/10/22 22:35:30 Changed the code not to use recursion, so the code
+ }
+
+ /**
+ * Gets the position of |point| to |rect|, left, right, top, or bottom.
+ * @param {Object} rect The base rectangle with x, y, width, and height.
+ * @param {Object} point The point to check the position.
+ * @return {SecondaryDisplayLayout} The position of the calculated point.
+ */
+ function getPositionToRectangle(rect, point) {
+ // Separates the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of
+ // the rect, and decides which area the display should reside.
+ var diagonalSlope = rect.height / rect.width;
+ var topDownIntercept = rect.y - rect.x * diagonalSlope;
+ var bottomUpIntercept = rect.y + rect.height + rect.x * diagonalSlope;
+
+ if (point.y > topDownIntercept + point.x * diagonalSlope) {
+ if (point.y > bottomUpIntercept - point.x * diagonalSlope)
+ return SecondaryDisplayLayout.BOTTOM;
+ else
+ return SecondaryDisplayLayout.LEFT;
+ } else {
+ if (point.y > bottomUpIntercept - point.x * diagonalSlope)
+ return SecondaryDisplayLayout.RIGHT;
+ else
+ return SecondaryDisplayLayout.TOP;
+ }
+ }
+
+ /**
+ * DisplayOverscanCalibrator shows the arrows to calibrate overscan settings
+ * and handles events of the actual user control.
+ * @param {Object} display The display object for the calibrating overscan.
+ * @constructor
+ */
+ function DisplayOverscanCalibrator(display) {
+ // Creates the calibration arrows over |display|. To achieve the UI,
+ // a transparent container holds the arrows and handles events.
+ this.container_ = document.createElement('div');
+ this.container_.id = 'display-overscan-calibration-arrow-container';
+ var containerSize = {
+ width: display.div.offsetWidth + ARROW_CONTAINER_MARGIN_PX * 2,
+ height: display.div.offsetHeight + ARROW_CONTAINER_MARGIN_PX * 2
+ };
+ this.container_.style.width = containerSize.width + 'px';
+ this.container_.style.height = containerSize.height + 'px';
+ this.container_.style.left =
+ display.div.offsetLeft - ARROW_CONTAINER_MARGIN_PX + 'px';
+ this.container_.style.top =
+ display.div.offsetTop - ARROW_CONTAINER_MARGIN_PX + 'px';
+ this.container_.onmousedown = this.onMouseDown_.bind(this);
+ this.container_.onmouseup = this.onOperationEnd_.bind(this);
+ this.container_.ontouchstart = this.onTouchStart_.bind(this);
+ this.container_.ontouchend = this.onOperationEnd_.bind(this);
+
+ // Creates arrows for each direction.
+ var topArrow = this.createVerticalArrows_();
+ topArrow.style.left = containerSize.width / 2 - ARROW_SIZE_PX + 'px';
+ topArrow.style.top = ARROW_CONTAINER_MARGIN_PX -
+ ARROW_SIZE_PX - ARROW_GAP_PX + 'px';
+ this.container_.appendChild(topArrow);
+ var bottomArrow = this.createVerticalArrows_();
+ bottomArrow.style.left = containerSize.width / 2 - ARROW_SIZE_PX + 'px';
+ bottomArrow.style.bottom = ARROW_CONTAINER_MARGIN_PX -
+ ARROW_SIZE_PX - ARROW_GAP_PX + 'px';
+ this.container_.appendChild(bottomArrow);
+ var leftArrow = this.createHorizontalArrows_();
+ leftArrow.style.left = ARROW_CONTAINER_MARGIN_PX -
+ ARROW_SIZE_PX - ARROW_GAP_PX + 'px';
+ leftArrow.style.top = containerSize.height / 2 - ARROW_SIZE_PX + 'px';
+ this.container_.appendChild(leftArrow);
+ var rightArrow = this.createHorizontalArrows_();
+ rightArrow.style.right = ARROW_CONTAINER_MARGIN_PX -
+ ARROW_SIZE_PX - ARROW_GAP_PX + 'px';
+ rightArrow.style.top = containerSize.height / 2 - ARROW_SIZE_PX + 'px';
+ this.container_.appendChild(rightArrow);
+
+ display.div.parentNode.appendChild(this.container_);
+ this.displayBounds_ = getBoundsInPage(display.div);
+ this.overscan_ = display.overscan;
+ chrome.send('startOverscanCalibration', [display.id]);
+ };
+
+ DisplayOverscanCalibrator.prototype = {
+ /**
+ * The container of arrows. It also receives the user events.
+ * @private
+ */
+ container_: null,
+
+ /**
+ * The bounds of the display rectangle in the page.
+ * @private
+ */
+ displayBounds_: null,
+
+ /**
+ * The current overscan settins.
+ * @private
+ */
+ overscan_: null,
+
+ /**
+ * The location of the current user operation against the display. The
+ * contents should be one of 'left', 'right', 'top', or 'bottom'.
+ * @type {string}
+ * @private
+ */
+ location_: null,
+
+ /**
+ * The direction of the current user operation against the display.
+ * @type {CalibrationDirection}
+ * @private
+ */
+ direction_: null,
+
+ /**
+ * The ID for the periodic timer to tic the calibration settings while the
+ * user keeps pressing the mouse button.
+ * @type {number}
+ * @private
+ */
+ timer_: null,
+
+ /**
+ * Called when everything is finished.
+ */
+ Finish: function() {
xiyuan 2012/10/22 19:54:44 nit: js method should start with lower case. Fin
Jun Mukai 2012/10/22 22:35:30 Done.
+ this.container_.parentNode.removeChild(this.container_);
+ chrome.send('finishOverscanCalibration');
+ },
+
+ /**
+ * Called when every settings are cleared.
+ */
+ Clear: function() {
xiyuan 2012/10/22 19:54:44 nit: Clear -> clear
Jun Mukai 2012/10/22 22:35:30 Done.
+ chrome.send('clearOverscanCalibration');
+ },
+
+ /**
+ * Mouse down event handler for overscan calibration.
+ * @param {Event} e The mouse down event.
+ * @private
+ */
+ onMouseDown_: function(e) {
+ e.preventDefault();
+ this.setupOverscanCalibration_(e);
+ },
+
+ /**
+ * Touch start event handler for overscan calibration.
+ * @param {Event} e The touch start event.
+ * @private
+ */
+ onTouchStart_: function(e) {
+ if (e.touches.length != 1)
+ return;
+
+ e.preventDefault();
+ var touch = e.touches[0];
+ this.setupOverscanCalibration_(e.touches[0]);
+ },
+
+ /**
+ * Event handler for ending the user operation of overscan calibration.
+ * @param {Event} e The event object, mouse up event or touch end event.
+ * @private
+ */
+ onOperationEnd_: function(e) {
+ if (this.timer_) {
+ window.clearInterval(this.timer_);
+ this.timer_ = null;
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Sets up a new overscan calibration operation. It calculates the event
+ * location and determines the contents of location. It also sets up a
+ * timer to change the overscan settings continuously as far as the user
+ * keeps pressing the mouse button or touching. The timer will be cleared
+ * on ending the operation.
+ * @param {Object} e The object to contain the location of the event with
+ * pageX and pageY attributes.
+ * @private
+ */
+ setupOverscanCalibration_: function(e) {
+ switch (getPositionToRectangle(
+ this.displayBounds_, {x: e.pageX, y: e.pageY})) {
+ case SecondaryDisplayLayout.RIGHT:
+ this.location_ = 'right';
+ if (e.pageX < this.displayBounds_.x + this.displayBounds_.width)
+ this.direction_ = CalibrationDirection.INNER;
+ else
+ this.direction_ = CalibrationDirection.OUTER;
+ break;
+ case SecondaryDisplayLayout.LEFT:
+ this.location_ = 'left';
+ if (e.pageX > this.displayBounds_.x)
+ this.direction_ = CalibrationDirection.INNER;
+ else
+ this.direction_ = CalibrationDirection.OUTER;
+ break;
+ case SecondaryDisplayLayout.TOP:
+ this.location_ = 'top';
+ if (e.pageY > this.displayBounds_.y)
+ this.direction_ = CalibrationDirection.INNER;
+ else
+ this.direction_ = CalibrationDirection.OUTER;
+ break;
+ case SecondaryDisplayLayout.BOTTOM:
+ this.location_ = 'bottom';
+ if (e.pageY < this.displayBounds_.y + this.displayBounds_.height) {
+ this.direction_ = CalibrationDirection.INNER;
+ } else {
+ this.direction_ = CalibrationDirection.OUTER;
+ }
+ break;
+ }
+
+ this.ticOverscanSize_();
+ this.timer_ = window.setInterval(
+ this.ticOverscanSize_.bind(this), OVERSCAN_TIC_INTERVAL_MS);
+ },
+
+ /**
+ * Modifies the current overscans actually and sends the update to the
+ * system.
+ * @private
+ */
+ ticOverscanSize_: function() {
+ // Overscan inset values have to be positive.
+ if (this.direction_ == CalibrationDirection.OUTER &&
+ this.overscan_[this.location_] == 0) {
xiyuan 2012/10/22 19:54:44 This does not match what the comment above says. S
Jun Mukai 2012/10/22 22:35:30 The code is correct, my intention is that this.ove
+ return;
+ }
+
+ this.overscan_[this.location_] += this.direction_;
+ chrome.send('updateOverscanCalibration',
+ [this.overscan_.top, this.overscan_.left,
+ this.overscan_.bottom, this.overscan_.right]);
+ },
+
+ /**
+ * Creates the arrows vertically aligned, for the calibration UI at the top
+ * and bottom of the target display.
+ * @return {HTMLElement} The created div which contains the arrows.
+ * @private
+ */
+ createVerticalArrows_: function() {
+ var container = document.createElement('div');
+ container.style.width = ARROW_SIZE_PX * 2 + 'px';
+ container.style.height =
+ ARROW_SIZE_PX * 2 + ARROW_GAP_PX * 2 + FOCUSED_BORDER_WIDTH_PX + 'px';
+ container.style.position = 'absolute';
+
+ var arrowUp = document.createElement('div');
+ arrowUp.className = 'display-overscan-calibration-arrow ' +
+ 'display-overscan-arrow-to-top';
+ arrowUp.style.left = '0';
+ arrowUp.style.top = -ARROW_SIZE_PX + 'px';
+ container.appendChild(arrowUp);
+ var arrowDown = document.createElement('div');
+ arrowDown.className = 'display-overscan-calibration-arrow ' +
+ 'display-overscan-arrow-to-bottom';
+ arrowDown.style.left = '0';
+ arrowDown.style.bottom = -ARROW_SIZE_PX + 'px';
+ container.appendChild(arrowDown);
+ return container;
+ },
+
+ /**
+ * Creates the arrows horizontally aligned, for the calibration UI at the
+ * left and right of the target display.
+ * @return {HTMLElement} The created div which contains the arrows.
+ * @private
+ */
+ createHorizontalArrows_: function() {
+ var container = document.createElement('div');
+ container.style.width =
+ ARROW_SIZE_PX * 2 + ARROW_GAP_PX * 2 + FOCUSED_BORDER_WIDTH_PX + 'px';
+ container.style.height = ARROW_SIZE_PX * 2 + 'px';
+ container.style.position = 'absolute';
+
+ var arrowLeft = document.createElement('div');
+ arrowLeft.className = 'display-overscan-calibration-arrow ' +
+ 'display-overscan-arrow-to-left';
+ arrowLeft.style.left = -ARROW_SIZE_PX + 'px';
+ arrowLeft.style.top = '0';
+ container.appendChild(arrowLeft);
+ var arrowRight = document.createElement('div');
+ arrowRight.className = 'display-overscan-calibration-arrow ' +
+ 'display-overscan-arrow-to-right';
+ arrowRight.style.right = -ARROW_SIZE_PX + 'px';
+ arrowRight.style.top = '0';
+ container.appendChild(arrowRight);
+ return container;
+ }
+ };
+
+ /**
* Encapsulated handling of the 'Display' page.
* @constructor
*/
@@ -125,6 +468,22 @@ cr.define('options', function() {
chrome.send('setPrimary', [this.displays_[this.focusedIndex_].id]);
}).bind(this);
+ $('selected-display-start-calibrating-overscan').onclick = (function() {
+ this.overscanCalibrator_ = new DisplayOverscanCalibrator(
+ this.displays_[this.focusedIndex_]);
+ this.updateSelectedDisplayDescription_();
+ }).bind(this);
+ $('selected-display-finish-calibrating-overscan').onclick = (function() {
+ this.overscanCalibrator_.Finish();
+ this.overscanCalibrator_ = null;
+ this.updateSelectedDisplayDescription_();
+ }).bind(this);
+ $('selected-display-clear-calibrating-overscan').onclick = (function() {
+ this.overscanCalibrator_.Clear();
+ this.overscanCalibrator_ = null;
+ this.updateSelectedDisplayDescription_();
+ }).bind(this);
+
chrome.send('getDisplayInfo');
},
@@ -137,8 +496,8 @@ cr.define('options', function() {
/**
* Mouse move handler for dragging display rectangle.
- * @private
* @param {Event} e The mouse move event.
+ * @private
*/
onMouseMove_: function(e) {
return this.processDragging_(e, {x: e.pageX, y: e.pageY});
@@ -146,8 +505,8 @@ cr.define('options', function() {
/**
* Touch move handler for dragging display rectangle.
- * @private
* @param {Event} e The touch move event.
+ * @private
*/
onTouchMove_: function(e) {
if (e.touches.length != 1)
@@ -172,8 +531,8 @@ cr.define('options', function() {
/**
* Mouse down handler for dragging display rectangle.
- * @private
* @param {Event} e The mouse down event.
+ * @private
*/
onMouseDown_: function(e) {
if (this.mirroring_)
@@ -188,8 +547,8 @@ cr.define('options', function() {
/**
* Touch start handler for dragging display rectangle.
- * @private
* @param {Event} e The touch start event.
+ * @private
*/
onTouchStart_: function(e) {
if (this.mirroring_)
@@ -253,9 +612,9 @@ cr.define('options', function() {
/**
* Processes the actual dragging of display rectangle.
- * @private
* @param {Event} e The event which triggers this drag.
* @param {Object} eventLocation The location where the event happens.
+ * @private
*/
processDragging_: function(e, eventLocation) {
if (!this.dragging_)
@@ -297,31 +656,29 @@ cr.define('options', function() {
y: newPosition.y + draggingDiv.offsetHeight / 2
};
- // Separate the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of
- // the base display, and decide which area the display should reside.
- var diagonalSlope = baseDiv.offsetHeight / baseDiv.offsetWidth;
- var topDownIntercept =
- baseDiv.offsetTop - baseDiv.offsetLeft * diagonalSlope;
- var bottomUpIntercept = baseDiv.offsetTop +
- baseDiv.offsetHeight + baseDiv.offsetLeft * diagonalSlope;
-
- if (newCenter.y >
- topDownIntercept + newCenter.x * diagonalSlope) {
- if (newCenter.y >
- bottomUpIntercept - newCenter.x * diagonalSlope)
- this.layout_ = this.dragging_.display.isPrimary ?
- SecondaryDisplayLayout.TOP : SecondaryDisplayLayout.BOTTOM;
- else
- this.layout_ = this.dragging_.display.isPrimary ?
- SecondaryDisplayLayout.RIGHT : SecondaryDisplayLayout.LEFT;
- } else {
- if (newCenter.y >
- bottomUpIntercept - newCenter.x * diagonalSlope)
- this.layout_ = this.dragging_.display.isPrimary ?
- SecondaryDisplayLayout.LEFT : SecondaryDisplayLayout.RIGHT;
- else
- this.layout_ = this.dragging_.display.isPrimary ?
- SecondaryDisplayLayout.BOTTOM : SecondaryDisplayLayout.TOP;
+ var baseBounds = {
+ x: baseDiv.offsetLeft,
+ y: baseDiv.offsetTop,
+ width: baseDiv.offsetWidth,
+ height: baseDiv.offsetHeight
+ };
+ switch (getPositionToRectangle(baseBounds, newCenter)) {
+ case SecondaryDisplayLayout.RIGHT:
+ this.layout_ = this.dragging_.display.isPrimary ?
+ SecondaryDisplayLayout.LEFT : SecondaryDisplayLayout.RIGHT;
+ break;
+ case SecondaryDisplayLayout.LEFT:
+ this.layout_ = this.dragging_.display.isPrimary ?
+ SecondaryDisplayLayout.RIGHT : SecondaryDisplayLayout.LEFT;
+ break;
+ case SecondaryDisplayLayout.TOP:
+ this.layout_ = this.dragging_.display.isPrimary ?
+ SecondaryDisplayLayout.BOTTOM : SecondaryDisplayLayout.TOP;
+ break;
+ case SecondaryDisplayLayout.BOTTOM:
+ this.layout_ = this.dragging_.display.isPrimary ?
+ SecondaryDisplayLayout.TOP : SecondaryDisplayLayout.BOTTOM;
+ break;
}
if (this.layout_ == SecondaryDisplayLayout.LEFT ||
@@ -392,10 +749,10 @@ cr.define('options', function() {
/**
* start dragging of a display rectangle.
- * @private
* @param {HTMLElement} target The event target.
* @param {Object} eventLocation The object to hold the location where
* this event happens.
+ * @private
*/
startDragging_: function(target, eventLocation) {
this.focusedIndex_ = null;
@@ -424,14 +781,19 @@ cr.define('options', function() {
eventLocation: eventLocation
};
}
+
+ if (this.overscanCalibrator_) {
+ this.overscanCalibrator_.Finish();
+ this.overscanCalibrator_ = null;
+ }
this.updateSelectedDisplayDescription_();
return false;
},
/**
* finish the current dragging of displays.
- * @private
* @param {Event} e The event which triggers this.
+ * @private
*/
endDragging_: function(e) {
this.lastTouchLocation_ = null;
@@ -492,6 +854,14 @@ cr.define('options', function() {
resolutionElement.removeChild(resolutionElement.firstChild);
resolutionElement.appendChild(document.createTextNode(resolutionData));
+ if (this.overscanCalibrator_) {
+ $('start-calibrating-overscan-control').hidden = true;
+ $('end-calibrating-overscan-control').hidden = false;
+ } else {
+ $('start-calibrating-overscan-control').hidden = false;
+ $('end-calibrating-overscan-control').hidden = true;
+ }
+
var arrow = $('display-configuration-arrow');
arrow.hidden = false;
arrow.style.top =
@@ -523,8 +893,6 @@ cr.define('options', function() {
* @private
*/
resizeDisplayRectangle_: function(display, index) {
- /** @const */ var FOCUSED_BORDER_WIDTH_PX = 2;
- /** @const */ var NORMAL_BORDER_WIDTH_PX = 1;
var borderWidth = (index == this.focusedIndex_) ?
FOCUSED_BORDER_WIDTH_PX : NORMAL_BORDER_WIDTH_PX;
display.div.style.width =
@@ -661,11 +1029,11 @@ cr.define('options', function() {
/**
* Called when the display arrangement has changed.
- * @private
* @param {boolean} mirroring Whether current mode is mirroring or not.
* @param {Array} displays The list of the display information.
* @param {SecondaryDisplayLayout} layout The layout strategy.
* @param {number} offset The offset of the secondary display.
+ * @private
*/
onDisplayChanged_: function(mirroring, displays, layout, offset) {
this.mirroring_ = mirroring;
@@ -691,6 +1059,12 @@ cr.define('options', function() {
this.layoutMirroringDisplays_();
else
this.layoutDisplays_();
+
+ if (this.overscanCalibrator_) {
+ this.overscanCalibrator_.Finish();
+ this.overscanCalibrator_ = new DisplayOverscanCalibrator(
+ this.displays_[this.focusedIndex_]);
+ }
this.updateSelectedDisplayDescription_();
}
};

Powered by Google App Engine
This is Rietveld 408576698