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 |