| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 cr.define('options', function() { | 5 cr.define('options', function() { |
| 6 var OptionsPage = options.OptionsPage; | 6 var OptionsPage = options.OptionsPage; |
| 7 | 7 |
| 8 // The scale ratio of the display rectangle to its original size. | 8 // The scale ratio of the display rectangle to its original size. |
| 9 /** @const */ var VISUAL_SCALE = 1 / 10; | 9 /** @const */ var VISUAL_SCALE = 1 / 10; |
| 10 | 10 |
| 11 // The number of pixels to share the edges between displays. |
| 12 /** @const */ var MIN_OFFSET_OVERLAP = 5; |
| 13 |
| 11 /** | 14 /** |
| 12 * Enumeration of secondary display layout. The value has to be same as the | 15 * Enumeration of secondary display layout. The value has to be same as the |
| 13 * values in ash/monitor/monitor_controller.cc. | 16 * values in ash/monitor/monitor_controller.cc. |
| 14 * @enum {number} | 17 * @enum {number} |
| 15 */ | 18 */ |
| 16 var SecondaryDisplayLayout = { | 19 var SecondaryDisplayLayout = { |
| 17 TOP: 0, | 20 TOP: 0, |
| 18 RIGHT: 1, | 21 RIGHT: 1, |
| 19 BOTTOM: 2, | 22 BOTTOM: 2, |
| 20 LEFT: 3 | 23 LEFT: 3 |
| 21 }; | 24 }; |
| 22 | 25 |
| 23 /** | 26 /** |
| 24 * Encapsulated handling of the 'Display' page. | 27 * Encapsulated handling of the 'Display' page. |
| 25 * @constructor | 28 * @constructor |
| 26 */ | 29 */ |
| 27 function DisplayOptions() { | 30 function DisplayOptions() { |
| 28 OptionsPage.call(this, 'display', | 31 OptionsPage.call(this, 'display', |
| 29 loadTimeData.getString('displayOptionsPageTabTitle'), | 32 loadTimeData.getString('displayOptionsPageTabTitle'), |
| 30 'display-options'); | 33 'display-options'); |
| 31 this.mirroring_ = false; | 34 this.mirroring_ = false; |
| 32 this.focused_index_ = null; | 35 this.focusedIndex_ = null; |
| 33 this.displays_ = []; | 36 this.displays_ = []; |
| 34 } | 37 } |
| 35 | 38 |
| 36 cr.addSingletonGetter(DisplayOptions); | 39 cr.addSingletonGetter(DisplayOptions); |
| 37 | 40 |
| 38 DisplayOptions.prototype = { | 41 DisplayOptions.prototype = { |
| 39 __proto__: OptionsPage.prototype, | 42 __proto__: OptionsPage.prototype, |
| 40 | 43 |
| 41 /** | 44 /** |
| 42 * Initialize the page. | 45 * Initialize the page. |
| 43 */ | 46 */ |
| 44 initializePage: function() { | 47 initializePage: function() { |
| 45 OptionsPage.prototype.initializePage.call(this); | 48 OptionsPage.prototype.initializePage.call(this); |
| 46 | 49 |
| 47 $('display-options-toggle-mirroring').onclick = (function() { | 50 $('display-options-toggle-mirroring').onclick = (function() { |
| 48 this.mirroring_ = !this.mirroring_; | 51 this.mirroring_ = !this.mirroring_; |
| 49 chrome.send('setMirroring', [this.mirroring_]); | 52 chrome.send('setMirroring', [this.mirroring_]); |
| 50 }).bind(this); | 53 }).bind(this); |
| 51 | 54 |
| 52 $('display-options-apply').onclick = (function() { | 55 $('display-options-apply').onclick = this.applyResult_.bind(this); |
| 53 chrome.send('setDisplayLayout', [this.layout_]); | |
| 54 }).bind(this); | |
| 55 chrome.send('getDisplayInfo'); | 56 chrome.send('getDisplayInfo'); |
| 56 }, | 57 }, |
| 57 | 58 |
| 58 /** | 59 /** |
| 60 * Collects the current data and sends it to Chrome. |
| 61 * @private |
| 62 */ |
| 63 applyResult_: function() { |
| 64 // Offset is calculated from top or left edge. |
| 65 var primary = this.displays_[0]; |
| 66 var secondary = this.displays_[1]; |
| 67 var offset; |
| 68 if (this.layout_ == SecondaryDisplayLayout.LEFT || |
| 69 this.layout_ == SecondaryDisplayLayout.RIGHT) { |
| 70 offset = secondary.div.offsetTop - primary.div.offsetTop; |
| 71 } else { |
| 72 offset = secondary.div.offsetLeft - primary.div.offsetLeft; |
| 73 } |
| 74 chrome.send('setDisplayLayout', [this.layout_, offset / VISUAL_SCALE]); |
| 75 }, |
| 76 |
| 77 /** |
| 59 * Mouse move handler for dragging display rectangle. | 78 * Mouse move handler for dragging display rectangle. |
| 60 * @private | 79 * @private |
| 61 * @param {Event} e The mouse move event. | 80 * @param {Event} e The mouse move event. |
| 62 */ | 81 */ |
| 63 onMouseMove_: function(e) { | 82 onMouseMove_: function(e) { |
| 64 if (!this.dragging_) | 83 if (!this.dragging_) |
| 65 return true; | 84 return true; |
| 66 | 85 |
| 67 var index = -1; | 86 var index = -1; |
| 68 for (var i = 0; i < this.displays_.length; i++) { | 87 for (var i = 0; i < this.displays_.length; i++) { |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 this.updateSelectedDisplayDescription_(); | 215 this.updateSelectedDisplayDescription_(); |
| 197 return false; | 216 return false; |
| 198 }, | 217 }, |
| 199 | 218 |
| 200 /** | 219 /** |
| 201 * Mouse up handler for dragging display rectangle. | 220 * Mouse up handler for dragging display rectangle. |
| 202 * @private | 221 * @private |
| 203 * @param {Event} e The mouse up event. | 222 * @param {Event} e The mouse up event. |
| 204 */ | 223 */ |
| 205 onMouseUp_: function(e) { | 224 onMouseUp_: function(e) { |
| 206 if (this.dragging_) | 225 if (this.dragging_) { |
| 226 // Make sure the dragging location is connected. |
| 227 var primaryDiv = this.displays_[0].div; |
| 228 var draggingDiv = this.dragging_.display.div; |
| 229 if (this.layout_ == SecondaryDisplayLayout.LEFT || |
| 230 this.layout_ == SecondaryDisplayLayout.RIGHT) { |
| 231 var top = Math.max(draggingDiv.offsetTop, |
| 232 primaryDiv.offsetTop - draggingDiv.offsetHeight + |
| 233 MIN_OFFSET_OVERLAP); |
| 234 top = Math.min(top, |
| 235 primaryDiv.offsetTop + primaryDiv.offsetHeight - |
| 236 MIN_OFFSET_OVERLAP); |
| 237 draggingDiv.style.top = top + 'px'; |
| 238 } else { |
| 239 var left = Math.max(draggingDiv.offsetLeft, |
| 240 primaryDiv.offsetLeft - draggingDiv.offsetWidth + |
| 241 MIN_OFFSET_OVERLAP); |
| 242 left = Math.min(left, |
| 243 primaryDiv.offsetLeft + primaryDiv.offsetWidth - |
| 244 MIN_OFFSET_OVERLAP); |
| 245 draggingDiv.style.left = left + 'px'; |
| 246 } |
| 207 this.dragging_ = null; | 247 this.dragging_ = null; |
| 248 } |
| 208 this.updateSelectedDisplayDescription_(); | 249 this.updateSelectedDisplayDescription_(); |
| 209 return false; | 250 return false; |
| 210 }, | 251 }, |
| 211 | 252 |
| 212 /** | 253 /** |
| 213 * Updates the description of the selected display section. | 254 * Updates the description of the selected display section. |
| 214 * @private | 255 * @private |
| 215 */ | 256 */ |
| 216 updateSelectedDisplayDescription_: function() { | 257 updateSelectedDisplayDescription_: function() { |
| 217 if (this.focusedIndex_ == null || | 258 if (this.focusedIndex_ == null || |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 this.displaysView_.appendChild(div); | 342 this.displaysView_.appendChild(div); |
| 302 } | 343 } |
| 303 }, | 344 }, |
| 304 | 345 |
| 305 /** | 346 /** |
| 306 * Layouts the display rectangles according to the current layout_. | 347 * Layouts the display rectangles according to the current layout_. |
| 307 * @private | 348 * @private |
| 308 */ | 349 */ |
| 309 layoutDisplays_: function() { | 350 layoutDisplays_: function() { |
| 310 var totalHeight = 0; | 351 var totalHeight = 0; |
| 352 var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; |
| 311 for (var i = 0; i < this.displays_.length; i++) { | 353 for (var i = 0; i < this.displays_.length; i++) { |
| 312 totalHeight += this.displays_[i].height * VISUAL_SCALE; | 354 var display = this.displays_[i]; |
| 355 totalHeight += display.height * VISUAL_SCALE; |
| 356 boundingBox.left = Math.min(boundingBox.left, display.x * VISUAL_SCALE); |
| 357 boundingBox.right = Math.max( |
| 358 boundingBox.right, (display.x + display.width) * VISUAL_SCALE); |
| 359 boundingBox.top = Math.min(boundingBox.top, display.y * VISUAL_SCALE); |
| 360 boundingBox.bottom = Math.max( |
| 361 boundingBox.bottom, (display.y + display.height) * VISUAL_SCALE); |
| 313 } | 362 } |
| 314 | 363 |
| 315 // Prepare enough area for thisplays_view by adding the maximum height. | 364 // Prepare enough area for thisplays_view by adding the maximum height. |
| 316 this.displaysView_.style.height = totalHeight + 'px'; | 365 this.displaysView_.style.height = totalHeight + 'px'; |
| 317 | 366 |
| 318 var basePoint = {x: 0, y: 0}; | 367 // Centering the bounding box of the display rectangles. |
| 319 var boundingSize = {width: 0, height: 0}; | 368 var offset = {x: $('display-options-displays-view').offsetWidth / 2 - |
| 369 (boundingBox.left + boundingBox.right) / 2, |
| 370 y: totalHeight / 2 - |
| 371 (boundingBox.top + boundingBox.bottom) / 2}; |
| 372 |
| 373 |
| 320 for (var i = 0; i < this.displays_.length; i++) { | 374 for (var i = 0; i < this.displays_.length; i++) { |
| 321 var display = this.displays_[i]; | 375 var display = this.displays_[i]; |
| 322 var div = document.createElement('div'); | 376 var div = document.createElement('div'); |
| 323 display.div = div; | 377 display.div = div; |
| 324 | 378 |
| 325 div.className = 'displays-display'; | 379 div.className = 'displays-display'; |
| 326 if (i == this.focusedIndex_) | 380 if (i == this.focusedIndex_) |
| 327 div.classList.add('displays-focused'); | 381 div.classList.add('displays-focused'); |
| 328 div.style.width = display.width * VISUAL_SCALE + 'px'; | 382 div.style.width = display.width * VISUAL_SCALE + 'px'; |
| 329 div.style.height = display.height * VISUAL_SCALE + 'px'; | 383 div.style.height = display.height * VISUAL_SCALE + 'px'; |
| 330 div.style.lineHeight = div.style.height; | 384 div.style.lineHeight = div.style.height; |
| 331 if (i == 0) { | 385 if (i == 0) { |
| 332 // Assumes that first display is primary and put a grey rectangle to | 386 // Assumes that first display is primary and put a grey rectangle to |
| 333 // denote launcher below. | 387 // denote launcher below. |
| 334 var launcher = document.createElement('div'); | 388 var launcher = document.createElement('div'); |
| 335 launcher.id = 'display-launcher'; | 389 launcher.id = 'display-launcher'; |
| 336 launcher.style.width = display.div.style.width; | 390 launcher.style.width = display.div.style.width; |
| 337 div.appendChild(launcher); | 391 div.appendChild(launcher); |
| 338 } | 392 } |
| 339 switch (this.layout_) { | 393 div.style.left = display.x * VISUAL_SCALE + offset.x + 'px'; |
| 340 case SecondaryDisplayLayout.RIGHT: | 394 div.style.top = display.y * VISUAL_SCALE + offset.y + 'px'; |
| 341 display.div.style.top = '0'; | |
| 342 display.div.style.left = basePoint.x + 'px'; | |
| 343 basePoint.x += display.width * VISUAL_SCALE; | |
| 344 boundingSize.width += display.width * VISUAL_SCALE; | |
| 345 boundingSize.height = Math.max(boundingSize.height, | |
| 346 display.height * VISUAL_SCALE); | |
| 347 break; | |
| 348 case SecondaryDisplayLayout.LEFT: | |
| 349 display.div.style.top = '0'; | |
| 350 basePoint.x -= display.width * VISUAL_SCALE; | |
| 351 display.div.style.left = basePoint.x + 'px'; | |
| 352 boundingSize.width += display.width * VISUAL_SCALE; | |
| 353 boundingSize.height = Math.max(boundingSize.height, | |
| 354 display.height * VISUAL_SCALE); | |
| 355 break; | |
| 356 case SecondaryDisplayLayout.TOP: | |
| 357 display.div.style.left = '0'; | |
| 358 basePoint.y -= display.height * VISUAL_SCALE; | |
| 359 display.div.style.top = basePoint.y + 'px'; | |
| 360 boundingSize.width = Math.max(boundingSize.width, | |
| 361 display.width * VISUAL_SCALE); | |
| 362 boundingSize.height += display.height * VISUAL_SCALE; | |
| 363 break; | |
| 364 case SecondaryDisplayLayout.BOTTOM: | |
| 365 display.div.style.left = '0'; | |
| 366 display.div.style.top = basePoint.y + 'px'; | |
| 367 basePoint.y += display.height * VISUAL_SCALE; | |
| 368 boundingSize.width = Math.max(boundingSize.width, | |
| 369 display.width * VISUAL_SCALE); | |
| 370 boundingSize.height += display.height * VISUAL_SCALE; | |
| 371 break; | |
| 372 } | |
| 373 | 395 |
| 374 div.appendChild(document.createTextNode(display.name)); | 396 div.appendChild(document.createTextNode(display.name)); |
| 375 | 397 |
| 376 this.displaysView_.appendChild(div); | 398 this.displaysView_.appendChild(div); |
| 377 } | 399 } |
| 378 | |
| 379 // Centering the display rectangles. | |
| 380 var offset = {x: $('display-options-displays-view').offsetWidth / 2 - | |
| 381 boundingSize.width / 2, | |
| 382 y: totalHeight / 2 - boundingSize.height / 2}; | |
| 383 if (basePoint.x < 0) | |
| 384 offset.x -= basePoint.x; | |
| 385 if (basePoint.y < 0) | |
| 386 offset.y -= basePoint.y; | |
| 387 for (var i = 0; i < this.displays_.length; i++) { | |
| 388 var div = this.displays_[i].div; | |
| 389 div.style.left = div.offsetLeft + offset.x + 'px'; | |
| 390 div.style.top = div.offsetTop + offset.y + 'px'; | |
| 391 } | |
| 392 }, | 400 }, |
| 393 | 401 |
| 394 /** | 402 /** |
| 395 * Called when the display arrangement has changed. | 403 * Called when the display arrangement has changed. |
| 396 * @private | 404 * @private |
| 397 * @param {boolean} mirroring Whether current mode is mirroring or not. | 405 * @param {boolean} mirroring Whether current mode is mirroring or not. |
| 398 * @param {Array} displays The list of the display information. | 406 * @param {Array} displays The list of the display information. |
| 399 * @param {SecondaryDisplayLayout} layout The layout strategy. | 407 * @param {SecondaryDisplayLayout} layout The layout strategy. |
| 408 * @param {number} offset The offset of the secondary display. |
| 400 */ | 409 */ |
| 401 onDisplayChanged_: function(mirroring, displays, layout) { | 410 onDisplayChanged_: function(mirroring, displays, layout, offset) { |
| 402 this.mirroring_ = mirroring; | 411 this.mirroring_ = mirroring; |
| 403 this.layout_ = layout; | 412 this.layout_ = layout; |
| 413 this.offset_ = offset; |
| 404 | 414 |
| 405 $('display-options-toggle-mirroring').textContent = | 415 $('display-options-toggle-mirroring').textContent = |
| 406 loadTimeData.getString( | 416 loadTimeData.getString( |
| 407 this.mirroring_ ? 'stopMirroring' : 'startMirroring'); | 417 this.mirroring_ ? 'stopMirroring' : 'startMirroring'); |
| 408 | 418 |
| 409 // Focus to the first display next to the primary one when |displays| list | 419 // Focus to the first display next to the primary one when |displays| list |
| 410 // is updated. | 420 // is updated. |
| 411 if (this.mirroring_) | 421 if (this.mirroring_) |
| 412 this.focusedIndex_ = null; | 422 this.focusedIndex_ = null; |
| 413 else if (this.displays_.length != displays.length) | 423 else if (this.displays_.length != displays.length) |
| 414 this.focusedIndex_ = 1; | 424 this.focusedIndex_ = 1; |
| 415 | 425 |
| 416 this.displays_ = displays; | 426 this.displays_ = displays; |
| 417 | 427 |
| 418 this.resetDisplaysView_(); | 428 this.resetDisplaysView_(); |
| 419 if (this.mirroring_) | 429 if (this.mirroring_) |
| 420 this.layoutMirroringDisplays_(); | 430 this.layoutMirroringDisplays_(); |
| 421 else | 431 else |
| 422 this.layoutDisplays_(); | 432 this.layoutDisplays_(); |
| 423 this.updateSelectedDisplayDescription_(); | 433 this.updateSelectedDisplayDescription_(); |
| 424 }, | 434 }, |
| 425 }; | 435 }; |
| 426 | 436 |
| 427 DisplayOptions.setDisplayInfo = function(mirroring, displays, layout) { | 437 DisplayOptions.setDisplayInfo = function( |
| 428 DisplayOptions.getInstance().onDisplayChanged_(mirroring, displays, layout); | 438 mirroring, displays, layout, offset) { |
| 439 DisplayOptions.getInstance().onDisplayChanged_( |
| 440 mirroring, displays, layout, offset); |
| 429 }; | 441 }; |
| 430 | 442 |
| 431 // Export | 443 // Export |
| 432 return { | 444 return { |
| 433 DisplayOptions: DisplayOptions | 445 DisplayOptions: DisplayOptions |
| 434 }; | 446 }; |
| 435 }); | 447 }); |
| OLD | NEW |