blob: bb083a537ab9cf575e6acf66fc89710da8ac063c [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
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 * @unrestricted
31 */
32UI.SplitWidget = class extends UI.Widget {
33 /**
34 * @param {boolean} isVertical
35 * @param {boolean} secondIsSidebar
36 * @param {string=} settingName
37 * @param {number=} defaultSidebarWidth
38 * @param {number=} defaultSidebarHeight
39 * @param {boolean=} constraintsInDip
40 */
41 constructor(isVertical, secondIsSidebar, settingName, defaultSidebarWidth, defaultSidebarHeight, constraintsInDip) {
42 super(true);
43 this.element.classList.add('split-widget');
44 this.registerRequiredCSS('ui/splitWidget.css');
45
46 this.contentElement.classList.add('shadow-split-widget');
Joel Einbinder27131b22019-03-14 09:29:5447 this._sidebarElement =
48 this.contentElement.createChild('div', 'shadow-split-widget-contents shadow-split-widget-sidebar vbox');
Blink Reformat4c46d092018-04-07 15:32:3749 this._mainElement =
50 this.contentElement.createChild('div', 'shadow-split-widget-contents shadow-split-widget-main vbox');
Joel Einbinder7fbe24c2019-01-24 05:19:0151 this._mainElement.createChild('slot').name = 'insertion-point-main';
Joel Einbinder7fbe24c2019-01-24 05:19:0152 this._sidebarElement.createChild('slot').name = 'insertion-point-sidebar';
Blink Reformat4c46d092018-04-07 15:32:3753 this._resizerElement = this.contentElement.createChild('div', 'shadow-split-widget-resizer');
54 this._resizerElementSize = null;
55
56 this._resizerWidget = new UI.SimpleResizerWidget();
57 this._resizerWidget.setEnabled(true);
58 this._resizerWidget.addEventListener(UI.ResizerWidget.Events.ResizeStart, this._onResizeStart, this);
59 this._resizerWidget.addEventListener(UI.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this);
60 this._resizerWidget.addEventListener(UI.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this);
61
62 this._defaultSidebarWidth = defaultSidebarWidth || 200;
63 this._defaultSidebarHeight = defaultSidebarHeight || this._defaultSidebarWidth;
64 this._constraintsInDip = !!constraintsInDip;
65 this._resizeStartSizeDIP = 0;
66 this._setting = settingName ? Common.settings.createSetting(settingName, {}) : null;
67
68 this._totalSizeCSS = 0;
69 this._totalSizeOtherDimensionCSS = 0;
70 /** @type {?UI.Widget} */
71 this._mainWidget = null;
72 /** @type {?UI.Widget} */
73 this._sidebarWidget = null;
74 this._animationFrameHandle = 0;
75 /** @type {?function()} */
76 this._animationCallback = null;
77 this._showHideSidebarButtonTitle = '';
78 /** @type {?UI.ToolbarButton} */
79 this._showHideSidebarButton = null;
80 this._isVertical = false;
81 this._sidebarMinimized = false;
82 this._detaching = false;
83 this._sidebarSizeDIP = -1;
84 this._savedSidebarSizeDIP = this._sidebarSizeDIP;
85 this._secondIsSidebar = false;
86 this._shouldSaveShowMode = false;
87 /** @type {?number} */
88 this._savedVerticalMainSize = null;
89 /** @type {?number} */
90 this._savedHorizontalMainSize = null;
91
92 this.setSecondIsSidebar(secondIsSidebar);
93
94 this._innerSetVertical(isVertical);
95 this._showMode = UI.SplitWidget.ShowMode.Both;
96 this._savedShowMode = this._showMode;
97
98 // Should be called after isVertical has the right value.
99 this.installResizer(this._resizerElement);
100 }
101
102 /**
103 * @return {boolean}
104 */
105 isVertical() {
106 return this._isVertical;
107 }
108
109 /**
110 * @param {boolean} isVertical
111 */
112 setVertical(isVertical) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34113 if (this._isVertical === isVertical) {
Blink Reformat4c46d092018-04-07 15:32:37114 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34115 }
Blink Reformat4c46d092018-04-07 15:32:37116
117 this._innerSetVertical(isVertical);
118
Tim van der Lippe1d6e57a2019-09-30 11:55:34119 if (this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37120 this._updateLayout();
Tim van der Lippe1d6e57a2019-09-30 11:55:34121 }
Blink Reformat4c46d092018-04-07 15:32:37122 }
123
124 /**
125 * @param {boolean} isVertical
126 */
127 _innerSetVertical(isVertical) {
128 this.contentElement.classList.toggle('vbox', !isVertical);
129 this.contentElement.classList.toggle('hbox', isVertical);
130 this._isVertical = isVertical;
131
132 this._resizerElementSize = null;
133 this._sidebarSizeDIP = -1;
134 this._restoreSidebarSizeFromSettings();
Tim van der Lippe1d6e57a2019-09-30 11:55:34135 if (this._shouldSaveShowMode) {
Blink Reformat4c46d092018-04-07 15:32:37136 this._restoreAndApplyShowModeFromSettings();
Tim van der Lippe1d6e57a2019-09-30 11:55:34137 }
Blink Reformat4c46d092018-04-07 15:32:37138 this._updateShowHideSidebarButton();
139 // FIXME: reverse SplitWidget.isVertical meaning.
140 this._resizerWidget.setVertical(!isVertical);
141 this.invalidateConstraints();
142 }
143
144 /**
145 * @param {boolean=} animate
146 */
147 _updateLayout(animate) {
148 this._totalSizeCSS = 0; // Lazy update.
149 this._totalSizeOtherDimensionCSS = 0;
150
151 // Remove properties that might affect total size calculation.
152 this._mainElement.style.removeProperty('width');
153 this._mainElement.style.removeProperty('height');
154 this._sidebarElement.style.removeProperty('width');
155 this._sidebarElement.style.removeProperty('height');
156
157 this._innerSetSidebarSizeDIP(this._preferredSidebarSizeDIP(), !!animate);
158 }
159
160 /**
161 * @param {!UI.Widget} widget
162 */
163 setMainWidget(widget) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34164 if (this._mainWidget === widget) {
Blink Reformat4c46d092018-04-07 15:32:37165 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34166 }
Blink Reformat4c46d092018-04-07 15:32:37167 this.suspendInvalidations();
Tim van der Lippe1d6e57a2019-09-30 11:55:34168 if (this._mainWidget) {
Blink Reformat4c46d092018-04-07 15:32:37169 this._mainWidget.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34170 }
Blink Reformat4c46d092018-04-07 15:32:37171 this._mainWidget = widget;
172 if (widget) {
Joel Einbinder7fbe24c2019-01-24 05:19:01173 widget.element.slot = 'insertion-point-main';
Tim van der Lippe1d6e57a2019-09-30 11:55:34174 if (this._showMode === UI.SplitWidget.ShowMode.OnlyMain || this._showMode === UI.SplitWidget.ShowMode.Both) {
Blink Reformat4c46d092018-04-07 15:32:37175 widget.show(this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34176 }
Blink Reformat4c46d092018-04-07 15:32:37177 }
178 this.resumeInvalidations();
179 }
180
181 /**
182 * @param {!UI.Widget} widget
183 */
184 setSidebarWidget(widget) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34185 if (this._sidebarWidget === widget) {
Blink Reformat4c46d092018-04-07 15:32:37186 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34187 }
Blink Reformat4c46d092018-04-07 15:32:37188 this.suspendInvalidations();
Tim van der Lippe1d6e57a2019-09-30 11:55:34189 if (this._sidebarWidget) {
Blink Reformat4c46d092018-04-07 15:32:37190 this._sidebarWidget.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34191 }
Blink Reformat4c46d092018-04-07 15:32:37192 this._sidebarWidget = widget;
193 if (widget) {
Joel Einbinder7fbe24c2019-01-24 05:19:01194 widget.element.slot = 'insertion-point-sidebar';
Tim van der Lippe1d6e57a2019-09-30 11:55:34195 if (this._showMode === UI.SplitWidget.ShowMode.OnlySidebar || this._showMode === UI.SplitWidget.ShowMode.Both) {
Blink Reformat4c46d092018-04-07 15:32:37196 widget.show(this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34197 }
Blink Reformat4c46d092018-04-07 15:32:37198 }
199 this.resumeInvalidations();
200 }
201
202 /**
203 * @return {?UI.Widget}
204 */
205 mainWidget() {
206 return this._mainWidget;
207 }
208
209 /**
210 * @return {?UI.Widget}
211 */
212 sidebarWidget() {
213 return this._sidebarWidget;
214 }
215
216 /**
217 * @override
218 * @param {!UI.Widget} widget
219 */
220 childWasDetached(widget) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34221 if (this._detaching) {
Blink Reformat4c46d092018-04-07 15:32:37222 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34223 }
224 if (this._mainWidget === widget) {
Blink Reformat4c46d092018-04-07 15:32:37225 this._mainWidget = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34226 }
227 if (this._sidebarWidget === widget) {
Blink Reformat4c46d092018-04-07 15:32:37228 this._sidebarWidget = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34229 }
Blink Reformat4c46d092018-04-07 15:32:37230 this.invalidateConstraints();
231 }
232
233 /**
234 * @return {boolean}
235 */
236 isSidebarSecond() {
237 return this._secondIsSidebar;
238 }
239
240 enableShowModeSaving() {
241 this._shouldSaveShowMode = true;
242 this._restoreAndApplyShowModeFromSettings();
243 }
244
245 /**
246 * @return {string}
247 */
248 showMode() {
249 return this._showMode;
250 }
251
252 /**
253 * @param {boolean} secondIsSidebar
254 */
255 setSecondIsSidebar(secondIsSidebar) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34256 if (secondIsSidebar === this._secondIsSidebar) {
Joel Einbinder27131b22019-03-14 09:29:54257 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34258 }
Blink Reformat4c46d092018-04-07 15:32:37259 this._secondIsSidebar = secondIsSidebar;
Joel Einbinder27131b22019-03-14 09:29:54260 if (!this._mainWidget || !this._mainWidget.shouldHideOnDetach()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34261 if (secondIsSidebar) {
Joel Einbinder27131b22019-03-14 09:29:54262 this.contentElement.insertBefore(this._mainElement, this._sidebarElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34263 } else {
Joel Einbinder27131b22019-03-14 09:29:54264 this.contentElement.insertBefore(this._mainElement, this._resizerElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34265 }
Joel Einbinder27131b22019-03-14 09:29:54266 } else if (!this._sidebarWidget || !this._sidebarWidget.shouldHideOnDetach()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34267 if (secondIsSidebar) {
Joel Einbinder27131b22019-03-14 09:29:54268 this.contentElement.insertBefore(this._sidebarElement, this._resizerElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34269 } else {
Joel Einbinder27131b22019-03-14 09:29:54270 this.contentElement.insertBefore(this._sidebarElement, this._mainElement);
Tim van der Lippe1d6e57a2019-09-30 11:55:34271 }
Joel Einbinder27131b22019-03-14 09:29:54272 } else {
273 console.error('Could not swap split widget side. Both children widgets contain iframes.');
274 this._secondIsSidebar = !secondIsSidebar;
275 }
Blink Reformat4c46d092018-04-07 15:32:37276 }
277
278 /**
279 * @return {?string}
280 */
281 sidebarSide() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34282 if (this._showMode !== UI.SplitWidget.ShowMode.Both) {
Blink Reformat4c46d092018-04-07 15:32:37283 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34284 }
Blink Reformat4c46d092018-04-07 15:32:37285 return this._isVertical ? (this._secondIsSidebar ? 'right' : 'left') : (this._secondIsSidebar ? 'bottom' : 'top');
286 }
287
288 /**
289 * @return {!Element}
290 */
291 resizerElement() {
292 return this._resizerElement;
293 }
294
295 /**
296 * @param {boolean=} animate
297 */
298 hideMain(animate) {
299 this._showOnly(this._sidebarWidget, this._mainWidget, this._sidebarElement, this._mainElement, animate);
300 this._updateShowMode(UI.SplitWidget.ShowMode.OnlySidebar);
301 }
302
303 /**
304 * @param {boolean=} animate
305 */
306 hideSidebar(animate) {
307 this._showOnly(this._mainWidget, this._sidebarWidget, this._mainElement, this._sidebarElement, animate);
308 this._updateShowMode(UI.SplitWidget.ShowMode.OnlyMain);
309 }
310
311 /**
312 * @param {boolean} minimized
313 */
314 setSidebarMinimized(minimized) {
315 this._sidebarMinimized = minimized;
316 this.invalidateConstraints();
317 }
318
319 /**
320 * @return {boolean}
321 */
322 isSidebarMinimized() {
323 return this._sidebarMinimized;
324 }
325
326 /**
327 * @param {?UI.Widget} sideToShow
328 * @param {?UI.Widget} sideToHide
329 * @param {!Element} shadowToShow
330 * @param {!Element} shadowToHide
331 * @param {boolean=} animate
332 */
333 _showOnly(sideToShow, sideToHide, shadowToShow, shadowToHide, animate) {
334 this._cancelAnimation();
335
336 /**
337 * @this {UI.SplitWidget}
338 */
339 function callback() {
340 if (sideToShow) {
341 // Make sure main is first in the children list.
Tim van der Lippe1d6e57a2019-09-30 11:55:34342 if (sideToShow === this._mainWidget) {
Blink Reformat4c46d092018-04-07 15:32:37343 this._mainWidget.show(this.element, this._sidebarWidget ? this._sidebarWidget.element : null);
Tim van der Lippe1d6e57a2019-09-30 11:55:34344 } else {
Blink Reformat4c46d092018-04-07 15:32:37345 this._sidebarWidget.show(this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34346 }
Blink Reformat4c46d092018-04-07 15:32:37347 }
348 if (sideToHide) {
349 this._detaching = true;
350 sideToHide.detach();
351 this._detaching = false;
352 }
353
354 this._resizerElement.classList.add('hidden');
355 shadowToShow.classList.remove('hidden');
356 shadowToShow.classList.add('maximized');
357 shadowToHide.classList.add('hidden');
358 shadowToHide.classList.remove('maximized');
359 this._removeAllLayoutProperties();
360 this.doResize();
361 this._showFinishedForTest();
362 }
363
Tim van der Lippe1d6e57a2019-09-30 11:55:34364 if (animate) {
Blink Reformat4c46d092018-04-07 15:32:37365 this._animate(true, callback.bind(this));
Tim van der Lippe1d6e57a2019-09-30 11:55:34366 } else {
Blink Reformat4c46d092018-04-07 15:32:37367 callback.call(this);
Tim van der Lippe1d6e57a2019-09-30 11:55:34368 }
Blink Reformat4c46d092018-04-07 15:32:37369
370 this._sidebarSizeDIP = -1;
371 this.setResizable(false);
372 }
373
374 _showFinishedForTest() {
375 // This method is sniffed in tests.
376 }
377
378 _removeAllLayoutProperties() {
379 this._sidebarElement.style.removeProperty('flexBasis');
380
381 this._mainElement.style.removeProperty('width');
382 this._mainElement.style.removeProperty('height');
383 this._sidebarElement.style.removeProperty('width');
384 this._sidebarElement.style.removeProperty('height');
385
386 this._resizerElement.style.removeProperty('left');
387 this._resizerElement.style.removeProperty('right');
388 this._resizerElement.style.removeProperty('top');
389 this._resizerElement.style.removeProperty('bottom');
390
391 this._resizerElement.style.removeProperty('margin-left');
392 this._resizerElement.style.removeProperty('margin-right');
393 this._resizerElement.style.removeProperty('margin-top');
394 this._resizerElement.style.removeProperty('margin-bottom');
395 }
396
397 /**
398 * @param {boolean=} animate
399 */
400 showBoth(animate) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34401 if (this._showMode === UI.SplitWidget.ShowMode.Both) {
Blink Reformat4c46d092018-04-07 15:32:37402 animate = false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34403 }
Blink Reformat4c46d092018-04-07 15:32:37404
405 this._cancelAnimation();
406 this._mainElement.classList.remove('maximized', 'hidden');
407 this._sidebarElement.classList.remove('maximized', 'hidden');
408 this._resizerElement.classList.remove('hidden');
409 this.setResizable(true);
410
411 // Make sure main is the first in the children list.
412 this.suspendInvalidations();
Tim van der Lippe1d6e57a2019-09-30 11:55:34413 if (this._sidebarWidget) {
Blink Reformat4c46d092018-04-07 15:32:37414 this._sidebarWidget.show(this.element);
Tim van der Lippe1d6e57a2019-09-30 11:55:34415 }
416 if (this._mainWidget) {
Blink Reformat4c46d092018-04-07 15:32:37417 this._mainWidget.show(this.element, this._sidebarWidget ? this._sidebarWidget.element : null);
Tim van der Lippe1d6e57a2019-09-30 11:55:34418 }
Blink Reformat4c46d092018-04-07 15:32:37419 this.resumeInvalidations();
420 // Order widgets in DOM properly.
421 this.setSecondIsSidebar(this._secondIsSidebar);
422
423 this._sidebarSizeDIP = -1;
424 this._updateShowMode(UI.SplitWidget.ShowMode.Both);
425 this._updateLayout(animate);
426 }
427
428 /**
429 * @param {boolean} resizable
430 */
431 setResizable(resizable) {
432 this._resizerWidget.setEnabled(resizable);
433 }
434
435 /**
436 * @return {boolean}
437 */
438 isResizable() {
439 return this._resizerWidget.isEnabled();
440 }
441
442 /**
443 * @param {number} size
444 */
445 setSidebarSize(size) {
446 const sizeDIP = UI.zoomManager.cssToDIP(size);
447 this._savedSidebarSizeDIP = sizeDIP;
448 this._saveSetting();
449 this._innerSetSidebarSizeDIP(sizeDIP, false, true);
450 }
451
452 /**
453 * @return {number}
454 */
455 sidebarSize() {
456 const sizeDIP = Math.max(0, this._sidebarSizeDIP);
457 return UI.zoomManager.dipToCSS(sizeDIP);
458 }
459
460 /**
461 * Returns total size in DIP.
462 * @return {number}
463 */
464 _totalSizeDIP() {
465 if (!this._totalSizeCSS) {
466 this._totalSizeCSS = this._isVertical ? this.contentElement.offsetWidth : this.contentElement.offsetHeight;
467 this._totalSizeOtherDimensionCSS =
468 this._isVertical ? this.contentElement.offsetHeight : this.contentElement.offsetWidth;
469 }
470 return UI.zoomManager.cssToDIP(this._totalSizeCSS);
471 }
472
473 /**
474 * @param {string} showMode
475 */
476 _updateShowMode(showMode) {
477 this._showMode = showMode;
478 this._saveShowModeToSettings();
479 this._updateShowHideSidebarButton();
480 this.dispatchEventToListeners(UI.SplitWidget.Events.ShowModeChanged, showMode);
481 this.invalidateConstraints();
482 }
483
484 /**
485 * @param {number} sizeDIP
486 * @param {boolean} animate
487 * @param {boolean=} userAction
488 */
489 _innerSetSidebarSizeDIP(sizeDIP, animate, userAction) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34490 if (this._showMode !== UI.SplitWidget.ShowMode.Both || !this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37491 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34492 }
Blink Reformat4c46d092018-04-07 15:32:37493
494 sizeDIP = this._applyConstraints(sizeDIP, userAction);
Tim van der Lippe1d6e57a2019-09-30 11:55:34495 if (this._sidebarSizeDIP === sizeDIP) {
Blink Reformat4c46d092018-04-07 15:32:37496 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34497 }
Blink Reformat4c46d092018-04-07 15:32:37498
499 if (!this._resizerElementSize) {
500 this._resizerElementSize =
501 this._isVertical ? this._resizerElement.offsetWidth : this._resizerElement.offsetHeight;
502 }
503
504 // Invalidate layout below.
505
506 this._removeAllLayoutProperties();
507
508 // this._totalSizeDIP is available below since we successfully applied constraints.
509 const roundSizeCSS = Math.round(UI.zoomManager.dipToCSS(sizeDIP));
510 const sidebarSizeValue = roundSizeCSS + 'px';
511 const mainSizeValue = (this._totalSizeCSS - roundSizeCSS) + 'px';
512 this._sidebarElement.style.flexBasis = sidebarSizeValue;
513
514 // Make both sides relayout boundaries.
515 if (this._isVertical) {
516 this._sidebarElement.style.width = sidebarSizeValue;
517 this._mainElement.style.width = mainSizeValue;
518 this._sidebarElement.style.height = this._totalSizeOtherDimensionCSS + 'px';
519 this._mainElement.style.height = this._totalSizeOtherDimensionCSS + 'px';
520 } else {
521 this._sidebarElement.style.height = sidebarSizeValue;
522 this._mainElement.style.height = mainSizeValue;
523 this._sidebarElement.style.width = this._totalSizeOtherDimensionCSS + 'px';
524 this._mainElement.style.width = this._totalSizeOtherDimensionCSS + 'px';
525 }
526
527 // Position resizer.
528 if (this._isVertical) {
529 if (this._secondIsSidebar) {
530 this._resizerElement.style.right = sidebarSizeValue;
531 this._resizerElement.style.marginRight = -this._resizerElementSize / 2 + 'px';
532 } else {
533 this._resizerElement.style.left = sidebarSizeValue;
534 this._resizerElement.style.marginLeft = -this._resizerElementSize / 2 + 'px';
535 }
536 } else {
537 if (this._secondIsSidebar) {
538 this._resizerElement.style.bottom = sidebarSizeValue;
539 this._resizerElement.style.marginBottom = -this._resizerElementSize / 2 + 'px';
540 } else {
541 this._resizerElement.style.top = sidebarSizeValue;
542 this._resizerElement.style.marginTop = -this._resizerElementSize / 2 + 'px';
543 }
544 }
545
546 this._sidebarSizeDIP = sizeDIP;
547
548 // Force layout.
549
550 if (animate) {
551 this._animate(false);
552 } else {
553 // No need to recalculate this._sidebarSizeDIP and this._totalSizeDIP again.
554 this.doResize();
555 this.dispatchEventToListeners(UI.SplitWidget.Events.SidebarSizeChanged, this.sidebarSize());
556 }
557 }
558
559 /**
560 * @param {boolean} reverse
561 * @param {function()=} callback
562 */
563 _animate(reverse, callback) {
564 const animationTime = 50;
565 this._animationCallback = callback || null;
566
567 let animatedMarginPropertyName;
Tim van der Lippe1d6e57a2019-09-30 11:55:34568 if (this._isVertical) {
Blink Reformat4c46d092018-04-07 15:32:37569 animatedMarginPropertyName = this._secondIsSidebar ? 'margin-right' : 'margin-left';
Tim van der Lippe1d6e57a2019-09-30 11:55:34570 } else {
Blink Reformat4c46d092018-04-07 15:32:37571 animatedMarginPropertyName = this._secondIsSidebar ? 'margin-bottom' : 'margin-top';
Tim van der Lippe1d6e57a2019-09-30 11:55:34572 }
Blink Reformat4c46d092018-04-07 15:32:37573
574 const marginFrom = reverse ? '0' : '-' + UI.zoomManager.dipToCSS(this._sidebarSizeDIP) + 'px';
575 const marginTo = reverse ? '-' + UI.zoomManager.dipToCSS(this._sidebarSizeDIP) + 'px' : '0';
576
577 // This order of things is important.
578 // 1. Resize main element early and force layout.
579 this.contentElement.style.setProperty(animatedMarginPropertyName, marginFrom);
580 if (!reverse) {
581 suppressUnused(this._mainElement.offsetWidth);
582 suppressUnused(this._sidebarElement.offsetWidth);
583 }
584
585 // 2. Issue onresize to the sidebar element, its size won't change.
Tim van der Lippe1d6e57a2019-09-30 11:55:34586 if (!reverse) {
Blink Reformat4c46d092018-04-07 15:32:37587 this._sidebarWidget.doResize();
Tim van der Lippe1d6e57a2019-09-30 11:55:34588 }
Blink Reformat4c46d092018-04-07 15:32:37589
590 // 3. Configure and run animation
591 this.contentElement.style.setProperty('transition', animatedMarginPropertyName + ' ' + animationTime + 'ms linear');
592
593 const boundAnimationFrame = animationFrame.bind(this);
594 let startTime;
595 /**
596 * @this {UI.SplitWidget}
597 */
598 function animationFrame() {
599 this._animationFrameHandle = 0;
600
601 if (!startTime) {
602 // Kick animation on first frame.
603 this.contentElement.style.setProperty(animatedMarginPropertyName, marginTo);
604 startTime = window.performance.now();
605 } else if (window.performance.now() < startTime + animationTime) {
606 // Process regular animation frame.
Tim van der Lippe1d6e57a2019-09-30 11:55:34607 if (this._mainWidget) {
Blink Reformat4c46d092018-04-07 15:32:37608 this._mainWidget.doResize();
Tim van der Lippe1d6e57a2019-09-30 11:55:34609 }
Blink Reformat4c46d092018-04-07 15:32:37610 } else {
611 // Complete animation.
612 this._cancelAnimation();
Tim van der Lippe1d6e57a2019-09-30 11:55:34613 if (this._mainWidget) {
Blink Reformat4c46d092018-04-07 15:32:37614 this._mainWidget.doResize();
Tim van der Lippe1d6e57a2019-09-30 11:55:34615 }
Blink Reformat4c46d092018-04-07 15:32:37616 this.dispatchEventToListeners(UI.SplitWidget.Events.SidebarSizeChanged, this.sidebarSize());
617 return;
618 }
619 this._animationFrameHandle = this.contentElement.window().requestAnimationFrame(boundAnimationFrame);
620 }
621 this._animationFrameHandle = this.contentElement.window().requestAnimationFrame(boundAnimationFrame);
622 }
623
624 _cancelAnimation() {
625 this.contentElement.style.removeProperty('margin-top');
626 this.contentElement.style.removeProperty('margin-right');
627 this.contentElement.style.removeProperty('margin-bottom');
628 this.contentElement.style.removeProperty('margin-left');
629 this.contentElement.style.removeProperty('transition');
630
631 if (this._animationFrameHandle) {
632 this.contentElement.window().cancelAnimationFrame(this._animationFrameHandle);
633 this._animationFrameHandle = 0;
634 }
635 if (this._animationCallback) {
636 this._animationCallback();
637 this._animationCallback = null;
638 }
639 }
640
641 /**
642 * @param {number} sidebarSize
643 * @param {boolean=} userAction
644 * @return {number}
645 */
646 _applyConstraints(sidebarSize, userAction) {
647 const totalSize = this._totalSizeDIP();
648 const zoomFactor = this._constraintsInDip ? 1 : UI.zoomManager.zoomFactor();
649
650 let constraints = this._sidebarWidget ? this._sidebarWidget.constraints() : new UI.Constraints();
651 let minSidebarSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
Tim van der Lippe1d6e57a2019-09-30 11:55:34652 if (!minSidebarSize) {
Blink Reformat4c46d092018-04-07 15:32:37653 minSidebarSize = UI.SplitWidget.MinPadding;
Tim van der Lippe1d6e57a2019-09-30 11:55:34654 }
Blink Reformat4c46d092018-04-07 15:32:37655 minSidebarSize *= zoomFactor;
Tim van der Lippe1d6e57a2019-09-30 11:55:34656 if (this._sidebarMinimized) {
Blink Reformat4c46d092018-04-07 15:32:37657 sidebarSize = minSidebarSize;
Tim van der Lippe1d6e57a2019-09-30 11:55:34658 }
Blink Reformat4c46d092018-04-07 15:32:37659
660 let preferredSidebarSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
Tim van der Lippe1d6e57a2019-09-30 11:55:34661 if (!preferredSidebarSize) {
Blink Reformat4c46d092018-04-07 15:32:37662 preferredSidebarSize = UI.SplitWidget.MinPadding;
Tim van der Lippe1d6e57a2019-09-30 11:55:34663 }
Blink Reformat4c46d092018-04-07 15:32:37664 preferredSidebarSize *= zoomFactor;
665 // Allow sidebar to be less than preferred by explicit user action.
Tim van der Lippe1d6e57a2019-09-30 11:55:34666 if (sidebarSize < preferredSidebarSize) {
Blink Reformat4c46d092018-04-07 15:32:37667 preferredSidebarSize = Math.max(sidebarSize, minSidebarSize);
Tim van der Lippe1d6e57a2019-09-30 11:55:34668 }
Blink Reformat4c46d092018-04-07 15:32:37669 preferredSidebarSize += zoomFactor; // 1 css pixel for splitter border.
670
671 constraints = this._mainWidget ? this._mainWidget.constraints() : new UI.Constraints();
672 let minMainSize = this.isVertical() ? constraints.minimum.width : constraints.minimum.height;
Tim van der Lippe1d6e57a2019-09-30 11:55:34673 if (!minMainSize) {
Blink Reformat4c46d092018-04-07 15:32:37674 minMainSize = UI.SplitWidget.MinPadding;
Tim van der Lippe1d6e57a2019-09-30 11:55:34675 }
Blink Reformat4c46d092018-04-07 15:32:37676 minMainSize *= zoomFactor;
677
678 let preferredMainSize = this.isVertical() ? constraints.preferred.width : constraints.preferred.height;
Tim van der Lippe1d6e57a2019-09-30 11:55:34679 if (!preferredMainSize) {
Blink Reformat4c46d092018-04-07 15:32:37680 preferredMainSize = UI.SplitWidget.MinPadding;
Tim van der Lippe1d6e57a2019-09-30 11:55:34681 }
Blink Reformat4c46d092018-04-07 15:32:37682 preferredMainSize *= zoomFactor;
683 const savedMainSize = this.isVertical() ? this._savedVerticalMainSize : this._savedHorizontalMainSize;
Tim van der Lippe1d6e57a2019-09-30 11:55:34684 if (savedMainSize !== null) {
Blink Reformat4c46d092018-04-07 15:32:37685 preferredMainSize = Math.min(preferredMainSize, savedMainSize * zoomFactor);
Tim van der Lippe1d6e57a2019-09-30 11:55:34686 }
687 if (userAction) {
Blink Reformat4c46d092018-04-07 15:32:37688 preferredMainSize = minMainSize;
Tim van der Lippe1d6e57a2019-09-30 11:55:34689 }
Blink Reformat4c46d092018-04-07 15:32:37690
691 // Enough space for preferred.
692 const totalPreferred = preferredMainSize + preferredSidebarSize;
Tim van der Lippe1d6e57a2019-09-30 11:55:34693 if (totalPreferred <= totalSize) {
Blink Reformat4c46d092018-04-07 15:32:37694 return Number.constrain(sidebarSize, preferredSidebarSize, totalSize - preferredMainSize);
Tim van der Lippe1d6e57a2019-09-30 11:55:34695 }
Blink Reformat4c46d092018-04-07 15:32:37696
697 // Enough space for minimum.
698 if (minMainSize + minSidebarSize <= totalSize) {
699 const delta = totalPreferred - totalSize;
700 const sidebarDelta = delta * preferredSidebarSize / totalPreferred;
701 sidebarSize = preferredSidebarSize - sidebarDelta;
702 return Number.constrain(sidebarSize, minSidebarSize, totalSize - minMainSize);
703 }
704
705 // Not enough space even for minimum sizes.
706 return Math.max(0, totalSize - minMainSize);
707 }
708
709 /**
710 * @override
711 */
712 wasShown() {
713 this._forceUpdateLayout();
714 UI.zoomManager.addEventListener(UI.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
715 }
716
717 /**
718 * @override
719 */
720 willHide() {
721 UI.zoomManager.removeEventListener(UI.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this);
722 }
723
724 /**
725 * @override
726 */
727 onResize() {
728 this._updateLayout();
729 }
730
731 /**
732 * @override
733 */
734 onLayout() {
735 this._updateLayout();
736 }
737
738 /**
739 * @override
740 * @return {!UI.Constraints}
741 */
742 calculateConstraints() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34743 if (this._showMode === UI.SplitWidget.ShowMode.OnlyMain) {
Blink Reformat4c46d092018-04-07 15:32:37744 return this._mainWidget ? this._mainWidget.constraints() : new UI.Constraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34745 }
746 if (this._showMode === UI.SplitWidget.ShowMode.OnlySidebar) {
Blink Reformat4c46d092018-04-07 15:32:37747 return this._sidebarWidget ? this._sidebarWidget.constraints() : new UI.Constraints();
Tim van der Lippe1d6e57a2019-09-30 11:55:34748 }
Blink Reformat4c46d092018-04-07 15:32:37749
750 let mainConstraints = this._mainWidget ? this._mainWidget.constraints() : new UI.Constraints();
751 let sidebarConstraints = this._sidebarWidget ? this._sidebarWidget.constraints() : new UI.Constraints();
752 const min = UI.SplitWidget.MinPadding;
753 if (this._isVertical) {
754 mainConstraints = mainConstraints.widthToMax(min).addWidth(1); // 1 for splitter
755 sidebarConstraints = sidebarConstraints.widthToMax(min);
756 return mainConstraints.addWidth(sidebarConstraints).heightToMax(sidebarConstraints);
757 } else {
758 mainConstraints = mainConstraints.heightToMax(min).addHeight(1); // 1 for splitter
759 sidebarConstraints = sidebarConstraints.heightToMax(min);
760 return mainConstraints.widthToMax(sidebarConstraints).addHeight(sidebarConstraints);
761 }
762 }
763
764 /**
765 * @param {!Common.Event} event
766 */
767 _onResizeStart(event) {
768 this._resizeStartSizeDIP = this._sidebarSizeDIP;
769 }
770
771 /**
772 * @param {!Common.Event} event
773 */
774 _onResizeUpdate(event) {
775 const offset = event.data.currentPosition - event.data.startPosition;
776 const offsetDIP = UI.zoomManager.cssToDIP(offset);
777 const newSizeDIP =
778 this._secondIsSidebar ? this._resizeStartSizeDIP - offsetDIP : this._resizeStartSizeDIP + offsetDIP;
779 const constrainedSizeDIP = this._applyConstraints(newSizeDIP, true);
780 this._savedSidebarSizeDIP = constrainedSizeDIP;
781 this._saveSetting();
782 this._innerSetSidebarSizeDIP(constrainedSizeDIP, false, true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34783 if (this.isVertical()) {
Blink Reformat4c46d092018-04-07 15:32:37784 this._savedVerticalMainSize = this._totalSizeDIP() - this._sidebarSizeDIP;
Tim van der Lippe1d6e57a2019-09-30 11:55:34785 } else {
Blink Reformat4c46d092018-04-07 15:32:37786 this._savedHorizontalMainSize = this._totalSizeDIP() - this._sidebarSizeDIP;
Tim van der Lippe1d6e57a2019-09-30 11:55:34787 }
Blink Reformat4c46d092018-04-07 15:32:37788 }
789
790 /**
791 * @param {!Common.Event} event
792 */
793 _onResizeEnd(event) {
794 this._resizeStartSizeDIP = 0;
795 }
796
797 /**
798 * @param {boolean=} noSplitter
799 */
800 hideDefaultResizer(noSplitter) {
801 this.uninstallResizer(this._resizerElement);
Pavel Feldmanc9060ea2018-04-30 04:42:18802 this._sidebarElement.classList.toggle('no-default-splitter', !!noSplitter);
Blink Reformat4c46d092018-04-07 15:32:37803 }
804
805 /**
806 * @param {!Element} resizerElement
807 */
808 installResizer(resizerElement) {
809 this._resizerWidget.addElement(resizerElement);
810 }
811
812 /**
813 * @param {!Element} resizerElement
814 */
815 uninstallResizer(resizerElement) {
816 this._resizerWidget.removeElement(resizerElement);
817 }
818
819 /**
820 * @return {boolean}
821 */
822 hasCustomResizer() {
823 const elements = this._resizerWidget.elements();
824 return elements.length > 1 || (elements.length === 1 && elements[0] !== this._resizerElement);
825 }
826
827 /**
828 * @param {!Element} resizer
829 * @param {boolean} on
830 */
831 toggleResizer(resizer, on) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34832 if (on) {
Blink Reformat4c46d092018-04-07 15:32:37833 this.installResizer(resizer);
Tim van der Lippe1d6e57a2019-09-30 11:55:34834 } else {
Blink Reformat4c46d092018-04-07 15:32:37835 this.uninstallResizer(resizer);
Tim van der Lippe1d6e57a2019-09-30 11:55:34836 }
Blink Reformat4c46d092018-04-07 15:32:37837 }
838
839 /**
840 * @return {?UI.SplitWidget.SettingForOrientation}
841 */
842 _settingForOrientation() {
843 const state = this._setting ? this._setting.get() : {};
844 return this._isVertical ? state.vertical : state.horizontal;
845 }
846
847 /**
848 * @return {number}
849 */
850 _preferredSidebarSizeDIP() {
851 let size = this._savedSidebarSizeDIP;
852 if (!size) {
853 size = this._isVertical ? this._defaultSidebarWidth : this._defaultSidebarHeight;
854 // If we have default value in percents, calculate it on first use.
Tim van der Lippe1d6e57a2019-09-30 11:55:34855 if (0 < size && size < 1) {
Blink Reformat4c46d092018-04-07 15:32:37856 size *= this._totalSizeDIP();
Tim van der Lippe1d6e57a2019-09-30 11:55:34857 }
Blink Reformat4c46d092018-04-07 15:32:37858 }
859 return size;
860 }
861
862 _restoreSidebarSizeFromSettings() {
863 const settingForOrientation = this._settingForOrientation();
864 this._savedSidebarSizeDIP = settingForOrientation ? settingForOrientation.size : 0;
865 }
866
867 _restoreAndApplyShowModeFromSettings() {
868 const orientationState = this._settingForOrientation();
869 this._savedShowMode = orientationState && orientationState.showMode ? orientationState.showMode : this._showMode;
870 this._showMode = this._savedShowMode;
871
872 switch (this._savedShowMode) {
873 case UI.SplitWidget.ShowMode.Both:
874 this.showBoth();
875 break;
876 case UI.SplitWidget.ShowMode.OnlyMain:
877 this.hideSidebar();
878 break;
879 case UI.SplitWidget.ShowMode.OnlySidebar:
880 this.hideMain();
881 break;
882 }
883 }
884
885 _saveShowModeToSettings() {
886 this._savedShowMode = this._showMode;
887 this._saveSetting();
888 }
889
890 _saveSetting() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34891 if (!this._setting) {
Blink Reformat4c46d092018-04-07 15:32:37892 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34893 }
Blink Reformat4c46d092018-04-07 15:32:37894 const state = this._setting.get();
895 const orientationState = (this._isVertical ? state.vertical : state.horizontal) || {};
896
897 orientationState.size = this._savedSidebarSizeDIP;
Tim van der Lippe1d6e57a2019-09-30 11:55:34898 if (this._shouldSaveShowMode) {
Blink Reformat4c46d092018-04-07 15:32:37899 orientationState.showMode = this._savedShowMode;
Tim van der Lippe1d6e57a2019-09-30 11:55:34900 }
Blink Reformat4c46d092018-04-07 15:32:37901
Tim van der Lippe1d6e57a2019-09-30 11:55:34902 if (this._isVertical) {
Blink Reformat4c46d092018-04-07 15:32:37903 state.vertical = orientationState;
Tim van der Lippe1d6e57a2019-09-30 11:55:34904 } else {
Blink Reformat4c46d092018-04-07 15:32:37905 state.horizontal = orientationState;
Tim van der Lippe1d6e57a2019-09-30 11:55:34906 }
Blink Reformat4c46d092018-04-07 15:32:37907 this._setting.set(state);
908 }
909
910 _forceUpdateLayout() {
911 // Force layout even if sidebar size does not change.
912 this._sidebarSizeDIP = -1;
913 this._updateLayout();
914 }
915
916 /**
917 * @param {!Common.Event} event
918 */
919 _onZoomChanged(event) {
920 this._forceUpdateLayout();
921 }
922
923 /**
924 * @param {string} title
925 * @return {!UI.ToolbarButton}
926 */
927 createShowHideSidebarButton(title) {
Mandy Chen84d56b62019-09-03 18:21:18928 this._showHideSidebarButtonTitle = title;
Blink Reformat4c46d092018-04-07 15:32:37929 this._showHideSidebarButton = new UI.ToolbarButton('', '');
930 this._showHideSidebarButton.addEventListener(UI.ToolbarButton.Events.Click, buttonClicked, this);
931 this._updateShowHideSidebarButton();
932
933 /**
934 * @param {!Common.Event} event
935 * @this {UI.SplitWidget}
936 */
937 function buttonClicked(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34938 if (this._showMode !== UI.SplitWidget.ShowMode.Both) {
Blink Reformat4c46d092018-04-07 15:32:37939 this.showBoth(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34940 } else {
Blink Reformat4c46d092018-04-07 15:32:37941 this.hideSidebar(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34942 }
Blink Reformat4c46d092018-04-07 15:32:37943 }
944
945 return this._showHideSidebarButton;
946 }
947
948 _updateShowHideSidebarButton() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34949 if (!this._showHideSidebarButton) {
Blink Reformat4c46d092018-04-07 15:32:37950 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34951 }
Blink Reformat4c46d092018-04-07 15:32:37952 const sidebarHidden = this._showMode === UI.SplitWidget.ShowMode.OnlyMain;
953 let glyph = '';
954 if (sidebarHidden) {
955 glyph = this.isVertical() ?
956 (this.isSidebarSecond() ? 'largeicon-show-right-sidebar' : 'largeicon-show-left-sidebar') :
957 (this.isSidebarSecond() ? 'largeicon-show-bottom-sidebar' : 'largeicon-show-top-sidebar');
958 } else {
959 glyph = this.isVertical() ?
960 (this.isSidebarSecond() ? 'largeicon-hide-right-sidebar' : 'largeicon-hide-left-sidebar') :
961 (this.isSidebarSecond() ? 'largeicon-hide-bottom-sidebar' : 'largeicon-hide-top-sidebar');
962 }
963 this._showHideSidebarButton.setGlyph(glyph);
964 this._showHideSidebarButton.setTitle(
965 sidebarHidden ? Common.UIString('Show %s', this._showHideSidebarButtonTitle) :
966 Common.UIString('Hide %s', this._showHideSidebarButtonTitle));
967 }
968};
969
970/** @typedef {{showMode: string, size: number}} */
971UI.SplitWidget.SettingForOrientation;
972
973UI.SplitWidget.ShowMode = {
974 Both: 'Both',
975 OnlyMain: 'OnlyMain',
976 OnlySidebar: 'OnlySidebar'
977};
978
979/** @enum {symbol} */
980UI.SplitWidget.Events = {
981 SidebarSizeChanged: Symbol('SidebarSizeChanged'),
982 ShowModeChanged: Symbol('ShowModeChanged')
983};
984
985UI.SplitWidget.MinPadding = 20;