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_(); |
} |
}; |