Chromium Code Reviews| Index: chrome/browser/resources/options2/chromeos/display_options.js |
| diff --git a/chrome/browser/resources/options2/chromeos/display_options.js b/chrome/browser/resources/options2/chromeos/display_options.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..737ba1ac9639383bd5307712b3583b32bd9f56aa |
| --- /dev/null |
| +++ b/chrome/browser/resources/options2/chromeos/display_options.js |
| @@ -0,0 +1,363 @@ |
| +// Copyright (c) 2012 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. |
| + |
| +cr.define('options', function() { |
| + |
| + var OptionsPage = options.OptionsPage; |
| + |
| + // The scale ratio of the display rectangle to its original size. |
| + var kVisualScale = 1 / 10; |
|
yoshiki
2012/06/15 10:21:28
Use VISUAL_SCALE as name of constants and add @con
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + |
| + /** |
| + * Enumeration of secondary display layout. THe value has to be same as the |
| + * values in ash/monitor/monitor_controller.cc. |
| + * @enum {number} |
| + */ |
| + var SecondaryDisplayLayout = { |
| + TOP: 0, |
| + RIGHT: 1, |
| + BOTTOM: 2, |
| + LEFT: 3 |
| + }; |
| + |
| + /** |
| + * Encapsulated handling of the 'Display' page. |
| + * @constructor |
| + */ |
| + function DisplayOptions() { |
| + OptionsPage.call(this, |
| + 'display', |
| + loadTimeData.getString('displayOptionsPageTabTitle'), |
| + 'display-options'); |
| + this.mirroring_ = false; |
| + this.focused_index_ = null; |
| + this.displays_ = []; |
| + } |
| + |
| + cr.addSingletonGetter(DisplayOptions); |
| + |
| + DisplayOptions.prototype = { |
| + __proto__: OptionsPage.prototype, |
| + |
| + /** |
| + * Initialize the page. |
| + */ |
| + initializePage: function() { |
| + var self = this; |
| + |
| + OptionsPage.prototype.initializePage.call(this); |
| + |
| + $('display-options-confirm').onclick = function() { |
| + OptionsPage.closeOverlay(); |
| + }; |
| + |
| + $('display-options-toggle-mirroring').onclick = function() { |
| + self.mirroring_ = !self.mirroring_; |
| + chrome.send('setMirroring', [self.mirroring_]); |
| + }; |
| + |
| + chrome.send('getDisplayInfo'); |
| + }, |
| + |
| + /** |
| + * Mouse move handler for dragging display rectangle. |
| + * @private |
| + * @param {Event} e The mouse move event. |
| + */ |
| + onMouseMove_: function(e) { |
| + if (!this.dragging) { |
|
yoshiki
2012/06/15 10:21:28
Remove braces when the statement is only one line
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return true; |
| + } |
| + |
| + var index = -1; |
| + for (var i = 0; i < this.displays_.length; i++) { |
| + if (this.displays_[i] == this.dragging.display) { |
| + index = i; |
| + break; |
| + } |
| + } |
| + if (index < 0) { |
|
yoshiki
2012/06/15 10:21:28
Remove braces.
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return true; |
| + } |
| + |
| + // 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 mouse_position = {x: e.pageX - this.dragging.offset.x, |
| + y: e.pageY - this.dragging.offset.y}; |
|
yoshiki
2012/06/15 10:21:28
Add CRLF before the end brace because multi-line i
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + var new_position = { |
| + x: mouse_position.x - this.dragging.click_location.x, |
| + y: mouse_position.y - this.dragging.click_location.y}; |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + |
| + var primary_div = this.displays_[0].div; |
| + var display = this.dragging.display; |
| + |
| + // Separate the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of |
| + // the primary display, and decide which area the display should reside. |
| + var diagonal_slope = primary_div.offsetHeight / primary_div.offsetWidth; |
| + var top_down_intercept = |
| + primary_div.offsetTop - primary_div.offsetLeft * diagonal_slope; |
| + var bottom_up_intercept = primary_div.offsetTop + |
| + primary_div.offsetHeight + primary_div.offsetLeft * diagonal_slope; |
| + |
| + if (mouse_position.y > |
| + top_down_intercept + mouse_position.x * diagonal_slope) { |
| + if (mouse_position.y > |
| + bottom_up_intercept - mouse_position.x * diagonal_slope) { |
| + this.layout_ = SecondaryDisplayLayout.BOTTOM; |
| + } else { |
| + this.layout_ = SecondaryDisplayLayout.LEFT; |
| + } |
| + } else { |
| + if (mouse_position.y > |
| + bottom_up_intercept - mouse_position.x * diagonal_slope) { |
| + this.layout_ = SecondaryDisplayLayout.RIGHT; |
| + } else { |
| + this.layout_ = SecondaryDisplayLayout.TOP; |
| + } |
| + } |
| + |
| + if (this.layout_ == SecondaryDisplayLayout.LEFT || |
| + this.layout_ == SecondaryDisplayLayout.RIGHT) { |
| + if (new_position.y > primary_div.offsetTop + primary_div.offsetHeight) { |
| + this.layout_ = SecondaryDisplayLayout.BOTTOM; |
| + } else if (new_position.y + display.div.offsetHeight < |
| + primary_div.offsetTop) { |
| + this.layout_ = SecondaryDisplayLayout.TOP; |
| + } |
| + } else { |
| + if (new_position.y > primary_div.offsetLeft + primary_div.offsetWidth) { |
| + this.layout_ = SecondaryDisplayLayout.RIGHT; |
| + } else if (new_position.y + display.div.offsetWidth < |
| + primary_div.offstLeft) { |
| + this.layout_ = SecondaryDisplayLayout.LEFT; |
| + } |
| + } |
| + |
| + switch (this.layout_) { |
| + case SecondaryDisplayLayout.RIGHT: |
|
yoshiki
2012/06/15 10:21:28
Indent. "case" should be same indent as "switch".
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.left = |
| + primary_div.offsetLeft + primary_div.offsetWidth + 'px'; |
| + display.div.style.top = new_position.y + 'px'; |
| + break; |
| + case SecondaryDisplayLayout.LEFT: |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.left = |
| + primary_div.offsetLeft - display.div.offsetWidth + 'px'; |
| + display.div.style.top = new_position.y + 'px'; |
| + break; |
| + case SecondaryDisplayLayout.TOP: |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.top = |
| + primary_div.offsetTop - display.div.offsetHeight + 'px'; |
| + display.div.style.left = new_position.x + 'px'; |
| + break; |
| + case SecondaryDisplayLayout.BOTTOM: |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.top = |
| + primary_div.offsetTop + primary_div.offsetHeight + 'px'; |
| + display.div.style.left = new_position.x + 'px'; |
| + break; |
| + } |
| + |
| + return false; |
| + }, |
| + |
| + /** |
| + * Mouse down handler for dragging display rectangle. |
| + * @private |
| + * @param {Event} e The mouse down event. |
| + */ |
| + onMouseDown_: function(e) { |
| + if (this.mirroring_) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return true; |
| + } |
| + |
| + if (e.button != 0) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return true; |
| + } |
| + |
| + if (e.target == this.displays_view_) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return true; |
| + } |
| + |
| + for (var i = 0; i < this.displays_.length; i++) { |
| + var display = this.displays_[i]; |
| + if (display.div == e.target) { |
| + if (i == 0) { |
| + // Do not drag the primary monitor. |
| + return true; |
| + } |
| + |
| + this.focused_index_ = i; |
| + this.dragging = { |
|
yoshiki
2012/06/15 10:21:28
DisplayOptions.dragging is a private property righ
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display: display, |
| + click_location: {x: e.offsetX, y: e.offsetY}, |
| + offset: {x: e.pageX - e.offsetX - display.div.offsetLeft, |
| + y: e.pageY - e.offsetY - display.div.offsetTop} |
| + }; |
| + if (display.div.className.indexOf('displays-focused') == -1) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.className += ' displays-focused'; |
| + } |
| + } else if (display.div.className.indexOf('displays-focused') >= 0) { |
| + this.displays_[i].div.className = 'displays-display'; |
|
yoshiki
2012/06/15 10:21:28
'displays-primary' class would be removed, right?
Jun Mukai
2012/06/18 01:15:10
displays-primary won't obtain the '-focused' class
|
| + } |
| + } |
| + return false; |
| + }, |
| + |
| + /** |
| + * Mouse up handler for dragging display rectangle. |
| + * @private |
| + * @param {Event} e The mouse up event. |
| + */ |
| + onMouseUp_: function(e) { |
| + if (this.dragging) { |
| + this.dragging = null; |
| + chrome.send('setDisplayLayout', [this.layout_]); |
| + } |
| + return false; |
| + }, |
| + |
| + resetDisplaysView_: function() { |
| + var displays_view_host = $('display-options-displays-view-host'); |
| + displays_view_host.removeChild(displays_view_host.firstChild); |
| + this.displays_view_ = document.createElement('div'); |
| + this.displays_view_.id = 'display-options-displays-view'; |
| + this.displays_view_.onmousemove = this.onMouseMove_.bind(this); |
| + this.displays_view_.onmousedown = this.onMouseDown_.bind(this); |
| + this.displays_view_.onmouseup = this.onMouseUp_.bind(this); |
| + displays_view_host.appendChild(this.displays_view_); |
| + }, |
| + |
| + /** |
| + * Layouts the display rectangles for mirroring. |
| + * @private |
| + */ |
| + layoutMirroringDisplays_: function() { |
| + // The width/height should be same as the primary display: |
| + var width = this.displays_[0].width * kVisualScale; |
| + var height = this.displays_[0].height * kVisualScale; |
| + |
| + this.displays_view_.style.height = |
| + height + this.displays_.length * 2 + 'px'; |
| + |
| + for (var i = 0; i < this.displays_.length; i++) { |
| + var div = document.createElement('div'); |
| + this.displays_[i].div = div; |
| + div.className = 'displays-display'; |
| + div.style.top = i * 2 + 'px'; |
| + div.style.left = i * 2 + 'px'; |
| + div.style.width = width + 'px'; |
| + div.style.height = height + 'px'; |
| + div.style.zIndex = i; |
| + if (i == this.displays_.length - 1) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + div.className += ' displays-primary'; |
| + } |
| + this.displays_view_.appendChild(div); |
| + } |
| + }, |
| + |
| + /** |
| + * Layouts the display rectangles according to the current layout_. |
| + * @private |
| + */ |
| + layoutDisplays_: function() { |
| + var total_size = {width: 0, height: 0}; |
| + for (var i = 0; i < this.displays_.length; i++) { |
| + total_size.width += this.displays_[i].width * kVisualScale; |
| + total_size.height += this.displays_[i].height * kVisualScale; |
| + } |
| + |
| + this.displays_view_.style.width = total_size.width + 'px'; |
| + this.displays_view_.style.height = total_size.height + 'px'; |
| + |
| + var base_point = {x: 0, y: 0}; |
| + if (this.layout_ == SecondaryDisplayLayout.LEFT) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + base_point.x = total_size.width; |
| + } else if (this.layout_ == SecondaryDisplayLayout.TOP) { |
| + base_point.y = total_size.height; |
| + } |
| + |
| + for (var i = 0; i < this.displays_.length; i++) { |
| + var display = this.displays_[i]; |
| + var div = document.createElement('div'); |
| + display.div = div; |
| + |
| + div.className = 'displays-display'; |
| + if (i == 0) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + div.className += ' displays-primary'; |
| + } else if (i == this.focused_index_) { |
| + div.className += ' displays-focused'; |
| + } |
| + div.style.width = display.width * kVisualScale + 'px'; |
| + div.style.height = display.height * kVisualScale + 'px'; |
| + div.style.lineHeight = div.style.height; |
| + switch (this.layout_) { |
| + case SecondaryDisplayLayout.RIGHT: |
| + display.div.style.top = '0px'; |
|
yoshiki
2012/06/15 10:21:28
Use '0' instead of '0px'.
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.left = base_point.x + 'px'; |
| + base_point.x += display.width * kVisualScale; |
| + break; |
| + case SecondaryDisplayLayout.LEFT: |
| + display.div.style.top = '0px'; |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + base_point.x -= display.width * kVisualScale; |
| + display.div.style.left = base_point.x + 'px'; |
| + break; |
| + case SecondaryDisplayLayout.TOP: |
| + display.div.style.left = '0px'; |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + base_point.y -= display.height * kVisualScale; |
| + display.div.style.top = base_point.y + 'px'; |
| + break; |
| + case SecondaryDisplayLayout.BOTTOM: |
| + display.div.style.left = '0px'; |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + display.div.style.top = base_point.y + 'px'; |
| + base_point.y += display.height * kVisualScale; |
| + break; |
| + } |
| + |
| + this.displays_view_.appendChild(div); |
| + } |
| + }, |
| + |
| + /** |
| + * 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. |
| + */ |
| + onDisplayChanged_: function(mirroring, displays, layout) { |
| + this.mirroring_ = mirroring; |
| + this.displays_ = displays; |
| + this.layout_ = layout; |
| + |
| + $('display-options-toggle-mirroring').textContent = |
| + loadTimeData.getString( |
| + this.mirroring_ ? 'stop-mirroring' : 'start-mirroring'); |
| + |
| + if (this.displays_.length <= 1) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + return; |
| + } |
| + |
| + if (this.focused_index_ == null) { |
|
yoshiki
2012/06/15 10:21:28
I think focused_index_ should be reset to 1 when d
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + // Focus to the first display next to the primary one. |
| + this.focused_index_ = 1; |
| + } |
| + |
| + this.resetDisplaysView_(); |
| + if (this.mirroring_) { |
|
yoshiki
2012/06/15 10:21:28
ditto
Jun Mukai
2012/06/18 01:15:10
Done.
|
| + this.layoutMirroringDisplays_(); |
| + } else { |
| + this.layoutDisplays_(); |
| + } |
| + }, |
| + }; |
| + |
| + DisplayOptions.setDisplayInfo = function(mirroring, displays, layout) { |
| + DisplayOptions.getInstance().onDisplayChanged_(mirroring, displays, layout); |
| + }; |
| + |
| + // Export |
| + return { |
| + DisplayOptions: DisplayOptions |
| + }; |
| +}); |