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