Home | History | Annotate | Download | only in ui
      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 {boolean} secondIsSidebar
     34  * @param {string=} settingName
     35  * @param {number=} defaultSidebarWidth
     36  * @param {number=} defaultSidebarHeight
     37  * @param {boolean=} constraintsInDip
     38  */
     39 WebInspector.SplitView = function(isVertical, secondIsSidebar, settingName, defaultSidebarWidth, defaultSidebarHeight, constraintsInDip)
     40 {
     41     WebInspector.View.call(this);
     42 
     43     this.registerRequiredCSS("splitView.css");
     44     this.element.classList.add("split-view");
     45 
     46     this._mainView = new WebInspector.VBox();
     47     this._mainElement = this._mainView.element;
     48     this._mainElement.className = "split-view-contents scroll-target split-view-main vbox"; // Override
     49 
     50     this._sidebarView = new WebInspector.VBox();
     51     this._sidebarElement = this._sidebarView.element;
     52     this._sidebarElement.className = "split-view-contents scroll-target split-view-sidebar vbox"; // Override
     53 
     54     this._resizerElement = this.element.createChild("div", "split-view-resizer");
     55     this._resizerElement.createChild("div", "split-view-resizer-border");
     56     if (secondIsSidebar) {
     57         this._mainView.show(this.element);
     58         this._sidebarView.show(this.element);
     59     } else {
     60         this._sidebarView.show(this.element);
     61         this._mainView.show(this.element);
     62     }
     63 
     64     this._resizerWidget = new WebInspector.ResizerWidget();
     65     this._resizerWidget.setEnabled(true);
     66     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeStart, this._onResizeStart, this);
     67     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this);
     68     this._resizerWidget.addEventListener(WebInspector.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this);
     69 
     70     this._defaultSidebarWidth = defaultSidebarWidth || 200;
     71     this._defaultSidebarHeight = defaultSidebarHeight || this._defaultSidebarWidth;
     72     this._constraintsInDip = !!constraintsInDip;
     73     this._settingName = settingName;
     74 
     75     this.setSecondIsSidebar(secondIsSidebar);
     76 
     77     this._innerSetVertical(isVertical);
     78     this._showMode = WebInspector.SplitView.ShowMode.Both;
     79 
     80     // Should be called after isVertical has the right value.
     81     this.installResizer(this._resizerElement);
     82 }
     83 
     84 /** @typedef {{showMode: string, size: number}} */
     85 WebInspector.SplitView.SettingForOrientation;
     86 
     87 WebInspector.SplitView.ShowMode = {
     88     Both: "Both",
     89     OnlyMain: "OnlyMain",
     90     OnlySidebar: "OnlySidebar"
     91 }
     92 
     93 WebInspector.SplitView.Events = {
     94     SidebarSizeChanged: "SidebarSizeChanged",
     95     ShowModeChanged: "ShowModeChanged"
     96 }
     97 
     98 WebInspector.SplitView.MinPadding = 20;
     99 
    100 WebInspector.SplitView.prototype = {
    101     /**
    102      * @return {boolean}
    103      */
    104     isVertical: function()
    105     {
    106         return this._isVertical;
    107     },
    108 
    109     /**
    110      * @param {boolean} isVertical
    111      */
    112     setVertical: function(isVertical)
    113     {
    114         if (this._isVertical === isVertical)
    115             return;
    116 
    117         this._innerSetVertical(isVertical);
    118 
    119         if (this.isShowing())
    120             this._updateLayout();
    121     },
    122 
    123     /**
    124      * @param {boolean} isVertical
    125      */
    126     _innerSetVertical: function(isVertical)
    127     {
    128         this.element.classList.remove(this._isVertical ? "hbox" : "vbox");
    129         this._isVertical = isVertical;
    130         this.element.classList.add(this._isVertical ? "hbox" : "vbox");
    131         delete this._resizerElementSize;
    132         this._sidebarSize = -1;
    133         this._restoreSidebarSizeFromSettings();
    134         if (this._shouldSaveShowMode)
    135             this._restoreAndApplyShowModeFromSettings();
    136         this._updateShowHideSidebarButton();
    137         // FIXME: reverse SplitView.isVertical meaning.
    138         this._resizerWidget.setVertical(!isVertical);
    139         this.invalidateConstraints();
    140     },
    141 
    142     /**
    143      * @param {boolean=} animate
    144      */
    145     _updateLayout: function(animate)
    146     {
    147         delete this._totalSize; // Lazy update.
    148         delete this._totalSizeOtherDimension;
    149 
    150         // Remove properties that might affect total size calculation.
    151         this._mainElement.style.removeProperty("width");
    152         this._mainElement.style.removeProperty("height");
    153         this._sidebarElement.style.removeProperty("width");
    154         this._sidebarElement.style.removeProperty("height");
    155 
    156         this._innerSetSidebarSize(this._preferredSidebarSize(), !!animate);
    157     },
    158 
    159     /**
    160      * @return {!Element}
    161      */
    162     mainElement: function()
    163     {
    164         return this._mainElement;
    165     },
    166 
    167     /**
    168      * @return {!Element}
    169      */
    170     sidebarElement: function()
    171     {
    172         return this._sidebarElement;
    173     },
    174 
    175     /**
    176      * @return {boolean}
    177      */
    178     isSidebarSecond: function()
    179     {
    180         return this._secondIsSidebar;
    181     },
    182 
    183     enableShowModeSaving: function()
    184     {
    185         this._shouldSaveShowMode = true;
    186         this._restoreAndApplyShowModeFromSettings();
    187     },
    188 
    189     /**
    190      * @return {string}
    191      */
    192     showMode: function()
    193     {
    194         return this._showMode;
    195     },
    196 
    197     /**
    198      * @param {boolean} secondIsSidebar
    199      */
    200     setSecondIsSidebar: function(secondIsSidebar)
    201     {
    202         this._mainElement.classList.toggle("split-view-contents-first", secondIsSidebar);
    203         this._mainElement.classList.toggle("split-view-contents-second", !secondIsSidebar);
    204         this._sidebarElement.classList.toggle("split-view-contents-first", !secondIsSidebar);
    205         this._sidebarElement.classList.toggle("split-view-contents-second", secondIsSidebar);
    206 
    207         // Make sure second is last in the children array.
    208         if (secondIsSidebar) {
    209             if (this._sidebarElement.parentElement && this._sidebarElement.nextSibling)
    210                 this.element.appendChild(this._sidebarElement);
    211         } else {
    212             if (this._mainElement.parentElement && this._mainElement.nextSibling)
    213                 this.element.appendChild(this._mainElement);
    214         }
    215 
    216         this._secondIsSidebar = secondIsSidebar;
    217     },
    218 
    219     /**
    220      * @return {?string}
    221      */
    222     sidebarSide: function()
    223     {
    224         if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
    225             return null;
    226         return this._isVertical ?
    227             (this._secondIsSidebar ? "right" : "left") :
    228             (this._secondIsSidebar ? "bottom" : "top");
    229     },
    230 
    231     /**
    232      * @return {number}
    233      */
    234     preferredSidebarSize: function()
    235     {
    236         return this._preferredSidebarSize();
    237     },
    238 
    239     /**
    240      * @return {!Element}
    241      */
    242     resizerElement: function()
    243     {
    244         return this._resizerElement;
    245     },
    246 
    247     /**
    248      * @param {boolean=} animate
    249      */
    250     hideMain: function(animate)
    251     {
    252         this._showOnly(this._sidebarView, this._mainView, animate);
    253         this._updateShowMode(WebInspector.SplitView.ShowMode.OnlySidebar);
    254     },
    255 
    256     /**
    257      * @param {boolean=} animate
    258      */
    259     hideSidebar: function(animate)
    260     {
    261         this._showOnly(this._mainView, this._sidebarView, animate);
    262         this._updateShowMode(WebInspector.SplitView.ShowMode.OnlyMain);
    263     },
    264 
    265     /**
    266      * @override
    267      */
    268     detachChildViews: function()
    269     {
    270         this._mainView.detachChildViews();
    271         this._sidebarView.detachChildViews();
    272     },
    273 
    274     /**
    275      * @param {!WebInspector.View} sideToShow
    276      * @param {!WebInspector.View} sideToHide
    277      * @param {boolean=} animate
    278      */
    279     _showOnly: function(sideToShow, sideToHide, animate)
    280     {
    281         this._cancelAnimation();
    282 
    283         /**
    284          * @this {WebInspector.SplitView}
    285          */
    286         function callback()
    287         {
    288             sideToShow.show(this.element);
    289             sideToHide.detach();
    290             sideToShow.element.classList.add("maximized");
    291             sideToHide.element.classList.remove("maximized");
    292             this._resizerElement.classList.add("hidden");
    293             this._removeAllLayoutProperties();
    294         }
    295 
    296         if (animate) {
    297             this._animate(true, callback.bind(this));
    298         } else {
    299             callback.call(this);
    300             this.doResize();
    301         }
    302 
    303         this._sidebarSize = -1;
    304         this.setResizable(false);
    305     },
    306 
    307     _removeAllLayoutProperties: function()
    308     {
    309         this._sidebarElement.style.removeProperty("flexBasis");
    310 
    311         this._mainElement.style.removeProperty("width");
    312         this._mainElement.style.removeProperty("height");
    313         this._sidebarElement.style.removeProperty("width");
    314         this._sidebarElement.style.removeProperty("height");
    315 
    316         this._resizerElement.style.removeProperty("left");
    317         this._resizerElement.style.removeProperty("right");
    318         this._resizerElement.style.removeProperty("top");
    319         this._resizerElement.style.removeProperty("bottom");
    320 
    321         this._resizerElement.style.removeProperty("margin-left");
    322         this._resizerElement.style.removeProperty("margin-right");
    323         this._resizerElement.style.removeProperty("margin-top");
    324         this._resizerElement.style.removeProperty("margin-bottom");
    325     },
    326 
    327     /**
    328      * @param {boolean=} animate
    329      */
    330     showBoth: function(animate)
    331     {
    332        if (this._showMode === WebInspector.SplitView.ShowMode.Both)
    333             animate = false;
    334 
    335         this._cancelAnimation();
    336         this._mainElement.classList.remove("maximized");
    337         this._sidebarElement.classList.remove("maximized");
    338         this._resizerElement.classList.remove("hidden");
    339 
    340         this._mainView.show(this.element);
    341         this._sidebarView.show(this.element);
    342         // Order views in DOM properly.
    343         this.setSecondIsSidebar(this._secondIsSidebar);
    344 
    345         this._sidebarSize = -1;
    346         this.setResizable(true);
    347         this._updateShowMode(WebInspector.SplitView.ShowMode.Both);
    348         this._updateLayout(animate);
    349     },
    350 
    351     /**
    352      * @param {boolean} resizable
    353      */
    354     setResizable: function(resizable)
    355     {
    356         this._resizerWidget.setEnabled(resizable);
    357     },
    358 
    359     /**
    360      * @return {boolean}
    361      */
    362     isResizable: function()
    363     {
    364         return this._resizerWidget.isEnabled();
    365     },
    366 
    367     /**
    368      * @param {number} size
    369      */
    370     setSidebarSize: function(size)
    371     {
    372         size *= WebInspector.zoomManager.zoomFactor();
    373         this._savedSidebarSize = size;
    374         this._saveSetting();
    375         this._innerSetSidebarSize(size, false, true);
    376     },
    377 
    378     /**
    379      * @return {number}
    380      */
    381     sidebarSize: function()
    382     {
    383         var size = Math.max(0, this._sidebarSize);
    384         return size / WebInspector.zoomManager.zoomFactor();
    385     },
    386 
    387     /**
    388      * Returns total size in DIP.
    389      * @return {number}
    390      */
    391     _totalSizeDIP: function()
    392     {
    393         if (!this._totalSize) {
    394             this._totalSize = this._isVertical ? this.element.offsetWidth : this.element.offsetHeight;
    395             this._totalSizeOtherDimension = this._isVertical ? this.element.offsetHeight : this.element.offsetWidth;
    396         }
    397         return this._totalSize * WebInspector.zoomManager.zoomFactor();
    398     },
    399 
    400     /**
    401      * @param {string} showMode
    402      */
    403     _updateShowMode: function(showMode)
    404     {
    405         this._showMode = showMode;
    406         this._saveShowModeToSettings();
    407         this._updateShowHideSidebarButton();
    408         this.dispatchEventToListeners(WebInspector.SplitView.Events.ShowModeChanged, showMode);
    409         this.invalidateConstraints();
    410     },
    411 
    412     /**
    413      * @param {number} size
    414      * @param {boolean} animate
    415      * @param {boolean=} userAction
    416      */
    417     _innerSetSidebarSize: function(size, animate, userAction)
    418     {
    419         if (this._showMode !== WebInspector.SplitView.ShowMode.Both || !this.isShowing())
    420             return;
    421 
    422         size = this._applyConstraints(size, userAction);
    423         if (this._sidebarSize === size)
    424             return;
    425 
    426         if (!this._resizerElementSize)
    427             this._resizerElementSize = this._isVertical ? this._resizerElement.offsetWidth : this._resizerElement.offsetHeight;
    428 
    429         // Invalidate layout below.
    430 
    431         this._removeAllLayoutProperties();
    432 
    433         // this._totalSize is available below since we successfully applied constraints.
    434         var sidebarSizeValue = (size / WebInspector.zoomManager.zoomFactor()) + "px";
    435         var mainSizeValue = (this._totalSize - size / WebInspector.zoomManager.zoomFactor()) + "px";
    436         this.sidebarElement().style.flexBasis = sidebarSizeValue;
    437 
    438         // Make both sides relayout boundaries.
    439         if (this._isVertical) {
    440             this._sidebarElement.style.width = sidebarSizeValue;
    441             this._mainElement.style.width = mainSizeValue;
    442             this._sidebarElement.style.height = this._totalSizeOtherDimension + "px";
    443             this._mainElement.style.height = this._totalSizeOtherDimension + "px";
    444         } else {
    445             this._sidebarElement.style.height = sidebarSizeValue;
    446             this._mainElement.style.height = mainSizeValue;
    447             this._sidebarElement.style.width = this._totalSizeOtherDimension + "px";
    448             this._mainElement.style.width = this._totalSizeOtherDimension + "px";
    449         }
    450 
    451         // Position resizer.
    452         if (this._isVertical) {
    453             if (this._secondIsSidebar) {
    454                 this._resizerElement.style.right = sidebarSizeValue;
    455                 this._resizerElement.style.marginRight = -this._resizerElementSize / 2 + "px";
    456             } else {
    457                 this._resizerElement.style.left = sidebarSizeValue;
    458                 this._resizerElement.style.marginLeft = -this._resizerElementSize / 2 + "px";
    459             }
    460         } else {
    461             if (this._secondIsSidebar) {
    462                 this._resizerElement.style.bottom = sidebarSizeValue;
    463                 this._resizerElement.style.marginBottom = -this._resizerElementSize / 2 + "px";
    464             } else {
    465                 this._resizerElement.style.top = sidebarSizeValue;
    466                 this._resizerElement.style.marginTop = -this._resizerElementSize / 2 + "px";
    467             }
    468         }
    469 
    470         this._sidebarSize = size;
    471 
    472         // Force layout.
    473 
    474         if (animate) {
    475             this._animate(false);
    476         } else {
    477             // No need to recalculate this._sidebarSize and this._totalSize again.
    478             this.doResize();
    479             this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
    480         }
    481     },
    482 
    483     /**
    484      * @param {boolean} reverse
    485      * @param {function()=} callback
    486      */
    487     _animate: function(reverse, callback)
    488     {
    489         var animationTime = 50;
    490         this._animationCallback = callback;
    491 
    492         var animatedMarginPropertyName;
    493         if (this._isVertical)
    494             animatedMarginPropertyName = this._secondIsSidebar ? "margin-right" : "margin-left";
    495         else
    496             animatedMarginPropertyName = this._secondIsSidebar ? "margin-bottom" : "margin-top";
    497 
    498         var zoomFactor = WebInspector.zoomManager.zoomFactor();
    499         var marginFrom = reverse ? "0" : "-" + (this._sidebarSize / zoomFactor) + "px";
    500         var marginTo = reverse ? "-" + (this._sidebarSize / zoomFactor) + "px" : "0";
    501 
    502         // This order of things is important.
    503         // 1. Resize main element early and force layout.
    504         this.element.style.setProperty(animatedMarginPropertyName, marginFrom);
    505         if (!reverse) {
    506             suppressUnused(this._mainElement.offsetWidth);
    507             suppressUnused(this._sidebarElement.offsetWidth);
    508         }
    509 
    510         // 2. Issue onresize to the sidebar element, its size won't change.
    511         if (!reverse)
    512             this._sidebarView.doResize();
    513 
    514         // 3. Configure and run animation
    515         this.element.style.setProperty("transition", animatedMarginPropertyName + " " + animationTime + "ms linear");
    516 
    517         var boundAnimationFrame;
    518         var startTime;
    519         /**
    520          * @this {WebInspector.SplitView}
    521          */
    522         function animationFrame()
    523         {
    524             delete this._animationFrameHandle;
    525 
    526             if (!startTime) {
    527                 // Kick animation on first frame.
    528                 this.element.style.setProperty(animatedMarginPropertyName, marginTo);
    529                 startTime = window.performance.now();
    530             } else if (window.performance.now() < startTime + animationTime) {
    531                 // Process regular animation frame.
    532                 this._mainView.doResize();
    533             } else {
    534                 // Complete animation.
    535                 this._cancelAnimation();
    536                 this._mainView.doResize();
    537                 this.dispatchEventToListeners(WebInspector.SplitView.Events.SidebarSizeChanged, this.sidebarSize());
    538                 return;
    539             }
    540             this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
    541         }
    542         boundAnimationFrame = animationFrame.bind(this);
    543         this._animationFrameHandle = window.requestAnimationFrame(boundAnimationFrame);
    544     },
    545 
    546     _cancelAnimation: function()
    547     {
    548         this.element.style.removeProperty("margin-top");
    549         this.element.style.removeProperty("margin-right");
    550         this.element.style.removeProperty("margin-bottom");
    551         this.element.style.removeProperty("margin-left");
    552         this.element.style.removeProperty("transition");
    553 
    554         if (this._animationFrameHandle) {
    555             window.cancelAnimationFrame(this._animationFrameHandle);
    556             delete this._animationFrameHandle;
    557         }
    558         if (this._animationCallback) {
    559             this._animationCallback();
    560             delete this._animationCallback;
    561         }
    562     },
    563 
    564     /**
    565      * @param {number} sidebarSize
    566      * @param {boolean=} userAction
    567      * @return {number}
    568      */
    569     _applyConstraints: function(sidebarSize, userAction)
    570     {
    571         var totalSize = this._totalSizeDIP();
    572         var zoomFactor = this._constraintsInDip ? 1 : WebInspector.zoomManager.zoomFactor();
    573 
    574         var constraints = this._sidebarView.constraints();
    575         var minSidebarSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
    576         if (!minSidebarSize)
    577             minSidebarSize = WebInspector.SplitView.MinPadding;
    578         minSidebarSize *= zoomFactor;
    579 
    580         var preferredSidebarSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
    581         if (!preferredSidebarSize)
    582             preferredSidebarSize = WebInspector.SplitView.MinPadding;
    583         preferredSidebarSize *= zoomFactor;
    584         // Allow sidebar to be less than preferred by explicit user action.
    585         if (sidebarSize < preferredSidebarSize)
    586             preferredSidebarSize = Math.max(sidebarSize, minSidebarSize);
    587 
    588         constraints = this._mainView.constraints();
    589         var minMainSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
    590         if (!minMainSize)
    591             minMainSize = WebInspector.SplitView.MinPadding;
    592         minMainSize *= zoomFactor;
    593 
    594         var preferredMainSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
    595         if (!preferredMainSize)
    596             preferredMainSize = WebInspector.SplitView.MinPadding;
    597         preferredMainSize *= zoomFactor;
    598         var savedMainSize = this.isVertical() ? this._savedVerticalMainSize : this._savedHorizontalMainSize;
    599         if (typeof savedMainSize !== "undefined")
    600             preferredMainSize = Math.min(preferredMainSize, savedMainSize * zoomFactor);
    601         if (userAction)
    602             preferredMainSize = minMainSize;
    603 
    604         // Enough space for preferred.
    605         var totalPreferred = preferredMainSize + preferredSidebarSize;
    606         if (totalPreferred <= totalSize)
    607             return Number.constrain(sidebarSize, preferredSidebarSize, totalSize - preferredMainSize);
    608 
    609         // Enough space for minimum.
    610         if (minMainSize + minSidebarSize <= totalSize) {
    611             var delta = totalPreferred - totalSize;
    612             var sidebarDelta = delta * preferredSidebarSize / totalPreferred;
    613             sidebarSize = preferredSidebarSize - sidebarDelta;
    614             return Number.constrain(sidebarSize, minSidebarSize, totalSize - minMainSize);
    615         }
    616 
    617         // Not enough space even for minimum sizes.
    618         return Math.max(0, totalSize - minMainSize);
    619     },
    620 
    621     wasShown: function()
    622     {
    623         this._forceUpdateLayout();
    624         WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
    625     },
    626 
    627     willHide: function()
    628     {
    629         WebInspector.zoomManager.removeEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
    630     },
    631 
    632     onResize: function()
    633     {
    634         this._updateLayout();
    635     },
    636 
    637     onLayout: function()
    638     {
    639         this._updateLayout();
    640     },
    641 
    642     /**
    643      * @return {!Constraints}
    644      */
    645     calculateConstraints: function()
    646     {
    647         if (this._showMode === WebInspector.SplitView.ShowMode.OnlyMain)
    648             return this._mainView.constraints();
    649         if (this._showMode === WebInspector.SplitView.ShowMode.OnlySidebar)
    650             return this._sidebarView.constraints();
    651 
    652         var mainConstraints = this._mainView.constraints();
    653         var sidebarConstraints = this._sidebarView.constraints();
    654         var min = WebInspector.SplitView.MinPadding;
    655         if (this._isVertical) {
    656             mainConstraints = mainConstraints.widthToMax(min);
    657             sidebarConstraints = sidebarConstraints.widthToMax(min);
    658             return mainConstraints.addWidth(sidebarConstraints).heightToMax(sidebarConstraints);
    659         } else {
    660             mainConstraints = mainConstraints.heightToMax(min);
    661             sidebarConstraints = sidebarConstraints.heightToMax(min);
    662             return mainConstraints.widthToMax(sidebarConstraints).addHeight(sidebarConstraints);
    663         }
    664     },
    665 
    666     /**
    667      * @param {!WebInspector.Event} event
    668      */
    669     _onResizeStart: function(event)
    670     {
    671         this._resizeStartSize = this._sidebarSize;
    672     },
    673 
    674     /**
    675      * @param {!WebInspector.Event} event
    676      */
    677     _onResizeUpdate: function(event)
    678     {
    679         var cssOffset = event.data.currentPosition - event.data.startPosition;
    680         var dipOffset = cssOffset * WebInspector.zoomManager.zoomFactor();
    681         var newSize = this._secondIsSidebar ? this._resizeStartSize - dipOffset : this._resizeStartSize + dipOffset;
    682         var constrainedSize = this._applyConstraints(newSize, true);
    683         this._savedSidebarSize = constrainedSize;
    684         this._saveSetting();
    685         this._innerSetSidebarSize(constrainedSize, false, true);
    686         if (this.isVertical())
    687             this._savedVerticalMainSize = this._totalSizeDIP() - this._sidebarSize;
    688         else
    689             this._savedHorizontalMainSize = this._totalSizeDIP() - this._sidebarSize;
    690     },
    691 
    692     /**
    693      * @param {!WebInspector.Event} event
    694      */
    695     _onResizeEnd: function(event)
    696     {
    697         delete this._resizeStartSize;
    698     },
    699 
    700     hideDefaultResizer: function()
    701     {
    702         this.uninstallResizer(this._resizerElement);
    703     },
    704 
    705     /**
    706      * @param {!Element} resizerElement
    707      */
    708     installResizer: function(resizerElement)
    709     {
    710         this._resizerWidget.addElement(resizerElement);
    711     },
    712 
    713     /**
    714      * @param {!Element} resizerElement
    715      */
    716     uninstallResizer: function(resizerElement)
    717     {
    718         this._resizerWidget.removeElement(resizerElement);
    719     },
    720 
    721     /**
    722      * @return {boolean}
    723      */
    724     hasCustomResizer: function()
    725     {
    726         var elements = this._resizerWidget.elements();
    727         return elements.length > 1 || (elements.length == 1 && elements[0] !== this._resizerElement);
    728     },
    729 
    730     /**
    731      * @param {!Element} resizer
    732      * @param {boolean} on
    733      */
    734     toggleResizer: function(resizer, on)
    735     {
    736         if (on)
    737             this.installResizer(resizer);
    738         else
    739             this.uninstallResizer(resizer);
    740     },
    741 
    742     /**
    743      * @return {?WebInspector.Setting}
    744      */
    745     _setting: function()
    746     {
    747         if (!this._settingName)
    748             return null;
    749 
    750         if (!WebInspector.settings[this._settingName])
    751             WebInspector.settings[this._settingName] = WebInspector.settings.createSetting(this._settingName, {});
    752 
    753         return WebInspector.settings[this._settingName];
    754     },
    755 
    756     /**
    757      * @return {?WebInspector.SplitView.SettingForOrientation}
    758      */
    759     _settingForOrientation: function()
    760     {
    761         var state = this._setting() ? this._setting().get() : {};
    762         return this._isVertical ? state.vertical : state.horizontal;
    763     },
    764 
    765     /**
    766      * @return {number}
    767      */
    768     _preferredSidebarSize: function()
    769     {
    770         var size = this._savedSidebarSize;
    771         if (!size) {
    772             size = this._isVertical ? this._defaultSidebarWidth : this._defaultSidebarHeight;
    773             // If we have default value in percents, calculate it on first use.
    774             if (0 < size && size < 1)
    775                 size *= this._totalSizeDIP();
    776         }
    777         return size;
    778     },
    779 
    780     _restoreSidebarSizeFromSettings: function()
    781     {
    782         var settingForOrientation = this._settingForOrientation();
    783         this._savedSidebarSize = settingForOrientation ? settingForOrientation.size : 0;
    784     },
    785 
    786     _restoreAndApplyShowModeFromSettings: function()
    787     {
    788         var orientationState = this._settingForOrientation();
    789         this._savedShowMode = orientationState ? orientationState.showMode : WebInspector.SplitView.ShowMode.Both;
    790         this._showMode = this._savedShowMode;
    791 
    792         switch (this._savedShowMode) {
    793         case WebInspector.SplitView.ShowMode.Both:
    794             this.showBoth();
    795             break;
    796         case WebInspector.SplitView.ShowMode.OnlyMain:
    797             this.hideSidebar();
    798             break;
    799         case WebInspector.SplitView.ShowMode.OnlySidebar:
    800             this.hideMain();
    801             break;
    802         }
    803     },
    804 
    805     _saveShowModeToSettings: function()
    806     {
    807         this._savedShowMode = this._showMode;
    808         this._saveSetting();
    809     },
    810 
    811     _saveSetting: function()
    812     {
    813         var setting = this._setting();
    814         if (!setting)
    815             return;
    816         var state = setting.get();
    817         var orientationState = (this._isVertical ? state.vertical : state.horizontal) || {};
    818 
    819         orientationState.size = this._savedSidebarSize;
    820         if (this._shouldSaveShowMode)
    821             orientationState.showMode = this._savedShowMode;
    822 
    823         if (this._isVertical)
    824             state.vertical = orientationState;
    825         else
    826             state.horizontal = orientationState;
    827         setting.set(state);
    828     },
    829 
    830     _forceUpdateLayout: function()
    831     {
    832         // Force layout even if sidebar size does not change.
    833         this._sidebarSize = -1;
    834         this._updateLayout();
    835     },
    836 
    837     /**
    838      * @param {!WebInspector.Event} event
    839      */
    840     _onZoomChanged: function(event)
    841     {
    842         this._forceUpdateLayout();
    843     },
    844 
    845     /**
    846      * @param {string} title
    847      * @param {string} className
    848      * @return {!WebInspector.StatusBarButton}
    849      */
    850     createShowHideSidebarButton: function(title, className)
    851     {
    852         console.assert(this.isVertical(), "Buttons for split view with horizontal split are not supported yet.");
    853 
    854         this._showHideSidebarButtonTitle = WebInspector.UIString(title);
    855         this._showHideSidebarButton = new WebInspector.StatusBarButton("", "sidebar-show-hide-button " + className, 3);
    856         this._showHideSidebarButton.addEventListener("click", buttonClicked.bind(this));
    857         this._updateShowHideSidebarButton();
    858 
    859         /**
    860          * @this {WebInspector.SplitView}
    861          * @param {!WebInspector.Event} event
    862          */
    863         function buttonClicked(event)
    864         {
    865             if (this._showMode !== WebInspector.SplitView.ShowMode.Both)
    866                 this.showBoth(true);
    867             else
    868                 this.hideSidebar(true);
    869         }
    870 
    871         return this._showHideSidebarButton;
    872     },
    873 
    874     _updateShowHideSidebarButton: function()
    875     {
    876         if (!this._showHideSidebarButton)
    877             return;
    878         var sidebarHidden = this._showMode === WebInspector.SplitView.ShowMode.OnlyMain;
    879         this._showHideSidebarButton.state = sidebarHidden ? "show" : "hide";
    880         this._showHideSidebarButton.element.classList.toggle("top-sidebar-show-hide-button", !this.isVertical() && !this.isSidebarSecond());
    881         this._showHideSidebarButton.element.classList.toggle("right-sidebar-show-hide-button", this.isVertical() && this.isSidebarSecond());
    882         this._showHideSidebarButton.element.classList.toggle("bottom-sidebar-show-hide-button", !this.isVertical() && this.isSidebarSecond());
    883         this._showHideSidebarButton.element.classList.toggle("left-sidebar-show-hide-button", this.isVertical() && !this.isSidebarSecond());
    884         this._showHideSidebarButton.title = sidebarHidden ? WebInspector.UIString("Show %s", this._showHideSidebarButtonTitle) : WebInspector.UIString("Hide %s", this._showHideSidebarButtonTitle);
    885     },
    886 
    887     __proto__: WebInspector.View.prototype
    888 }
    889