1/* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/** 30 * @constructor 31 * @extends {WebInspector.View} 32 * @param {boolean} isVertical 33 * @param {string=} sidebarSizeSettingName 34 * @param {number=} defaultSidebarWidth 35 * @param {number=} defaultSidebarHeight 36 */ 37WebInspector.SplitView = function(isVertical, sidebarSizeSettingName, defaultSidebarWidth, defaultSidebarHeight) 38{ 39 WebInspector.View.call(this); 40 41 this.registerRequiredCSS("splitView.css"); 42 43 this.element.className = "split-view"; 44 45 this._firstElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-first"); 46 this._secondElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-second"); 47 48 this._resizerElement = this.element.createChild("div", "split-view-resizer"); 49 this.installResizer(this._resizerElement); 50 this._resizable = true; 51 52 this._savedSidebarWidth = defaultSidebarWidth || 200; 53 this._savedSidebarHeight = defaultSidebarHeight || this._savedSidebarWidth; 54 55 if (0 < this._savedSidebarWidth && this._savedSidebarWidth < 1 && 56 0 < this._savedSidebarHeight && this._savedSidebarHeight < 1) 57 this._useFraction = true; 58 59 this._sidebarSizeSettingName = sidebarSizeSettingName; 60 61 this.setSecondIsSidebar(true); 62 63 this._innerSetVertical(isVertical); 64} 65 66WebInspector.SplitView.prototype = { 67 /** 68 * @return {boolean} 69 */ 70 isVertical: function() 71 { 72 return this._isVertical; 73 }, 74 75 /** 76 * @param {boolean} isVertical 77 */ 78 setVertical: function(isVertical) 79 { 80 if (this._isVertical === isVertical) 81 return; 82 83 this._innerSetVertical(isVertical); 84 85 if (this.isShowing()) 86 this._updateLayout(); 87 }, 88 89 /** 90 * @param {boolean} isVertical 91 */ 92 _innerSetVertical: function(isVertical) 93 { 94 this.element.removeStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal"); 95 this._isVertical = isVertical; 96 this.element.addStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal"); 97 }, 98 99 _updateLayout: function() 100 { 101 this._updateTotalSize(); 102 103 delete this._sidebarSize; // Force update. 104 this.setSidebarSize(this._lastSidebarSize()); 105 }, 106 107 /** 108 * @return {Element} 109 */ 110 firstElement: function() 111 { 112 return this._firstElement; 113 }, 114 115 /** 116 * @return {Element} 117 */ 118 secondElement: function() 119 { 120 return this._secondElement; 121 }, 122 123 /** 124 * @return {Element} 125 */ 126 get mainElement() 127 { 128 return this.isSidebarSecond() ? this.firstElement() : this.secondElement(); 129 }, 130 131 /** 132 * @return {Element} 133 */ 134 get sidebarElement() 135 { 136 return this.isSidebarSecond() ? this.secondElement() : this.firstElement(); 137 }, 138 139 /** 140 * @return {boolean} 141 */ 142 isSidebarSecond: function() 143 { 144 return this._secondIsSidebar; 145 }, 146 147 /** 148 * @param {boolean} secondIsSidebar 149 */ 150 setSecondIsSidebar: function(secondIsSidebar) 151 { 152 this.sidebarElement.removeStyleClass("split-view-sidebar"); 153 this._secondIsSidebar = secondIsSidebar; 154 this.sidebarElement.addStyleClass("split-view-sidebar"); 155 }, 156 157 /** 158 * @return {Element} 159 */ 160 resizerElement: function() 161 { 162 return this._resizerElement; 163 }, 164 165 showOnlyFirst: function() 166 { 167 this._showOnly(this._firstElement, this._secondElement); 168 }, 169 170 showOnlySecond: function() 171 { 172 this._showOnly(this._secondElement, this._firstElement); 173 }, 174 175 /** 176 * @param {Element} sideA 177 * @param {Element} sideB 178 */ 179 _showOnly: function(sideA, sideB) 180 { 181 sideA.removeStyleClass("hidden"); 182 sideA.addStyleClass("maximized"); 183 sideB.addStyleClass("hidden"); 184 sideB.removeStyleClass("maximized"); 185 this._removeAllLayoutProperties(); 186 187 this._isShowingOne = true; 188 this.setResizable(false); 189 this.doResize(); 190 }, 191 192 _removeAllLayoutProperties: function() 193 { 194 this._firstElement.style.removeProperty("right"); 195 this._firstElement.style.removeProperty("bottom"); 196 this._firstElement.style.removeProperty("width"); 197 this._firstElement.style.removeProperty("height"); 198 199 this._secondElement.style.removeProperty("left"); 200 this._secondElement.style.removeProperty("top"); 201 this._secondElement.style.removeProperty("width"); 202 this._secondElement.style.removeProperty("height"); 203 204 this._resizerElement.style.removeProperty("left"); 205 this._resizerElement.style.removeProperty("right"); 206 this._resizerElement.style.removeProperty("top"); 207 this._resizerElement.style.removeProperty("bottom"); 208 209 this._resizerElement.style.removeProperty("margin-left"); 210 this._resizerElement.style.removeProperty("margin-right"); 211 this._resizerElement.style.removeProperty("margin-top"); 212 this._resizerElement.style.removeProperty("margin-bottom"); 213 }, 214 215 showBoth: function() 216 { 217 this._isShowingOne = false; 218 this._firstElement.removeStyleClass("hidden"); 219 this._firstElement.removeStyleClass("maximized"); 220 this._secondElement.removeStyleClass("hidden"); 221 this._secondElement.removeStyleClass("maximized"); 222 223 this._updateLayout(); 224 225 this.setResizable(true); 226 this.doResize(); 227 }, 228 229 /** 230 * @param {boolean} resizable 231 */ 232 setResizable: function(resizable) 233 { 234 if (this._resizable === resizable) 235 return; 236 this._resizable = resizable; 237 if (resizable) 238 this._resizerElement.removeStyleClass("hidden"); 239 else 240 this._resizerElement.addStyleClass("hidden"); 241 }, 242 243 /** 244 * @param {number} size 245 */ 246 setSidebarSize: function(size) 247 { 248 if (this._sidebarSize === size) 249 return; 250 251 size = this.applyConstraints(size); 252 this._innerSetSidebarSize(size); 253 this._saveSidebarSize(size); 254 }, 255 256 /** 257 * @return {number} 258 */ 259 sidebarSize: function() 260 { 261 return this._sidebarSize; 262 }, 263 264 /** 265 * @return {number} 266 */ 267 totalSize: function() 268 { 269 return this._totalSize; 270 }, 271 272 _updateTotalSize: function() 273 { 274 this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight; 275 if (this._useFraction) 276 this._sidebarSize = this._lastSidebarSize(); 277 }, 278 279 /** 280 * @param {number} size 281 */ 282 _innerSetSidebarSize: function(size) 283 { 284 if (this._isShowingOne) 285 return; 286 287 this._removeAllLayoutProperties(); 288 289 var sizeValue; 290 if (this._useFraction) 291 sizeValue = (size / this._totalSize) * 100 + "%"; 292 else 293 sizeValue = size + "px"; 294 295 if (this._isVertical) { 296 var resizerWidth = this._resizerElement.offsetWidth; 297 if (this._secondIsSidebar) { 298 this._firstElement.style.right = sizeValue; 299 this._secondElement.style.width = sizeValue; 300 this._resizerElement.style.right = sizeValue; 301 this._resizerElement.style.marginRight = -resizerWidth / 2 + "px"; 302 } else { 303 this._firstElement.style.width = sizeValue; 304 this._secondElement.style.left = sizeValue; 305 this._resizerElement.style.left = sizeValue; 306 this._resizerElement.style.marginLeft = -resizerWidth / 2 + "px"; 307 } 308 } else { 309 var resizerHeight = this._resizerElement.offsetHeight; 310 311 if (this._secondIsSidebar) { 312 this._firstElement.style.bottom = sizeValue; 313 this._secondElement.style.height = sizeValue; 314 this._resizerElement.style.bottom = sizeValue; 315 this._resizerElement.style.marginBottom = -resizerHeight / 2 + "px"; 316 } else { 317 this._firstElement.style.height = sizeValue; 318 this._secondElement.style.top = sizeValue; 319 this._resizerElement.style.top = sizeValue; 320 this._resizerElement.style.marginTop = -resizerHeight / 2 + "px"; 321 } 322 } 323 324 this._sidebarSize = size; 325 this.doResize(); 326 }, 327 328 /** 329 * @param {number} size 330 * @return {number} 331 */ 332 applyConstraints: function(size) 333 { 334 const minSize = 20; 335 size = Math.max(size, minSize); 336 if (this._totalSize - size < minSize) 337 size = this._totalSize - minSize; 338 return size; 339 }, 340 341 wasShown: function() 342 { 343 this._updateLayout(); 344 }, 345 346 onResize: function() 347 { 348 this._updateTotalSize(); 349 }, 350 351 /** 352 * @param {Event} event 353 * @return {boolean} 354 */ 355 _startResizerDragging: function(event) 356 { 357 if (!this._resizable) 358 return false; 359 360 this._dragOffset = (this._secondIsSidebar ? this._totalSize - this._sidebarSize : this._sidebarSize) - (this._isVertical ? event.pageX : event.pageY); 361 return true; 362 }, 363 364 /** 365 * @param {Event} event 366 */ 367 _resizerDragging: function(event) 368 { 369 var newOffset = (this._isVertical ? event.pageX : event.pageY) + this._dragOffset; 370 var newSize = (this._secondIsSidebar ? this._totalSize - newOffset : newOffset); 371 this.setSidebarSize(newSize); 372 event.preventDefault(); 373 }, 374 375 /** 376 * @param {Event} event 377 */ 378 _endResizerDragging: function(event) 379 { 380 delete this._dragOffset; 381 }, 382 383 /** 384 * @param {Element} resizerElement 385 */ 386 installResizer: function(resizerElement) 387 { 388 resizerElement.addEventListener("mousedown", this._onDragStart.bind(this), false); 389 }, 390 391 /** 392 * 393 * @param {Event} event 394 */ 395 _onDragStart: function(event) 396 { 397 WebInspector._elementDragStart(this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), this._isVertical ? "ew-resize" : "ns-resize", event); 398 }, 399 400 /** 401 * @return {WebInspector.Setting} 402 */ 403 _sizeSetting: function() 404 { 405 if (!this._sidebarSizeSettingName) 406 return null; 407 408 var settingName = this._sidebarSizeSettingName + (this._isVertical ? "" : "H"); 409 if (!WebInspector.settings[settingName]) 410 WebInspector.settings[settingName] = WebInspector.settings.createSetting(settingName, undefined); 411 412 return WebInspector.settings[settingName]; 413 }, 414 415 /** 416 * @return {number} 417 */ 418 _lastSidebarSize: function() 419 { 420 var sizeSetting = this._sizeSetting(); 421 var size = sizeSetting ? sizeSetting.get() : 0; 422 if (!size) 423 size = this._isVertical ? this._savedSidebarWidth : this._savedSidebarHeight; 424 if (this._useFraction) 425 size *= this._totalSize; 426 return size; 427 }, 428 429 /** 430 * @param {number} size 431 */ 432 _saveSidebarSize: function(size) 433 { 434 if (this._useFraction) 435 size /= this._totalSize; 436 437 if (this._isVertical) 438 this._savedSidebarWidth = size; 439 else 440 this._savedSidebarHeight = size; 441 442 var sizeSetting = this._sizeSetting(); 443 if (sizeSetting) 444 sizeSetting.set(size); 445 }, 446 447 __proto__: WebInspector.View.prototype 448} 449