blob: 52ac4149db78deeba3e066e91d4858d49f333806 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
* OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @extends {WebInspector.View}
* @param {boolean} isVertical
* @param {string=} sidebarSizeSettingName
* @param {number=} defaultSidebarWidth
* @param {number=} defaultSidebarHeight
*/
WebInspector.SplitView = function(isVertical, sidebarSizeSettingName, defaultSidebarWidth, defaultSidebarHeight)
{
WebInspector.View.call(this);
this.registerRequiredCSS("splitView.css");
this.element.className = "split-view";
this._firstElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-first");
this._secondElement = this.element.createChild("div", "split-view-contents scroll-target split-view-contents-second");
this._resizerElement = this.element.createChild("div", "split-view-resizer");
this.installResizer(this._resizerElement);
this._resizable = true;
this._savedSidebarWidth = defaultSidebarWidth || 200;
this._savedSidebarHeight = defaultSidebarHeight || this._savedSidebarWidth;
if (0 < this._savedSidebarWidth && this._savedSidebarWidth < 1 &&
0 < this._savedSidebarHeight && this._savedSidebarHeight < 1)
this._useFraction = true;
this._sidebarSizeSettingName = sidebarSizeSettingName;
this.setSecondIsSidebar(true);
this._innerSetVertical(isVertical);
}
WebInspector.SplitView.prototype = {
/**
* @return {boolean}
*/
isVertical: function()
{
return this._isVertical;
},
/**
* @param {boolean} isVertical
*/
setVertical: function(isVertical)
{
if (this._isVertical === isVertical)
return;
this._innerSetVertical(isVertical);
if (this.isShowing())
this._updateLayout();
},
/**
* @param {boolean} isVertical
*/
_innerSetVertical: function(isVertical)
{
this.element.removeStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal");
this._isVertical = isVertical;
this.element.addStyleClass(this._isVertical ? "split-view-vertical" : "split-view-horizontal");
delete this._resizerElementSize;
this._sidebarSize = -1;
},
_updateLayout: function()
{
delete this._totalSize; // Lazy update.
this._innerSetSidebarSize(this._lastSidebarSize());
},
/**
* @return {Element}
*/
firstElement: function()
{
return this._firstElement;
},
/**
* @return {Element}
*/
secondElement: function()
{
return this._secondElement;
},
/**
* @return {Element}
*/
get mainElement()
{
return this.isSidebarSecond() ? this.firstElement() : this.secondElement();
},
/**
* @return {Element}
*/
get sidebarElement()
{
return this.isSidebarSecond() ? this.secondElement() : this.firstElement();
},
/**
* @return {boolean}
*/
isSidebarSecond: function()
{
return this._secondIsSidebar;
},
/**
* @param {boolean} secondIsSidebar
*/
setSecondIsSidebar: function(secondIsSidebar)
{
this.sidebarElement.removeStyleClass("split-view-sidebar");
this._secondIsSidebar = secondIsSidebar;
this.sidebarElement.addStyleClass("split-view-sidebar");
},
/**
* @return {Element}
*/
resizerElement: function()
{
return this._resizerElement;
},
showOnlyFirst: function()
{
this._showOnly(this._firstElement, this._secondElement);
},
showOnlySecond: function()
{
this._showOnly(this._secondElement, this._firstElement);
},
/**
* @param {Element} sideA
* @param {Element} sideB
*/
_showOnly: function(sideA, sideB)
{
sideA.removeStyleClass("hidden");
sideA.addStyleClass("maximized");
sideB.addStyleClass("hidden");
sideB.removeStyleClass("maximized");
this._removeAllLayoutProperties();
this._isShowingOne = true;
this._sidebarSize = -1;
this.setResizable(false);
this.doResize();
},
_removeAllLayoutProperties: function()
{
this._firstElement.style.removeProperty("right");
this._firstElement.style.removeProperty("bottom");
this._firstElement.style.removeProperty("width");
this._firstElement.style.removeProperty("height");
this._secondElement.style.removeProperty("left");
this._secondElement.style.removeProperty("top");
this._secondElement.style.removeProperty("width");
this._secondElement.style.removeProperty("height");
this._resizerElement.style.removeProperty("left");
this._resizerElement.style.removeProperty("right");
this._resizerElement.style.removeProperty("top");
this._resizerElement.style.removeProperty("bottom");
this._resizerElement.style.removeProperty("margin-left");
this._resizerElement.style.removeProperty("margin-right");
this._resizerElement.style.removeProperty("margin-top");
this._resizerElement.style.removeProperty("margin-bottom");
},
showBoth: function()
{
this._firstElement.removeStyleClass("hidden");
this._firstElement.removeStyleClass("maximized");
this._secondElement.removeStyleClass("hidden");
this._secondElement.removeStyleClass("maximized");
this._isShowingOne = false;
this._sidebarSize = -1;
this.setResizable(true);
this.doResize();
},
/**
* @param {boolean} resizable
*/
setResizable: function(resizable)
{
this._resizable = resizable;
this._resizerElement.enableStyleClass("hidden", !resizable);
},
/**
* @param {number} size
*/
setSidebarSize: function(size)
{
this._innerSetSidebarSize(size);
this._saveSidebarSize();
},
/**
* @return {number}
*/
sidebarSize: function()
{
return Math.max(0, this._sidebarSize);
},
/**
* @return {number}
*/
totalSize: function()
{
if (!this._totalSize)
this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight;
return this._totalSize;
},
/**
* @param {number} size
*/
_innerSetSidebarSize: function(size)
{
if (this._isShowingOne) {
this._sidebarSize = size;
return;
}
size = this._applyConstraints(size);
if (this._sidebarSize === size)
return;
if (size < 0) {
// Never apply bad values, fix it upon onResize instead.
return;
}
this._removeAllLayoutProperties();
var sizeValue;
if (this._useFraction)
sizeValue = (size / this.totalSize()) * 100 + "%";
else
sizeValue = size + "px";
if (!this._resizerElementSize)
this._resizerElementSize = this._isVertical ? this._resizerElement.offsetWidth : this._resizerElement.offsetHeight;
if (this._isVertical) {
if (this._secondIsSidebar) {
this._firstElement.style.right = sizeValue;
this._secondElement.style.width = sizeValue;
this._resizerElement.style.right = sizeValue;
this._resizerElement.style.marginRight = -this._resizerElementSize / 2 + "px";
} else {
this._firstElement.style.width = sizeValue;
this._secondElement.style.left = sizeValue;
this._resizerElement.style.left = sizeValue;
this._resizerElement.style.marginLeft = -this._resizerElementSize / 2 + "px";
}
} else {
if (this._secondIsSidebar) {
this._firstElement.style.bottom = sizeValue;
this._secondElement.style.height = sizeValue;
this._resizerElement.style.bottom = sizeValue;
this._resizerElement.style.marginBottom = -this._resizerElementSize / 2 + "px";
} else {
this._firstElement.style.height = sizeValue;
this._secondElement.style.top = sizeValue;
this._resizerElement.style.top = sizeValue;
this._resizerElement.style.marginTop = -this._resizerElementSize / 2 + "px";
}
}
this._sidebarSize = size;
// No need to recalculate this._sidebarSize and this._totalSize again.
this._muteOnResize = true;
this.doResize();
delete this._muteOnResize;
},
/**
* @param {number=} minWidth
* @param {number=} minHeight
*/
setSidebarElementConstraints: function(minWidth, minHeight)
{
if (typeof minWidth === "number")
this._minimumSidebarWidth = minWidth;
if (typeof minHeight === "number")
this._minimumSidebarHeight = minHeight;
},
/**
* @param {number=} minWidth
* @param {number=} minHeight
*/
setMainElementConstraints: function(minWidth, minHeight)
{
if (typeof minWidth === "number")
this._minimumMainWidth = minWidth;
if (typeof minHeight === "number")
this._minimumMainHeight = minHeight;
},
/**
* @param {number} sidebarSize
* @return {number}
*/
_applyConstraints: function(sidebarSize)
{
const minPadding = 20;
var totalSize = this.totalSize();
var from = (this.isVertical() ? this._minimumSidebarWidth : this._minimumSidebarHeight) || 0;
var fromInPercents = false;
if (from && from < 1) {
fromInPercents = true;
from = Math.round(totalSize * from);
}
from = Math.max(from, minPadding);
var minMainSize = (this.isVertical() ? this._minimumMainWidth : this._minimumMainHeight) || 0;
var toInPercents = false;
if (minMainSize && minMainSize < 1) {
toInPercents = true;
minMainSize = Math.round(totalSize * minMainSize);
}
minMainSize = Math.max(minMainSize, minPadding);
var to = totalSize - minMainSize;
if (from <= to)
return Number.constrain(sidebarSize, from, to);
// Respect fixed constraints over percents. This will, for example, shrink
// the sidebar to its minimum size when possible.
if (!fromInPercents && !toInPercents)
return -1;
if (toInPercents && sidebarSize >= from && from < totalSize)
return from;
if (fromInPercents && sidebarSize <= to && to < totalSize)
return to;
return -1;
},
wasShown: function()
{
this._updateLayout();
},
onResize: function()
{
if (this._muteOnResize)
return;
this._updateLayout();
},
/**
* @param {Event} event
* @return {boolean}
*/
_startResizerDragging: function(event)
{
if (!this._resizable)
return false;
this._saveSidebarSizeRecursively();
this._dragOffset = (this._secondIsSidebar ? this.totalSize() - this._sidebarSize : this._sidebarSize) - (this._isVertical ? event.pageX : event.pageY);
return true;
},
/**
* @param {Event} event
*/
_resizerDragging: function(event)
{
var newOffset = (this._isVertical ? event.pageX : event.pageY) + this._dragOffset;
var newSize = (this._secondIsSidebar ? this.totalSize() - newOffset : newOffset);
this.setSidebarSize(newSize);
event.preventDefault();
},
/**
* @param {Event} event
*/
_endResizerDragging: function(event)
{
delete this._dragOffset;
this._saveSidebarSizeRecursively();
},
_saveSidebarSizeRecursively: function()
{
/** @this {WebInspector.View} */
function doSaveSidebarSizeRecursively()
{
if (this._saveSidebarSize)
this._saveSidebarSize();
this._callOnVisibleChildren(doSaveSidebarSizeRecursively);
}
this._saveSidebarSize();
this._callOnVisibleChildren(doSaveSidebarSizeRecursively);
},
/**
* @param {Element} resizerElement
*/
installResizer: function(resizerElement)
{
resizerElement.addEventListener("mousedown", this._onDragStart.bind(this), false);
},
/**
*
* @param {Event} event
*/
_onDragStart: function(event)
{
WebInspector._elementDragStart(this._startResizerDragging.bind(this), this._resizerDragging.bind(this), this._endResizerDragging.bind(this), this._isVertical ? "ew-resize" : "ns-resize", event);
},
/**
* @return {WebInspector.Setting}
*/
_sizeSetting: function()
{
if (!this._sidebarSizeSettingName)
return null;
var settingName = this._sidebarSizeSettingName + (this._isVertical ? "" : "H");
if (!WebInspector.settings[settingName])
WebInspector.settings[settingName] = WebInspector.settings.createSetting(settingName, undefined);
return WebInspector.settings[settingName];
},
/**
* @return {number}
*/
_lastSidebarSize: function()
{
var sizeSetting = this._sizeSetting();
var size = sizeSetting ? sizeSetting.get() : 0;
if (!size)
size = this._isVertical ? this._savedSidebarWidth : this._savedSidebarHeight;
if (this._useFraction)
size *= this.totalSize();
return size;
},
_saveSidebarSize: function()
{
var size = this._sidebarSize;
if (size < 0)
return;
if (this._useFraction)
size /= this.totalSize();
if (this._isVertical)
this._savedSidebarWidth = size;
else
this._savedSidebarHeight = size;
var sizeSetting = this._sizeSetting();
if (sizeSetting)
sizeSetting.set(size);
},
__proto__: WebInspector.View.prototype
}