| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 cr.define('options', function() { |
| 6 var OptionsPage = options.OptionsPage; |
| 7 |
| 8 // The scale ratio of the display rectangle to its original size. |
| 9 /** @const */ var VISUAL_SCALE = 1 / 10; |
| 10 |
| 11 /** |
| 12 * Enumeration of secondary display layout. The value has to be same as the |
| 13 * values in ash/monitor/monitor_controller.cc. |
| 14 * @enum {number} |
| 15 */ |
| 16 var SecondaryDisplayLayout = { |
| 17 TOP: 0, |
| 18 RIGHT: 1, |
| 19 BOTTOM: 2, |
| 20 LEFT: 3 |
| 21 }; |
| 22 |
| 23 /** |
| 24 * Encapsulated handling of the 'Display' page. |
| 25 * @constructor |
| 26 */ |
| 27 function DisplayOptions() { |
| 28 OptionsPage.call(this, 'display', |
| 29 loadTimeData.getString('displayOptionsPageTabTitle'), |
| 30 'display-options'); |
| 31 this.mirroring_ = false; |
| 32 this.focused_index_ = null; |
| 33 this.displays_ = []; |
| 34 } |
| 35 |
| 36 cr.addSingletonGetter(DisplayOptions); |
| 37 |
| 38 DisplayOptions.prototype = { |
| 39 __proto__: OptionsPage.prototype, |
| 40 |
| 41 /** |
| 42 * Initialize the page. |
| 43 */ |
| 44 initializePage: function() { |
| 45 OptionsPage.prototype.initializePage.call(this); |
| 46 |
| 47 $('display-options-confirm').onclick = function() { |
| 48 OptionsPage.closeOverlay(); |
| 49 }; |
| 50 |
| 51 $('display-options-toggle-mirroring').onclick = (function() { |
| 52 this.mirroring_ = !this.mirroring_; |
| 53 chrome.send('setMirroring', [this.mirroring_]); |
| 54 }).bind(this); |
| 55 |
| 56 chrome.send('getDisplayInfo'); |
| 57 }, |
| 58 |
| 59 /** |
| 60 * Mouse move handler for dragging display rectangle. |
| 61 * @private |
| 62 * @param {Event} e The mouse move event. |
| 63 */ |
| 64 onMouseMove_: function(e) { |
| 65 if (!this.dragging_) |
| 66 return true; |
| 67 |
| 68 var index = -1; |
| 69 for (var i = 0; i < this.displays_.length; i++) { |
| 70 if (this.displays_[i] == this.dragging_.display) { |
| 71 index = i; |
| 72 break; |
| 73 } |
| 74 } |
| 75 if (index < 0) |
| 76 return true; |
| 77 |
| 78 // Note that current code of moving display-rectangles doesn't work |
| 79 // if there are >=3 displays. This is our assumption for M21. |
| 80 // TODO(mukai): Fix the code to allow >=3 displays. |
| 81 var mouse_position = { |
| 82 x: e.pageX - this.dragging_.offset.x, |
| 83 y: e.pageY - this.dragging_.offset.y |
| 84 }; |
| 85 var new_position = { |
| 86 x: mouse_position.x - this.dragging_.click_location.x, |
| 87 y: mouse_position.y - this.dragging_.click_location.y |
| 88 }; |
| 89 |
| 90 var primary_div = this.displays_[0].div; |
| 91 var display = this.dragging_.display; |
| 92 |
| 93 // Separate the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of |
| 94 // the primary display, and decide which area the display should reside. |
| 95 var diagonal_slope = primary_div.offsetHeight / primary_div.offsetWidth; |
| 96 var top_down_intercept = |
| 97 primary_div.offsetTop - primary_div.offsetLeft * diagonal_slope; |
| 98 var bottom_up_intercept = primary_div.offsetTop + |
| 99 primary_div.offsetHeight + primary_div.offsetLeft * diagonal_slope; |
| 100 |
| 101 if (mouse_position.y > |
| 102 top_down_intercept + mouse_position.x * diagonal_slope) { |
| 103 if (mouse_position.y > |
| 104 bottom_up_intercept - mouse_position.x * diagonal_slope) |
| 105 this.layout_ = SecondaryDisplayLayout.BOTTOM; |
| 106 else |
| 107 this.layout_ = SecondaryDisplayLayout.LEFT; |
| 108 } else { |
| 109 if (mouse_position.y > |
| 110 bottom_up_intercept - mouse_position.x * diagonal_slope) |
| 111 this.layout_ = SecondaryDisplayLayout.RIGHT; |
| 112 else |
| 113 this.layout_ = SecondaryDisplayLayout.TOP; |
| 114 } |
| 115 |
| 116 if (this.layout_ == SecondaryDisplayLayout.LEFT || |
| 117 this.layout_ == SecondaryDisplayLayout.RIGHT) { |
| 118 if (new_position.y > primary_div.offsetTop + primary_div.offsetHeight) |
| 119 this.layout_ = SecondaryDisplayLayout.BOTTOM; |
| 120 else if (new_position.y + display.div.offsetHeight < |
| 121 primary_div.offsetTop) |
| 122 this.layout_ = SecondaryDisplayLayout.TOP; |
| 123 } else { |
| 124 if (new_position.y > primary_div.offsetLeft + primary_div.offsetWidth) |
| 125 this.layout_ = SecondaryDisplayLayout.RIGHT; |
| 126 else if (new_position.y + display.div.offsetWidth < |
| 127 primary_div.offstLeft) |
| 128 this.layout_ = SecondaryDisplayLayout.LEFT; |
| 129 } |
| 130 |
| 131 switch (this.layout_) { |
| 132 case SecondaryDisplayLayout.RIGHT: |
| 133 display.div.style.left = |
| 134 primary_div.offsetLeft + primary_div.offsetWidth + 'px'; |
| 135 display.div.style.top = new_position.y + 'px'; |
| 136 break; |
| 137 case SecondaryDisplayLayout.LEFT: |
| 138 display.div.style.left = |
| 139 primary_div.offsetLeft - display.div.offsetWidth + 'px'; |
| 140 display.div.style.top = new_position.y + 'px'; |
| 141 break; |
| 142 case SecondaryDisplayLayout.TOP: |
| 143 display.div.style.top = |
| 144 primary_div.offsetTop - display.div.offsetHeight + 'px'; |
| 145 display.div.style.left = new_position.x + 'px'; |
| 146 break; |
| 147 case SecondaryDisplayLayout.BOTTOM: |
| 148 display.div.style.top = |
| 149 primary_div.offsetTop + primary_div.offsetHeight + 'px'; |
| 150 display.div.style.left = new_position.x + 'px'; |
| 151 break; |
| 152 } |
| 153 |
| 154 return false; |
| 155 }, |
| 156 |
| 157 /** |
| 158 * Mouse down handler for dragging display rectangle. |
| 159 * @private |
| 160 * @param {Event} e The mouse down event. |
| 161 */ |
| 162 onMouseDown_: function(e) { |
| 163 if (this.mirroring_) |
| 164 return true; |
| 165 |
| 166 if (e.button != 0) |
| 167 return true; |
| 168 |
| 169 if (e.target == this.displays_view_) |
| 170 return true; |
| 171 |
| 172 for (var i = 0; i < this.displays_.length; i++) { |
| 173 var display = this.displays_[i]; |
| 174 if (display.div == e.target) { |
| 175 // Do not drag the primary monitor. |
| 176 if (i == 0) |
| 177 return true; |
| 178 |
| 179 this.focused_index_ = i; |
| 180 this.dragging_ = { |
| 181 display: display, |
| 182 click_location: {x: e.offsetX, y: e.offsetY}, |
| 183 offset: {x: e.pageX - e.offsetX - display.div.offsetLeft, |
| 184 y: e.pageY - e.offsetY - display.div.offsetTop} |
| 185 }; |
| 186 if (display.div.className.indexOf('displays-focused') == -1) |
| 187 display.div.className += ' displays-focused'; |
| 188 } else if (display.div.className.indexOf('displays-focused') >= 0) { |
| 189 // We can assume that '-primary' monitor doesn't have '-focused'. |
| 190 this.displays_[i].div.className = 'displays-display'; |
| 191 } |
| 192 } |
| 193 return false; |
| 194 }, |
| 195 |
| 196 /** |
| 197 * Mouse up handler for dragging display rectangle. |
| 198 * @private |
| 199 * @param {Event} e The mouse up event. |
| 200 */ |
| 201 onMouseUp_: function(e) { |
| 202 if (this.dragging_) { |
| 203 this.dragging_ = null; |
| 204 chrome.send('setDisplayLayout', [this.layout_]); |
| 205 } |
| 206 return false; |
| 207 }, |
| 208 |
| 209 /** |
| 210 * Clears the drawing area for display rectangles. |
| 211 * @private |
| 212 */ |
| 213 resetDisplaysView_: function() { |
| 214 var displays_view_host = $('display-options-displays-view-host'); |
| 215 displays_view_host.removeChild(displays_view_host.firstChild); |
| 216 this.displays_view_ = document.createElement('div'); |
| 217 this.displays_view_.id = 'display-options-displays-view'; |
| 218 this.displays_view_.onmousemove = this.onMouseMove_.bind(this); |
| 219 this.displays_view_.onmousedown = this.onMouseDown_.bind(this); |
| 220 this.displays_view_.onmouseup = this.onMouseUp_.bind(this); |
| 221 displays_view_host.appendChild(this.displays_view_); |
| 222 }, |
| 223 |
| 224 /** |
| 225 * Lays out the display rectangles for mirroring. |
| 226 * @private |
| 227 */ |
| 228 layoutMirroringDisplays_: function() { |
| 229 // The width/height should be same as the primary display: |
| 230 var width = this.displays_[0].width * VISUAL_SCALE; |
| 231 var height = this.displays_[0].height * VISUAL_SCALE; |
| 232 |
| 233 this.displays_view_.style.height = |
| 234 height + this.displays_.length * 2 + 'px'; |
| 235 |
| 236 for (var i = 0; i < this.displays_.length; i++) { |
| 237 var div = document.createElement('div'); |
| 238 this.displays_[i].div = div; |
| 239 div.className = 'displays-display'; |
| 240 div.style.top = i * 2 + 'px'; |
| 241 div.style.left = i * 2 + 'px'; |
| 242 div.style.width = width + 'px'; |
| 243 div.style.height = height + 'px'; |
| 244 div.style.zIndex = i; |
| 245 if (i == this.displays_.length - 1) |
| 246 div.className += ' displays-primary'; |
| 247 this.displays_view_.appendChild(div); |
| 248 } |
| 249 }, |
| 250 |
| 251 /** |
| 252 * Layouts the display rectangles according to the current layout_. |
| 253 * @private |
| 254 */ |
| 255 layoutDisplays_: function() { |
| 256 var total_size = {width: 0, height: 0}; |
| 257 for (var i = 0; i < this.displays_.length; i++) { |
| 258 total_size.width += this.displays_[i].width * VISUAL_SCALE; |
| 259 total_size.height += this.displays_[i].height * VISUAL_SCALE; |
| 260 } |
| 261 |
| 262 this.displays_view_.style.width = total_size.width + 'px'; |
| 263 this.displays_view_.style.height = total_size.height + 'px'; |
| 264 |
| 265 var base_point = {x: 0, y: 0}; |
| 266 if (this.layout_ == SecondaryDisplayLayout.LEFT) |
| 267 base_point.x = total_size.width; |
| 268 else if (this.layout_ == SecondaryDisplayLayout.TOP) |
| 269 base_point.y = total_size.height; |
| 270 |
| 271 for (var i = 0; i < this.displays_.length; i++) { |
| 272 var display = this.displays_[i]; |
| 273 var div = document.createElement('div'); |
| 274 display.div = div; |
| 275 |
| 276 div.className = 'displays-display'; |
| 277 if (i == 0) |
| 278 div.className += ' displays-primary'; |
| 279 else if (i == this.focused_index_) |
| 280 div.className += ' displays-focused'; |
| 281 div.style.width = display.width * VISUAL_SCALE + 'px'; |
| 282 div.style.height = display.height * VISUAL_SCALE + 'px'; |
| 283 div.style.lineHeight = div.style.height; |
| 284 switch (this.layout_) { |
| 285 case SecondaryDisplayLayout.RIGHT: |
| 286 display.div.style.top = '0'; |
| 287 display.div.style.left = base_point.x + 'px'; |
| 288 base_point.x += display.width * VISUAL_SCALE; |
| 289 break; |
| 290 case SecondaryDisplayLayout.LEFT: |
| 291 display.div.style.top = '0'; |
| 292 base_point.x -= display.width * VISUAL_SCALE; |
| 293 display.div.style.left = base_point.x + 'px'; |
| 294 break; |
| 295 case SecondaryDisplayLayout.TOP: |
| 296 display.div.style.left = '0'; |
| 297 base_point.y -= display.height * VISUAL_SCALE; |
| 298 display.div.style.top = base_point.y + 'px'; |
| 299 break; |
| 300 case SecondaryDisplayLayout.BOTTOM: |
| 301 display.div.style.left = '0'; |
| 302 display.div.style.top = base_point.y + 'px'; |
| 303 base_point.y += display.height * VISUAL_SCALE; |
| 304 break; |
| 305 } |
| 306 |
| 307 this.displays_view_.appendChild(div); |
| 308 } |
| 309 }, |
| 310 |
| 311 /** |
| 312 * Called when the display arrangement has changed. |
| 313 * @private |
| 314 * @param {boolean} mirroring Whether current mode is mirroring or not. |
| 315 * @param {Array} displays The list of the display information. |
| 316 * @param {SecondaryDisplayLayout} layout The layout strategy. |
| 317 */ |
| 318 onDisplayChanged_: function(mirroring, displays, layout) { |
| 319 this.mirroring_ = mirroring; |
| 320 this.layout_ = layout; |
| 321 |
| 322 $('display-options-toggle-mirroring').textContent = |
| 323 loadTimeData.getString( |
| 324 this.mirroring_ ? 'stopMirroring' : 'startMirroring'); |
| 325 |
| 326 // Focus to the first display next to the primary one when |displays| list |
| 327 // is updated. |
| 328 if (this.displays_.length != displays.length) |
| 329 this.focused_index_ = 1; |
| 330 |
| 331 this.displays_ = displays; |
| 332 |
| 333 if (this.displays_.length <= 1) |
| 334 return; |
| 335 |
| 336 this.resetDisplaysView_(); |
| 337 if (this.mirroring_) |
| 338 this.layoutMirroringDisplays_(); |
| 339 else |
| 340 this.layoutDisplays_(); |
| 341 }, |
| 342 }; |
| 343 |
| 344 DisplayOptions.setDisplayInfo = function(mirroring, displays, layout) { |
| 345 DisplayOptions.getInstance().onDisplayChanged_(mirroring, displays, layout); |
| 346 }; |
| 347 |
| 348 // Export |
| 349 return { |
| 350 DisplayOptions: DisplayOptions |
| 351 }; |
| 352 }); |
| OLD | NEW |