Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer 11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the 12 * in the documentation and/or other materials provided with the
13 * distribution. 13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its 14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from 15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission. 16 * this software without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30
31 /** 30 /**
32 * @extends {WebInspector.VBox} 31 * @unrestricted
33 * @constructor
34 */ 32 */
35 WebInspector.TabbedPane = function() 33 WebInspector.TabbedPane = class extends WebInspector.VBox {
36 { 34 constructor() {
37 WebInspector.VBox.call(this, true); 35 super(true);
38 this.registerRequiredCSS("ui/tabbedPane.css"); 36 this.registerRequiredCSS('ui/tabbedPane.css');
39 this.element.classList.add("tabbed-pane"); 37 this.element.classList.add('tabbed-pane');
40 this.contentElement.classList.add("tabbed-pane-shadow"); 38 this.contentElement.classList.add('tabbed-pane-shadow');
41 this.contentElement.tabIndex = -1; 39 this.contentElement.tabIndex = -1;
42 this._headerElement = this.contentElement.createChild("div", "tabbed-pane-he ader"); 40 this._headerElement = this.contentElement.createChild('div', 'tabbed-pane-he ader');
43 this._headerContentsElement = this._headerElement.createChild("div", "tabbed -pane-header-contents"); 41 this._headerContentsElement = this._headerElement.createChild('div', 'tabbed -pane-header-contents');
44 this._headerContentsElement.setAttribute("aria-label", WebInspector.UIString ("Panels")); 42 this._headerContentsElement.setAttribute('aria-label', WebInspector.UIString ('Panels'));
45 this._tabSlider = createElementWithClass("div", "tabbed-pane-tab-slider"); 43 this._tabSlider = createElementWithClass('div', 'tabbed-pane-tab-slider');
46 this._tabsElement = this._headerContentsElement.createChild("div", "tabbed-p ane-header-tabs"); 44 this._tabsElement = this._headerContentsElement.createChild('div', 'tabbed-p ane-header-tabs');
47 this._tabsElement.setAttribute("role", "tablist"); 45 this._tabsElement.setAttribute('role', 'tablist');
48 this._contentElement = this.contentElement.createChild("div", "tabbed-pane-c ontent"); 46 this._contentElement = this.contentElement.createChild('div', 'tabbed-pane-c ontent');
49 this._contentElement.setAttribute("role", "tabpanel"); 47 this._contentElement.setAttribute('role', 'tabpanel');
50 this._contentElement.createChild("content"); 48 this._contentElement.createChild('content');
51 /** @type {!Array.<!WebInspector.TabbedPaneTab>} */ 49 /** @type {!Array.<!WebInspector.TabbedPaneTab>} */
52 this._tabs = []; 50 this._tabs = [];
53 /** @type {!Array.<!WebInspector.TabbedPaneTab>} */ 51 /** @type {!Array.<!WebInspector.TabbedPaneTab>} */
54 this._tabsHistory = []; 52 this._tabsHistory = [];
55 /** @type {!Object.<string, !WebInspector.TabbedPaneTab>} */ 53 /** @type {!Object.<string, !WebInspector.TabbedPaneTab>} */
56 this._tabsById = {}; 54 this._tabsById = {};
57 this._currentTabLocked = false; 55 this._currentTabLocked = false;
58 this._autoSelectFirstItemOnShow = true; 56 this._autoSelectFirstItemOnShow = true;
59 57
60 this._dropDownButton = this._createDropDownButton(); 58 this._dropDownButton = this._createDropDownButton();
61 WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.Zo omChanged, this._zoomChanged, this); 59 WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.Zo omChanged, this._zoomChanged, this);
60 }
61
62 /**
63 * @param {boolean} locked
64 */
65 setCurrentTabLocked(locked) {
66 this._currentTabLocked = locked;
67 this._headerElement.classList.toggle('locked', this._currentTabLocked);
68 }
69
70 /**
71 * @param {boolean} autoSelect
72 */
73 setAutoSelectFirstItemOnShow(autoSelect) {
74 this._autoSelectFirstItemOnShow = autoSelect;
75 }
76
77 /**
78 * @return {?WebInspector.Widget}
79 */
80 get visibleView() {
81 return this._currentTab ? this._currentTab.view : null;
82 }
83
84 /**
85 * @return {!Array.<string>}
86 */
87 tabIds() {
88 return this._tabs.map(tab => tab._id);
89 }
90
91 /**
92 * @param {string} tabId
93 * @return {number}
94 */
95 tabIndex(tabId) {
96 return this._tabs.findIndex(tab => tab.id === tabId);
97 }
98
99 /**
100 * @return {!Array.<!WebInspector.Widget>}
101 */
102 tabViews() {
103 return this._tabs.map(tab => tab.view);
104 }
105
106 /**
107 * @param {string} tabId
108 * @return {?WebInspector.Widget}
109 */
110 tabView(tabId) {
111 return this._tabsById[tabId] ? this._tabsById[tabId].view : null;
112 }
113
114 /**
115 * @return {?string}
116 */
117 get selectedTabId() {
118 return this._currentTab ? this._currentTab.id : null;
119 }
120
121 /**
122 * @param {boolean} shrinkableTabs
123 */
124 setShrinkableTabs(shrinkableTabs) {
125 this._shrinkableTabs = shrinkableTabs;
126 }
127
128 /**
129 * @param {boolean} verticalTabLayout
130 */
131 setVerticalTabLayout(verticalTabLayout) {
132 this._verticalTabLayout = verticalTabLayout;
133 this.contentElement.classList.add('vertical-tab-layout');
134 this.invalidateConstraints();
135 }
136
137 /**
138 * @param {boolean} closeableTabs
139 */
140 setCloseableTabs(closeableTabs) {
141 this._closeableTabs = closeableTabs;
142 }
143
144 /**
145 * @override
146 */
147 focus() {
148 if (this.visibleView)
149 this.visibleView.focus();
150 else
151 this.contentElement.focus();
152 }
153
154 /**
155 * @return {!Element}
156 */
157 headerElement() {
158 return this._headerElement;
159 }
160
161 /**
162 * @param {string} id
163 * @return {boolean}
164 */
165 isTabCloseable(id) {
166 var tab = this._tabsById[id];
167 return tab ? tab.isCloseable() : false;
168 }
169
170 /**
171 * @param {!WebInspector.TabbedPaneTabDelegate} delegate
172 */
173 setTabDelegate(delegate) {
174 var tabs = this._tabs.slice();
175 for (var i = 0; i < tabs.length; ++i)
176 tabs[i].setDelegate(delegate);
177 this._delegate = delegate;
178 }
179
180 /**
181 * @param {string} id
182 * @param {string} tabTitle
183 * @param {!WebInspector.Widget} view
184 * @param {string=} tabTooltip
185 * @param {boolean=} userGesture
186 * @param {boolean=} isCloseable
187 * @param {number=} index
188 */
189 appendTab(id, tabTitle, view, tabTooltip, userGesture, isCloseable, index) {
190 isCloseable = typeof isCloseable === 'boolean' ? isCloseable : this._closeab leTabs;
191 var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, isCloseable, vi ew, tabTooltip);
192 tab.setDelegate(this._delegate);
193 this._tabsById[id] = tab;
194 if (index !== undefined)
195 this._tabs.splice(index, 0, tab);
196 else
197 this._tabs.push(tab);
198 this._tabsHistory.push(tab);
199 view.attach(this);
200 if (this._tabsHistory[0] === tab && this.isShowing())
201 this.selectTab(tab.id, userGesture);
202 this._updateTabElements();
203 }
204
205 /**
206 * @param {string} id
207 * @param {boolean=} userGesture
208 */
209 closeTab(id, userGesture) {
210 this.closeTabs([id], userGesture);
211 }
212
213 /**
214 * @param {!Array.<string>} ids
215 * @param {boolean=} userGesture
216 */
217 closeTabs(ids, userGesture) {
218 var focused = this.hasFocus();
219 for (var i = 0; i < ids.length; ++i)
220 this._innerCloseTab(ids[i], userGesture);
221 this._updateTabElements();
222 if (this._tabsHistory.length)
223 this.selectTab(this._tabsHistory[0].id, false);
224 if (focused)
225 this.focus();
226 }
227
228 /**
229 * @param {string} id
230 * @param {boolean=} userGesture
231 */
232 _innerCloseTab(id, userGesture) {
233 if (!this._tabsById[id])
234 return;
235 if (userGesture && !this._tabsById[id]._closeable)
236 return;
237 if (this._currentTab && this._currentTab.id === id)
238 this._hideCurrentTab();
239
240 var tab = this._tabsById[id];
241 delete this._tabsById[id];
242
243 this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
244 this._tabs.splice(this._tabs.indexOf(tab), 1);
245 if (tab._shown)
246 this._hideTabElement(tab);
247 tab.view.detach();
248
249 var eventData = {tabId: id, view: tab.view, isUserGesture: userGesture};
250 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabClosed, even tData);
251 return true;
252 }
253
254 /**
255 * @param {string} tabId
256 * @return {boolean}
257 */
258 hasTab(tabId) {
259 return !!this._tabsById[tabId];
260 }
261
262 /**
263 * @return {!Array.<string>}
264 */
265 allTabs() {
266 return this._tabs.map(function(tab) {
267 return tab.id;
268 });
269 }
270
271 /**
272 * @param {string} id
273 * @return {!Array.<string>}
274 */
275 otherTabs(id) {
276 var result = [];
277 for (var i = 0; i < this._tabs.length; ++i) {
278 if (this._tabs[i].id !== id)
279 result.push(this._tabs[i].id);
280 }
281 return result;
282 }
283
284 /**
285 * @param {string} id
286 * @return {!Array.<string>}
287 */
288 _tabsToTheRight(id) {
289 var index = -1;
290 for (var i = 0; i < this._tabs.length; ++i) {
291 if (this._tabs[i].id === id) {
292 index = i;
293 break;
294 }
295 }
296 if (index === -1)
297 return [];
298 return this._tabs.slice(index + 1).map(function(tab) {
299 return tab.id;
300 });
301 }
302
303 /**
304 * @param {string} id
305 * @param {boolean=} userGesture
306 * @return {boolean}
307 */
308 selectTab(id, userGesture) {
309 if (this._currentTabLocked)
310 return false;
311 var focused = this.hasFocus();
312 var tab = this._tabsById[id];
313 if (!tab)
314 return false;
315 if (this._currentTab && this._currentTab.id === id)
316 return true;
317
318 this.suspendInvalidations();
319 this._hideCurrentTab();
320 this._showTab(tab);
321 this.resumeInvalidations();
322 this._currentTab = tab;
323
324 this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
325 this._tabsHistory.splice(0, 0, tab);
326
327 this._updateTabElements();
328 if (focused)
329 this.focus();
330
331 var eventData = {tabId: id, view: tab.view, isUserGesture: userGesture};
332 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabSelected, ev entData);
333 return true;
334 }
335
336 /**
337 * @param {number} tabsCount
338 * @return {!Array.<string>}
339 */
340 lastOpenedTabIds(tabsCount) {
341 function tabToTabId(tab) {
342 return tab.id;
343 }
344
345 return this._tabsHistory.slice(0, tabsCount).map(tabToTabId);
346 }
347
348 /**
349 * @param {string} id
350 * @param {string} iconType
351 * @param {string=} iconTooltip
352 */
353 setTabIcon(id, iconType, iconTooltip) {
354 var tab = this._tabsById[id];
355 if (tab._setIconType(iconType, iconTooltip))
356 this._updateTabElements();
357 }
358
359 /**
360 * @param {string} id
361 * @param {boolean} enabled
362 */
363 setTabEnabled(id, enabled) {
364 var tab = this._tabsById[id];
365 tab.tabElement.classList.toggle('disabled', !enabled);
366 }
367
368 /**
369 * @param {string} id
370 * @param {string} className
371 * @param {boolean=} force
372 */
373 toggleTabClass(id, className, force) {
374 var tab = this._tabsById[id];
375 if (tab._toggleClass(className, force))
376 this._updateTabElements();
377 }
378
379 /**
380 * @param {!WebInspector.Event} event
381 */
382 _zoomChanged(event) {
383 for (var i = 0; i < this._tabs.length; ++i)
384 delete this._tabs[i]._measuredWidth;
385 if (this.isShowing())
386 this._updateTabElements();
387 }
388
389 /**
390 * @param {string} id
391 * @param {string} tabTitle
392 * @param {string=} tabTooltip
393 */
394 changeTabTitle(id, tabTitle, tabTooltip) {
395 var tab = this._tabsById[id];
396 if (tabTooltip !== undefined)
397 tab.tooltip = tabTooltip;
398 if (tab.title !== tabTitle) {
399 tab.title = tabTitle;
400 this._updateTabElements();
401 }
402 }
403
404 /**
405 * @param {string} id
406 * @param {!WebInspector.Widget} view
407 */
408 changeTabView(id, view) {
409 var tab = this._tabsById[id];
410 if (tab.view === view)
411 return;
412
413 var shouldFocus = tab.view.hasFocus();
414
415 this.suspendInvalidations();
416
417 var isSelected = this._currentTab && this._currentTab.id === id;
418 if (isSelected)
419 this._hideTab(tab);
420 tab.view.detach();
421 tab.view = view;
422 tab.view.attach(this);
423 if (isSelected)
424 this._showTab(tab);
425 if (shouldFocus)
426 tab.view.focus();
427
428 this.resumeInvalidations();
429 }
430
431 /**
432 * @override
433 */
434 onResize() {
435 this._updateTabElements();
436 }
437
438 headerResized() {
439 this._updateTabElements();
440 }
441
442 /**
443 * @override
444 */
445 wasShown() {
446 var effectiveTab = this._currentTab || this._tabsHistory[0];
447 if (effectiveTab && this._autoSelectFirstItemOnShow)
448 this.selectTab(effectiveTab.id);
449 }
450
451 /**
452 * @param {boolean} enable
453 */
454 setTabSlider(enable) {
455 this._sliderEnabled = enable;
456 this._tabSlider.classList.toggle('enabled', enable);
457 }
458
459 /**
460 * @override
461 * @return {!Constraints}
462 */
463 calculateConstraints() {
464 var constraints = super.calculateConstraints();
465 var minContentConstraints = new Constraints(new Size(0, 0), new Size(50, 50) );
466 constraints = constraints.widthToMax(minContentConstraints).heightToMax(minC ontentConstraints);
467 if (this._verticalTabLayout)
468 constraints = constraints.addWidth(new Constraints(new Size(120, 0)));
469 else
470 constraints = constraints.addHeight(new Constraints(new Size(0, 30)));
471 return constraints;
472 }
473
474 _updateTabElements() {
475 WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElements);
476 }
477
478 /**
479 * @param {string} text
480 */
481 setPlaceholderText(text) {
482 this._noTabsMessage = text;
483 }
484
485 _innerUpdateTabElements() {
486 if (!this.isShowing())
487 return;
488
489 if (!this._tabs.length) {
490 this._contentElement.classList.add('has-no-tabs');
491 if (this._noTabsMessage && !this._noTabsMessageElement) {
492 this._noTabsMessageElement = this._contentElement.createChild('div', 'ta bbed-pane-placeholder fill');
493 this._noTabsMessageElement.textContent = this._noTabsMessage;
494 }
495 } else {
496 this._contentElement.classList.remove('has-no-tabs');
497 if (this._noTabsMessageElement) {
498 this._noTabsMessageElement.remove();
499 delete this._noTabsMessageElement;
500 }
501 }
502
503 this._measureDropDownButton();
504 this._updateWidths();
505 this._updateTabsDropDown();
506 this._updateTabSlider();
507 }
508
509 /**
510 * @param {number} index
511 * @param {!WebInspector.TabbedPaneTab} tab
512 */
513 _showTabElement(index, tab) {
514 if (index >= this._tabsElement.children.length)
515 this._tabsElement.appendChild(tab.tabElement);
516 else
517 this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.children[ index]);
518 tab._shown = true;
519 }
520
521 /**
522 * @param {!WebInspector.TabbedPaneTab} tab
523 */
524 _hideTabElement(tab) {
525 this._tabsElement.removeChild(tab.tabElement);
526 tab._shown = false;
527 }
528
529 _createDropDownButton() {
530 var dropDownContainer = createElementWithClass('div', 'tabbed-pane-header-ta bs-drop-down-container');
531 dropDownContainer.createChild('div', 'glyph');
532 this._dropDownMenu = new WebInspector.DropDownMenu(dropDownContainer);
533 this._dropDownMenu.addEventListener(
534 WebInspector.DropDownMenu.Events.ItemSelected, this._dropDownMenuItemSel ected, this);
535
536 return dropDownContainer;
537 }
538
539 /**
540 * @param {!WebInspector.Event} event
541 */
542 _dropDownMenuItemSelected(event) {
543 var tabId = /** @type {string} */ (event.data);
544 this._lastSelectedOverflowTab = this._tabsById[tabId];
545 this.selectTab(tabId, true);
546 }
547
548 _totalWidth() {
549 return this._headerContentsElement.getBoundingClientRect().width;
550 }
551
552 /**
553 * @return {number}
554 */
555 _numberOfTabsShown() {
556 var numTabsShown = 0;
557 for (var tab of this._tabs) {
558 if (tab._shown)
559 numTabsShown++;
560 }
561 return numTabsShown;
562 }
563
564 disableOverflowMenu() {
565 this._overflowDisabled = true;
566 }
567
568 _updateTabsDropDown() {
569 var tabsToShowIndexes = this._tabsToShowIndexes(
570 this._tabs, this._tabsHistory, this._totalWidth(), this._measuredDropDow nButtonWidth || 0);
571 if (this._lastSelectedOverflowTab && this._numberOfTabsShown() !== tabsToSho wIndexes.length) {
572 delete this._lastSelectedOverflowTab;
573 this._updateTabsDropDown();
574 return;
575 }
576
577 for (var i = 0; i < this._tabs.length; ++i) {
578 if (this._tabs[i]._shown && tabsToShowIndexes.indexOf(i) === -1)
579 this._hideTabElement(this._tabs[i]);
580 }
581 for (var i = 0; i < tabsToShowIndexes.length; ++i) {
582 var tab = this._tabs[tabsToShowIndexes[i]];
583 if (!tab._shown)
584 this._showTabElement(i, tab);
585 }
586
587 if (!this._overflowDisabled)
588 this._populateDropDownFromIndex();
589 }
590
591 _populateDropDownFromIndex() {
592 if (this._dropDownButton.parentElement)
593 this._headerContentsElement.removeChild(this._dropDownButton);
594
595 this._dropDownMenu.clear();
596
597 var tabsToShow = [];
598 for (var i = 0; i < this._tabs.length; ++i) {
599 if (!this._tabs[i]._shown)
600 tabsToShow.push(this._tabs[i]);
601 }
602
603 var selectedId = null;
604 for (var i = 0; i < tabsToShow.length; ++i) {
605 var tab = tabsToShow[i];
606 this._dropDownMenu.addItem(tab.id, tab.title);
607 if (this._tabsHistory[0] === tab)
608 selectedId = tab.id;
609 }
610 if (tabsToShow.length) {
611 this._headerContentsElement.appendChild(this._dropDownButton);
612 this._dropDownMenu.selectItem(selectedId);
613 }
614 }
615
616 _measureDropDownButton() {
617 if (this._overflowDisabled || this._measuredDropDownButtonWidth)
618 return;
619 this._dropDownButton.classList.add('measuring');
620 this._headerContentsElement.appendChild(this._dropDownButton);
621 this._measuredDropDownButtonWidth = this._dropDownButton.getBoundingClientRe ct().width;
622 this._headerContentsElement.removeChild(this._dropDownButton);
623 this._dropDownButton.classList.remove('measuring');
624 }
625
626 _updateWidths() {
627 var measuredWidths = this._measureWidths();
628 var maxWidth =
629 this._shrinkableTabs ? this._calculateMaxWidth(measuredWidths.slice(), t his._totalWidth()) : Number.MAX_VALUE;
630
631 var i = 0;
632 for (var tab of this._tabs)
633 tab.setWidth(this._verticalTabLayout ? -1 : Math.min(maxWidth, measuredWid ths[i++]));
634 }
635
636 _measureWidths() {
637 // Add all elements to measure into this._tabsElement
638 this._tabsElement.style.setProperty('width', '2000px');
639 var measuringTabElements = [];
640 for (var tab of this._tabs) {
641 if (typeof tab._measuredWidth === 'number')
642 continue;
643 var measuringTabElement = tab._createTabElement(true);
644 measuringTabElement.__tab = tab;
645 measuringTabElements.push(measuringTabElement);
646 this._tabsElement.appendChild(measuringTabElement);
647 }
648
649 // Perform measurement
650 for (var i = 0; i < measuringTabElements.length; ++i) {
651 var width = measuringTabElements[i].getBoundingClientRect().width;
652 measuringTabElements[i].__tab._measuredWidth = Math.ceil(width);
653 }
654
655 // Nuke elements from the UI
656 for (var i = 0; i < measuringTabElements.length; ++i)
657 measuringTabElements[i].remove();
658
659 // Combine the results.
660 var measuredWidths = [];
661 for (var tab of this._tabs)
662 measuredWidths.push(tab._measuredWidth);
663 this._tabsElement.style.removeProperty('width');
664
665 return measuredWidths;
666 }
667
668 /**
669 * @param {!Array.<number>} measuredWidths
670 * @param {number} totalWidth
671 */
672 _calculateMaxWidth(measuredWidths, totalWidth) {
673 if (!measuredWidths.length)
674 return 0;
675
676 measuredWidths.sort(function(x, y) {
677 return x - y;
678 });
679
680 var totalMeasuredWidth = 0;
681 for (var i = 0; i < measuredWidths.length; ++i)
682 totalMeasuredWidth += measuredWidths[i];
683
684 if (totalWidth >= totalMeasuredWidth)
685 return measuredWidths[measuredWidths.length - 1];
686
687 var totalExtraWidth = 0;
688 for (var i = measuredWidths.length - 1; i > 0; --i) {
689 var extraWidth = measuredWidths[i] - measuredWidths[i - 1];
690 totalExtraWidth += (measuredWidths.length - i) * extraWidth;
691
692 if (totalWidth + totalExtraWidth >= totalMeasuredWidth)
693 return measuredWidths[i - 1] +
694 (totalWidth + totalExtraWidth - totalMeasuredWidth) / (measuredWidth s.length - i);
695 }
696
697 return totalWidth / measuredWidths.length;
698 }
699
700 /**
701 * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsOrdered
702 * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsHistory
703 * @param {number} totalWidth
704 * @param {number} measuredDropDownButtonWidth
705 * @return {!Array.<number>}
706 */
707 _tabsToShowIndexes(tabsOrdered, tabsHistory, totalWidth, measuredDropDownButto nWidth) {
708 var tabsToShowIndexes = [];
709
710 var totalTabsWidth = 0;
711 var tabCount = tabsOrdered.length;
712 var tabsToLookAt = tabsOrdered.slice(0);
713 if (this._currentTab !== undefined)
714 tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._curren tTab), 1)[0]);
715 if (this._lastSelectedOverflowTab !== undefined)
716 tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._lastSe lectedOverflowTab), 1)[0]);
717 for (var i = 0; i < tabCount; ++i) {
718 var tab = this._automaticReorder ? tabsHistory[i] : tabsToLookAt[i];
719 totalTabsWidth += tab.width();
720 var minimalRequiredWidth = totalTabsWidth;
721 if (i !== tabCount - 1)
722 minimalRequiredWidth += measuredDropDownButtonWidth;
723 if (!this._verticalTabLayout && minimalRequiredWidth > totalWidth)
724 break;
725 tabsToShowIndexes.push(tabsOrdered.indexOf(tab));
726 }
727
728 tabsToShowIndexes.sort(function(x, y) {
729 return x - y;
730 });
731
732 return tabsToShowIndexes;
733 }
734
735 _hideCurrentTab() {
736 if (!this._currentTab)
737 return;
738
739 this._hideTab(this._currentTab);
740 delete this._currentTab;
741 }
742
743 /**
744 * @param {!WebInspector.TabbedPaneTab} tab
745 */
746 _showTab(tab) {
747 tab.tabElement.classList.add('selected');
748 tab.tabElement.setAttribute('aria-selected', 'true');
749 tab.view.showWidget(this.element);
750 this._updateTabSlider();
751 }
752
753 _updateTabSlider() {
754 if (!this._currentTab || !this._sliderEnabled)
755 return;
756 var left = 0;
757 for (var i = 0; i < this._tabs.length && this._currentTab !== this._tabs[i] && this._tabs[i]._shown; i++)
758 left += this._tabs[i]._measuredWidth;
759 var sliderWidth = this._currentTab._shown ? this._currentTab._measuredWidth : this._dropDownButton.offsetWidth;
760 var scaleFactor = window.devicePixelRatio >= 1.5 ? ' scaleY(0.75)' : '';
761 this._tabSlider.style.transform = 'translateX(' + left + 'px)' + scaleFactor ;
762 this._tabSlider.style.width = sliderWidth + 'px';
763
764 if (this._tabSlider.parentElement !== this._headerContentsElement)
765 this._headerContentsElement.appendChild(this._tabSlider);
766 }
767
768 /**
769 * @param {!WebInspector.TabbedPaneTab} tab
770 */
771 _hideTab(tab) {
772 tab.tabElement.classList.remove('selected');
773 tab.tabElement.setAttribute('aria-selected', 'false');
774 tab.view.hideWidget();
775 }
776
777 /**
778 * @override
779 * @return {!Array.<!Element>}
780 */
781 elementsToRestoreScrollPositionsFor() {
782 return [this._contentElement];
783 }
784
785 /**
786 * @param {!WebInspector.TabbedPaneTab} tab
787 * @param {number} index
788 */
789 _insertBefore(tab, index) {
790 this._tabsElement.insertBefore(tab._tabElement || null, this._tabsElement.ch ildNodes[index]);
791 var oldIndex = this._tabs.indexOf(tab);
792 this._tabs.splice(oldIndex, 1);
793 if (oldIndex < index)
794 --index;
795 this._tabs.splice(index, 0, tab);
796 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabOrderChanged , this._tabs);
797 }
798
799 /**
800 * @return {!WebInspector.Toolbar}
801 */
802 leftToolbar() {
803 if (!this._leftToolbar) {
804 this._leftToolbar = new WebInspector.Toolbar('tabbed-pane-left-toolbar');
805 this._headerElement.insertBefore(this._leftToolbar.element, this._headerEl ement.firstChild);
806 }
807 return this._leftToolbar;
808 }
809
810 /**
811 * @return {!WebInspector.Toolbar}
812 */
813 rightToolbar() {
814 if (!this._rightToolbar) {
815 this._rightToolbar = new WebInspector.Toolbar('tabbed-pane-right-toolbar') ;
816 this._headerElement.appendChild(this._rightToolbar.element);
817 }
818 return this._rightToolbar;
819 }
820
821 renderWithNoHeaderBackground() {
822 this._headerElement.classList.add('tabbed-pane-no-header-background');
823 }
824
825 /**
826 * @param {boolean} allow
827 * @param {boolean=} automatic
828 */
829 setAllowTabReorder(allow, automatic) {
830 this._allowTabReorder = allow;
831 this._automaticReorder = automatic;
832 }
62 }; 833 };
63 834
64 /** @enum {symbol} */ 835 /** @enum {symbol} */
65 WebInspector.TabbedPane.Events = { 836 WebInspector.TabbedPane.Events = {
66 TabSelected: Symbol("TabSelected"), 837 TabSelected: Symbol('TabSelected'),
67 TabClosed: Symbol("TabClosed"), 838 TabClosed: Symbol('TabClosed'),
68 TabOrderChanged: Symbol("TabOrderChanged") 839 TabOrderChanged: Symbol('TabOrderChanged')
69 }; 840 };
70 841
71 WebInspector.TabbedPane.prototype = {
72 /**
73 * @param {boolean} locked
74 */
75 setCurrentTabLocked: function(locked)
76 {
77 this._currentTabLocked = locked;
78 this._headerElement.classList.toggle("locked", this._currentTabLocked);
79 },
80
81 /**
82 * @param {boolean} autoSelect
83 */
84 setAutoSelectFirstItemOnShow: function(autoSelect)
85 {
86 this._autoSelectFirstItemOnShow = autoSelect;
87 },
88
89 /**
90 * @return {?WebInspector.Widget}
91 */
92 get visibleView()
93 {
94 return this._currentTab ? this._currentTab.view : null;
95 },
96
97 /**
98 * @return {!Array.<string>}
99 */
100 tabIds: function()
101 {
102 return this._tabs.map(tab => tab._id);
103 },
104
105 /**
106 * @param {string} tabId
107 * @return {number}
108 */
109 tabIndex: function(tabId)
110 {
111 return this._tabs.findIndex(tab => tab.id === tabId);
112 },
113
114 /**
115 * @return {!Array.<!WebInspector.Widget>}
116 */
117 tabViews: function()
118 {
119 return this._tabs.map(tab => tab.view);
120 },
121
122 /**
123 * @param {string} tabId
124 * @return {?WebInspector.Widget}
125 */
126 tabView: function(tabId)
127 {
128 return this._tabsById[tabId] ? this._tabsById[tabId].view : null;
129 },
130
131 /**
132 * @return {?string}
133 */
134 get selectedTabId()
135 {
136 return this._currentTab ? this._currentTab.id : null;
137 },
138
139 /**
140 * @param {boolean} shrinkableTabs
141 */
142 setShrinkableTabs: function(shrinkableTabs)
143 {
144 this._shrinkableTabs = shrinkableTabs;
145 },
146
147 /**
148 * @param {boolean} verticalTabLayout
149 */
150 setVerticalTabLayout: function(verticalTabLayout)
151 {
152 this._verticalTabLayout = verticalTabLayout;
153 this.contentElement.classList.add("vertical-tab-layout");
154 this.invalidateConstraints();
155 },
156
157 /**
158 * @param {boolean} closeableTabs
159 */
160 setCloseableTabs: function(closeableTabs)
161 {
162 this._closeableTabs = closeableTabs;
163 },
164
165 /**
166 * @override
167 */
168 focus: function()
169 {
170 if (this.visibleView)
171 this.visibleView.focus();
172 else
173 this.contentElement.focus();
174 },
175
176 /**
177 * @return {!Element}
178 */
179 headerElement: function()
180 {
181 return this._headerElement;
182 },
183
184 /**
185 * @param {string} id
186 * @return {boolean}
187 */
188 isTabCloseable: function(id)
189 {
190 var tab = this._tabsById[id];
191 return tab ? tab.isCloseable() : false;
192 },
193
194 /**
195 * @param {!WebInspector.TabbedPaneTabDelegate} delegate
196 */
197 setTabDelegate: function(delegate)
198 {
199 var tabs = this._tabs.slice();
200 for (var i = 0; i < tabs.length; ++i)
201 tabs[i].setDelegate(delegate);
202 this._delegate = delegate;
203 },
204
205 /**
206 * @param {string} id
207 * @param {string} tabTitle
208 * @param {!WebInspector.Widget} view
209 * @param {string=} tabTooltip
210 * @param {boolean=} userGesture
211 * @param {boolean=} isCloseable
212 * @param {number=} index
213 */
214 appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable , index)
215 {
216 isCloseable = typeof isCloseable === "boolean" ? isCloseable : this._clo seableTabs;
217 var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, isCloseable , view, tabTooltip);
218 tab.setDelegate(this._delegate);
219 this._tabsById[id] = tab;
220 if (index !== undefined)
221 this._tabs.splice(index, 0, tab);
222 else
223 this._tabs.push(tab);
224 this._tabsHistory.push(tab);
225 view.attach(this);
226 if (this._tabsHistory[0] === tab && this.isShowing())
227 this.selectTab(tab.id, userGesture);
228 this._updateTabElements();
229 },
230
231 /**
232 * @param {string} id
233 * @param {boolean=} userGesture
234 */
235 closeTab: function(id, userGesture)
236 {
237 this.closeTabs([id], userGesture);
238 },
239
240 /**
241 * @param {!Array.<string>} ids
242 * @param {boolean=} userGesture
243 */
244 closeTabs: function(ids, userGesture)
245 {
246 var focused = this.hasFocus();
247 for (var i = 0; i < ids.length; ++i)
248 this._innerCloseTab(ids[i], userGesture);
249 this._updateTabElements();
250 if (this._tabsHistory.length)
251 this.selectTab(this._tabsHistory[0].id, false);
252 if (focused)
253 this.focus();
254 },
255
256 /**
257 * @param {string} id
258 * @param {boolean=} userGesture
259 */
260 _innerCloseTab: function(id, userGesture)
261 {
262 if (!this._tabsById[id])
263 return;
264 if (userGesture && !this._tabsById[id]._closeable)
265 return;
266 if (this._currentTab && this._currentTab.id === id)
267 this._hideCurrentTab();
268
269 var tab = this._tabsById[id];
270 delete this._tabsById[id];
271
272 this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
273 this._tabs.splice(this._tabs.indexOf(tab), 1);
274 if (tab._shown)
275 this._hideTabElement(tab);
276 tab.view.detach();
277
278 var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
279 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabClosed, eventData);
280 return true;
281 },
282
283 /**
284 * @param {string} tabId
285 * @return {boolean}
286 */
287 hasTab: function(tabId)
288 {
289 return !!this._tabsById[tabId];
290 },
291
292 /**
293 * @return {!Array.<string>}
294 */
295 allTabs: function()
296 {
297 return this._tabs.map(function(tab) { return tab.id; });
298 },
299
300 /**
301 * @param {string} id
302 * @return {!Array.<string>}
303 */
304 otherTabs: function(id)
305 {
306 var result = [];
307 for (var i = 0; i < this._tabs.length; ++i) {
308 if (this._tabs[i].id !== id)
309 result.push(this._tabs[i].id);
310 }
311 return result;
312 },
313
314 /**
315 * @param {string} id
316 * @return {!Array.<string>}
317 */
318 _tabsToTheRight: function(id)
319 {
320 var index = -1;
321 for (var i = 0; i < this._tabs.length; ++i) {
322 if (this._tabs[i].id === id) {
323 index = i;
324 break;
325 }
326 }
327 if (index === -1)
328 return [];
329 return this._tabs.slice(index + 1).map(function(tab) { return tab.id; }) ;
330 },
331
332 /**
333 * @param {string} id
334 * @param {boolean=} userGesture
335 * @return {boolean}
336 */
337 selectTab: function(id, userGesture)
338 {
339 if (this._currentTabLocked)
340 return false;
341 var focused = this.hasFocus();
342 var tab = this._tabsById[id];
343 if (!tab)
344 return false;
345 if (this._currentTab && this._currentTab.id === id)
346 return true;
347
348 this.suspendInvalidations();
349 this._hideCurrentTab();
350 this._showTab(tab);
351 this.resumeInvalidations();
352 this._currentTab = tab;
353
354 this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1);
355 this._tabsHistory.splice(0, 0, tab);
356
357 this._updateTabElements();
358 if (focused)
359 this.focus();
360
361 var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture };
362 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabSelected , eventData);
363 return true;
364 },
365
366 /**
367 * @param {number} tabsCount
368 * @return {!Array.<string>}
369 */
370 lastOpenedTabIds: function(tabsCount)
371 {
372 function tabToTabId(tab) {
373 return tab.id;
374 }
375
376 return this._tabsHistory.slice(0, tabsCount).map(tabToTabId);
377 },
378
379 /**
380 * @param {string} id
381 * @param {string} iconType
382 * @param {string=} iconTooltip
383 */
384 setTabIcon: function(id, iconType, iconTooltip)
385 {
386 var tab = this._tabsById[id];
387 if (tab._setIconType(iconType, iconTooltip))
388 this._updateTabElements();
389 },
390
391 /**
392 * @param {string} id
393 * @param {boolean} enabled
394 */
395 setTabEnabled: function(id, enabled)
396 {
397 var tab = this._tabsById[id];
398 tab.tabElement.classList.toggle("disabled", !enabled);
399 },
400
401 /**
402 * @param {string} id
403 * @param {string} className
404 * @param {boolean=} force
405 */
406 toggleTabClass: function(id, className, force)
407 {
408 var tab = this._tabsById[id];
409 if (tab._toggleClass(className, force))
410 this._updateTabElements();
411 },
412
413 /**
414 * @param {!WebInspector.Event} event
415 */
416 _zoomChanged: function(event)
417 {
418 for (var i = 0; i < this._tabs.length; ++i)
419 delete this._tabs[i]._measuredWidth;
420 if (this.isShowing())
421 this._updateTabElements();
422 },
423
424 /**
425 * @param {string} id
426 * @param {string} tabTitle
427 * @param {string=} tabTooltip
428 */
429 changeTabTitle: function(id, tabTitle, tabTooltip)
430 {
431 var tab = this._tabsById[id];
432 if (tabTooltip !== undefined)
433 tab.tooltip = tabTooltip;
434 if (tab.title !== tabTitle) {
435 tab.title = tabTitle;
436 this._updateTabElements();
437 }
438 },
439
440 /**
441 * @param {string} id
442 * @param {!WebInspector.Widget} view
443 */
444 changeTabView: function(id, view)
445 {
446 var tab = this._tabsById[id];
447 if (tab.view === view)
448 return;
449
450 var shouldFocus = tab.view.hasFocus();
451
452 this.suspendInvalidations();
453
454 var isSelected = this._currentTab && this._currentTab.id === id;
455 if (isSelected)
456 this._hideTab(tab);
457 tab.view.detach();
458 tab.view = view;
459 tab.view.attach(this);
460 if (isSelected)
461 this._showTab(tab);
462 if (shouldFocus)
463 tab.view.focus();
464
465 this.resumeInvalidations();
466 },
467
468 onResize: function()
469 {
470 this._updateTabElements();
471 },
472
473 headerResized: function()
474 {
475 this._updateTabElements();
476 },
477
478 wasShown: function()
479 {
480 var effectiveTab = this._currentTab || this._tabsHistory[0];
481 if (effectiveTab && this._autoSelectFirstItemOnShow)
482 this.selectTab(effectiveTab.id);
483 },
484
485 /**
486 * @param {boolean} enable
487 */
488 setTabSlider: function(enable)
489 {
490 this._sliderEnabled = enable;
491 this._tabSlider.classList.toggle("enabled", enable);
492 },
493
494 /**
495 * @override
496 * @return {!Constraints}
497 */
498 calculateConstraints: function()
499 {
500 var constraints = WebInspector.VBox.prototype.calculateConstraints.call( this);
501 var minContentConstraints = new Constraints(new Size(0, 0), new Size(50, 50));
502 constraints = constraints.widthToMax(minContentConstraints).heightToMax( minContentConstraints);
503 if (this._verticalTabLayout)
504 constraints = constraints.addWidth(new Constraints(new Size(120, 0)) );
505 else
506 constraints = constraints.addHeight(new Constraints(new Size(0, 30)) );
507 return constraints;
508 },
509
510 _updateTabElements: function()
511 {
512 WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElemen ts);
513 },
514
515 /**
516 * @param {string} text
517 */
518 setPlaceholderText: function(text)
519 {
520 this._noTabsMessage = text;
521 },
522
523 _innerUpdateTabElements: function()
524 {
525 if (!this.isShowing())
526 return;
527
528 if (!this._tabs.length) {
529 this._contentElement.classList.add("has-no-tabs");
530 if (this._noTabsMessage && !this._noTabsMessageElement) {
531 this._noTabsMessageElement = this._contentElement.createChild("d iv", "tabbed-pane-placeholder fill");
532 this._noTabsMessageElement.textContent = this._noTabsMessage;
533 }
534 } else {
535 this._contentElement.classList.remove("has-no-tabs");
536 if (this._noTabsMessageElement) {
537 this._noTabsMessageElement.remove();
538 delete this._noTabsMessageElement;
539 }
540 }
541
542 this._measureDropDownButton();
543 this._updateWidths();
544 this._updateTabsDropDown();
545 this._updateTabSlider();
546 },
547
548 /**
549 * @param {number} index
550 * @param {!WebInspector.TabbedPaneTab} tab
551 */
552 _showTabElement: function(index, tab)
553 {
554 if (index >= this._tabsElement.children.length)
555 this._tabsElement.appendChild(tab.tabElement);
556 else
557 this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.chi ldren[index]);
558 tab._shown = true;
559 },
560
561 /**
562 * @param {!WebInspector.TabbedPaneTab} tab
563 */
564 _hideTabElement: function(tab)
565 {
566 this._tabsElement.removeChild(tab.tabElement);
567 tab._shown = false;
568 },
569
570 _createDropDownButton: function()
571 {
572 var dropDownContainer = createElementWithClass("div", "tabbed-pane-heade r-tabs-drop-down-container");
573 dropDownContainer.createChild("div", "glyph");
574 this._dropDownMenu = new WebInspector.DropDownMenu(dropDownContainer);
575 this._dropDownMenu.addEventListener(WebInspector.DropDownMenu.Events.Ite mSelected, this._dropDownMenuItemSelected, this);
576
577 return dropDownContainer;
578 },
579
580 /**
581 * @param {!WebInspector.Event} event
582 */
583 _dropDownMenuItemSelected: function(event)
584 {
585 var tabId = /** @type {string} */ (event.data);
586 this._lastSelectedOverflowTab = this._tabsById[tabId];
587 this.selectTab(tabId, true);
588 },
589
590 _totalWidth: function()
591 {
592 return this._headerContentsElement.getBoundingClientRect().width;
593 },
594
595 /**
596 * @return {number}
597 */
598 _numberOfTabsShown: function()
599 {
600 var numTabsShown = 0;
601 for (var tab of this._tabs) {
602 if (tab._shown)
603 numTabsShown++;
604 }
605 return numTabsShown;
606 },
607
608 disableOverflowMenu: function()
609 {
610 this._overflowDisabled = true;
611 },
612
613 _updateTabsDropDown: function()
614 {
615 var tabsToShowIndexes = this._tabsToShowIndexes(this._tabs, this._tabsHi story, this._totalWidth(), this._measuredDropDownButtonWidth || 0);
616 if (this._lastSelectedOverflowTab && this._numberOfTabsShown() !== tabsT oShowIndexes.length) {
617 delete this._lastSelectedOverflowTab;
618 this._updateTabsDropDown();
619 return;
620 }
621
622 for (var i = 0; i < this._tabs.length; ++i) {
623 if (this._tabs[i]._shown && tabsToShowIndexes.indexOf(i) === -1)
624 this._hideTabElement(this._tabs[i]);
625 }
626 for (var i = 0; i < tabsToShowIndexes.length; ++i) {
627 var tab = this._tabs[tabsToShowIndexes[i]];
628 if (!tab._shown)
629 this._showTabElement(i, tab);
630 }
631
632 if (!this._overflowDisabled)
633 this._populateDropDownFromIndex();
634 },
635
636 _populateDropDownFromIndex: function()
637 {
638 if (this._dropDownButton.parentElement)
639 this._headerContentsElement.removeChild(this._dropDownButton);
640
641 this._dropDownMenu.clear();
642
643 var tabsToShow = [];
644 for (var i = 0; i < this._tabs.length; ++i) {
645 if (!this._tabs[i]._shown)
646 tabsToShow.push(this._tabs[i]);
647 }
648
649 var selectedId = null;
650 for (var i = 0; i < tabsToShow.length; ++i) {
651 var tab = tabsToShow[i];
652 this._dropDownMenu.addItem(tab.id, tab.title);
653 if (this._tabsHistory[0] === tab)
654 selectedId = tab.id;
655 }
656 if (tabsToShow.length) {
657 this._headerContentsElement.appendChild(this._dropDownButton);
658 this._dropDownMenu.selectItem(selectedId);
659 }
660 },
661
662 _measureDropDownButton: function()
663 {
664 if (this._overflowDisabled || this._measuredDropDownButtonWidth)
665 return;
666 this._dropDownButton.classList.add("measuring");
667 this._headerContentsElement.appendChild(this._dropDownButton);
668 this._measuredDropDownButtonWidth = this._dropDownButton.getBoundingClie ntRect().width;
669 this._headerContentsElement.removeChild(this._dropDownButton);
670 this._dropDownButton.classList.remove("measuring");
671 },
672
673 _updateWidths: function()
674 {
675 var measuredWidths = this._measureWidths();
676 var maxWidth = this._shrinkableTabs ? this._calculateMaxWidth(measuredWi dths.slice(), this._totalWidth()) : Number.MAX_VALUE;
677
678 var i = 0;
679 for (var tab of this._tabs)
680 tab.setWidth(this._verticalTabLayout ? -1 : Math.min(maxWidth, measu redWidths[i++]));
681 },
682
683 _measureWidths: function()
684 {
685 // Add all elements to measure into this._tabsElement
686 this._tabsElement.style.setProperty("width", "2000px");
687 var measuringTabElements = [];
688 for (var tab of this._tabs) {
689 if (typeof tab._measuredWidth === "number")
690 continue;
691 var measuringTabElement = tab._createTabElement(true);
692 measuringTabElement.__tab = tab;
693 measuringTabElements.push(measuringTabElement);
694 this._tabsElement.appendChild(measuringTabElement);
695 }
696
697 // Perform measurement
698 for (var i = 0; i < measuringTabElements.length; ++i) {
699 var width = measuringTabElements[i].getBoundingClientRect().width;
700 measuringTabElements[i].__tab._measuredWidth = Math.ceil(width);
701 }
702
703 // Nuke elements from the UI
704 for (var i = 0; i < measuringTabElements.length; ++i)
705 measuringTabElements[i].remove();
706
707 // Combine the results.
708 var measuredWidths = [];
709 for (var tab of this._tabs)
710 measuredWidths.push(tab._measuredWidth);
711 this._tabsElement.style.removeProperty("width");
712
713 return measuredWidths;
714 },
715
716 /**
717 * @param {!Array.<number>} measuredWidths
718 * @param {number} totalWidth
719 */
720 _calculateMaxWidth: function(measuredWidths, totalWidth)
721 {
722 if (!measuredWidths.length)
723 return 0;
724
725 measuredWidths.sort(function(x, y) { return x - y; });
726
727 var totalMeasuredWidth = 0;
728 for (var i = 0; i < measuredWidths.length; ++i)
729 totalMeasuredWidth += measuredWidths[i];
730
731 if (totalWidth >= totalMeasuredWidth)
732 return measuredWidths[measuredWidths.length - 1];
733
734 var totalExtraWidth = 0;
735 for (var i = measuredWidths.length - 1; i > 0; --i) {
736 var extraWidth = measuredWidths[i] - measuredWidths[i - 1];
737 totalExtraWidth += (measuredWidths.length - i) * extraWidth;
738
739 if (totalWidth + totalExtraWidth >= totalMeasuredWidth)
740 return measuredWidths[i - 1] + (totalWidth + totalExtraWidth - t otalMeasuredWidth) / (measuredWidths.length - i);
741 }
742
743 return totalWidth / measuredWidths.length;
744 },
745
746 /**
747 * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsOrdered
748 * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsHistory
749 * @param {number} totalWidth
750 * @param {number} measuredDropDownButtonWidth
751 * @return {!Array.<number>}
752 */
753 _tabsToShowIndexes: function(tabsOrdered, tabsHistory, totalWidth, measuredD ropDownButtonWidth)
754 {
755 var tabsToShowIndexes = [];
756
757 var totalTabsWidth = 0;
758 var tabCount = tabsOrdered.length;
759 var tabsToLookAt = tabsOrdered.slice(0);
760 if (this._currentTab !== undefined)
761 tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._ currentTab), 1)[0]);
762 if (this._lastSelectedOverflowTab !== undefined)
763 tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._ lastSelectedOverflowTab), 1)[0]);
764 for (var i = 0; i < tabCount; ++i) {
765 var tab = this._automaticReorder ? tabsHistory[i] : tabsToLookAt[i];
766 totalTabsWidth += tab.width();
767 var minimalRequiredWidth = totalTabsWidth;
768 if (i !== tabCount - 1)
769 minimalRequiredWidth += measuredDropDownButtonWidth;
770 if (!this._verticalTabLayout && minimalRequiredWidth > totalWidth)
771 break;
772 tabsToShowIndexes.push(tabsOrdered.indexOf(tab));
773 }
774
775 tabsToShowIndexes.sort(function(x, y) { return x - y; });
776
777 return tabsToShowIndexes;
778 },
779
780 _hideCurrentTab: function()
781 {
782 if (!this._currentTab)
783 return;
784
785 this._hideTab(this._currentTab);
786 delete this._currentTab;
787 },
788
789 /**
790 * @param {!WebInspector.TabbedPaneTab} tab
791 */
792 _showTab: function(tab)
793 {
794 tab.tabElement.classList.add("selected");
795 tab.tabElement.setAttribute("aria-selected", "true");
796 tab.view.showWidget(this.element);
797 this._updateTabSlider();
798 },
799
800 _updateTabSlider: function()
801 {
802 if (!this._currentTab || !this._sliderEnabled)
803 return;
804 var left = 0;
805 for (var i = 0; i < this._tabs.length && this._currentTab !== this._tabs [i] && this._tabs[i]._shown; i++)
806 left += this._tabs[i]._measuredWidth;
807 var sliderWidth = this._currentTab._shown ? this._currentTab._measuredWi dth : this._dropDownButton.offsetWidth;
808 var scaleFactor = window.devicePixelRatio >= 1.5 ? " scaleY(0.75)" : "";
809 this._tabSlider.style.transform = "translateX(" + left + "px)" + scaleFa ctor;
810 this._tabSlider.style.width = sliderWidth + "px";
811
812 if (this._tabSlider.parentElement !== this._headerContentsElement)
813 this._headerContentsElement.appendChild(this._tabSlider);
814 },
815
816 /**
817 * @param {!WebInspector.TabbedPaneTab} tab
818 */
819 _hideTab: function(tab)
820 {
821 tab.tabElement.classList.remove("selected");
822 tab.tabElement.setAttribute("aria-selected", "false");
823 tab.view.hideWidget();
824 },
825
826 /**
827 * @override
828 * @return {!Array.<!Element>}
829 */
830 elementsToRestoreScrollPositionsFor: function()
831 {
832 return [ this._contentElement ];
833 },
834
835 /**
836 * @param {!WebInspector.TabbedPaneTab} tab
837 * @param {number} index
838 */
839 _insertBefore: function(tab, index)
840 {
841 this._tabsElement.insertBefore(tab._tabElement || null, this._tabsElemen t.childNodes[index]);
842 var oldIndex = this._tabs.indexOf(tab);
843 this._tabs.splice(oldIndex, 1);
844 if (oldIndex < index)
845 --index;
846 this._tabs.splice(index, 0, tab);
847 this.dispatchEventToListeners(WebInspector.TabbedPane.Events.TabOrderCha nged, this._tabs);
848 },
849
850 /**
851 * @return {!WebInspector.Toolbar}
852 */
853 leftToolbar: function()
854 {
855 if (!this._leftToolbar) {
856 this._leftToolbar = new WebInspector.Toolbar("tabbed-pane-left-toolb ar");
857 this._headerElement.insertBefore(this._leftToolbar.element, this._he aderElement.firstChild);
858 }
859 return this._leftToolbar;
860 },
861
862 /**
863 * @return {!WebInspector.Toolbar}
864 */
865 rightToolbar: function()
866 {
867 if (!this._rightToolbar) {
868 this._rightToolbar = new WebInspector.Toolbar("tabbed-pane-right-too lbar");
869 this._headerElement.appendChild(this._rightToolbar.element);
870 }
871 return this._rightToolbar;
872 },
873
874 renderWithNoHeaderBackground: function()
875 {
876 this._headerElement.classList.add("tabbed-pane-no-header-background");
877 },
878
879 /**
880 * @param {boolean} allow
881 * @param {boolean=} automatic
882 */
883 setAllowTabReorder: function(allow, automatic)
884 {
885 this._allowTabReorder = allow;
886 this._automaticReorder = automatic;
887 },
888
889 __proto__: WebInspector.VBox.prototype
890 };
891
892 /** 842 /**
893 * @constructor 843 * @unrestricted
894 * @param {!WebInspector.TabbedPane} tabbedPane
895 * @param {string} id
896 * @param {string} title
897 * @param {boolean} closeable
898 * @param {!WebInspector.Widget} view
899 * @param {string=} tooltip
900 */ 844 */
901 WebInspector.TabbedPaneTab = function(tabbedPane, id, title, closeable, view, to oltip) 845 WebInspector.TabbedPaneTab = class {
902 { 846 /**
847 * @param {!WebInspector.TabbedPane} tabbedPane
848 * @param {string} id
849 * @param {string} title
850 * @param {boolean} closeable
851 * @param {!WebInspector.Widget} view
852 * @param {string=} tooltip
853 */
854 constructor(tabbedPane, id, title, closeable, view, tooltip) {
903 this._closeable = closeable; 855 this._closeable = closeable;
904 this._tabbedPane = tabbedPane; 856 this._tabbedPane = tabbedPane;
905 this._id = id; 857 this._id = id;
906 this._title = title; 858 this._title = title;
907 this._tooltip = tooltip; 859 this._tooltip = tooltip;
908 this._view = view; 860 this._view = view;
909 this._shown = false; 861 this._shown = false;
910 /** @type {number} */ this._measuredWidth; 862 /** @type {number} */ this._measuredWidth;
911 /** @type {!Element|undefined} */ this._tabElement; 863 /** @type {!Element|undefined} */ this._tabElement;
912 }; 864 }
913 865
914 WebInspector.TabbedPaneTab.prototype = { 866 /**
867 * @return {string}
868 */
869 get id() {
870 return this._id;
871 }
872
873 /**
874 * @return {string}
875 */
876 get title() {
877 return this._title;
878 }
879
880 /**
881 * @param {string} title
882 */
883 set title(title) {
884 if (title === this._title)
885 return;
886 this._title = title;
887 if (this._titleElement)
888 this._titleElement.textContent = title;
889 delete this._measuredWidth;
890 }
891
892 /**
893 * @return {boolean}
894 */
895 isCloseable() {
896 return this._closeable;
897 }
898
899 /**
900 * @param {string} iconType
901 * @param {string=} iconTooltip
902 * @return {boolean}
903 */
904 _setIconType(iconType, iconTooltip) {
905 if (iconType === this._iconType && iconTooltip === this._iconTooltip)
906 return false;
907 this._iconType = iconType;
908 this._iconTooltip = iconTooltip;
909 if (this._tabElement)
910 this._createIconElement(this._tabElement, this._titleElement);
911 delete this._measuredWidth;
912 return true;
913 }
914
915 /**
916 * @param {string} className
917 * @param {boolean=} force
918 * @return {boolean}
919 */
920 _toggleClass(className, force) {
921 var element = this.tabElement;
922 var hasClass = element.classList.contains(className);
923 if (hasClass === force)
924 return false;
925 element.classList.toggle(className, force);
926 delete this._measuredWidth;
927 return true;
928 }
929
930 /**
931 * @return {!WebInspector.Widget}
932 */
933 get view() {
934 return this._view;
935 }
936
937 /**
938 * @param {!WebInspector.Widget} view
939 */
940 set view(view) {
941 this._view = view;
942 }
943
944 /**
945 * @return {string|undefined}
946 */
947 get tooltip() {
948 return this._tooltip;
949 }
950
951 /**
952 * @param {string|undefined} tooltip
953 */
954 set tooltip(tooltip) {
955 this._tooltip = tooltip;
956 if (this._titleElement)
957 this._titleElement.title = tooltip || '';
958 }
959
960 /**
961 * @return {!Element}
962 */
963 get tabElement() {
964 if (!this._tabElement)
965 this._tabElement = this._createTabElement(false);
966
967 return this._tabElement;
968 }
969
970 /**
971 * @return {number}
972 */
973 width() {
974 return this._width;
975 }
976
977 /**
978 * @param {number} width
979 */
980 setWidth(width) {
981 this.tabElement.style.width = width === -1 ? '' : (width + 'px');
982 this._width = width;
983 }
984
985 /**
986 * @param {!WebInspector.TabbedPaneTabDelegate} delegate
987 */
988 setDelegate(delegate) {
989 this._delegate = delegate;
990 }
991
992 /**
993 * @param {!Element} tabElement
994 * @param {!Element} titleElement
995 */
996 _createIconElement(tabElement, titleElement) {
997 if (tabElement.__iconElement)
998 tabElement.__iconElement.remove();
999 if (!this._iconType)
1000 return;
1001
1002 var iconElement = createElementWithClass('label', 'tabbed-pane-header-tab-ic on', 'dt-icon-label');
1003 iconElement.type = this._iconType;
1004 if (this._iconTooltip)
1005 iconElement.title = this._iconTooltip;
1006 tabElement.insertBefore(iconElement, titleElement);
1007 tabElement.__iconElement = iconElement;
1008 }
1009
1010 /**
1011 * @param {boolean} measuring
1012 * @return {!Element}
1013 */
1014 _createTabElement(measuring) {
1015 var tabElement = createElementWithClass('div', 'tabbed-pane-header-tab');
1016 tabElement.id = 'tab-' + this._id;
1017 tabElement.tabIndex = -1;
1018 tabElement.setAttribute('role', 'tab');
1019 tabElement.setAttribute('aria-selected', 'false');
1020 tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPa ne, this.id, true);
1021
1022 var titleElement = tabElement.createChild('span', 'tabbed-pane-header-tab-ti tle');
1023 titleElement.textContent = this.title;
1024 titleElement.title = this.tooltip || '';
1025 this._createIconElement(tabElement, titleElement);
1026 if (!measuring)
1027 this._titleElement = titleElement;
1028
1029 if (this._closeable)
1030 tabElement.createChild('div', 'tabbed-pane-close-button', 'dt-close-button ').gray = true;
1031
1032 if (measuring) {
1033 tabElement.classList.add('measuring');
1034 } else {
1035 tabElement.addEventListener('click', this._tabClicked.bind(this), false);
1036 tabElement.addEventListener('auxclick', this._tabClicked.bind(this), false );
1037 tabElement.addEventListener('mousedown', this._tabMouseDown.bind(this), fa lse);
1038 tabElement.addEventListener('mouseup', this._tabMouseUp.bind(this), false) ;
1039
1040 tabElement.addEventListener('contextmenu', this._tabContextMenu.bind(this) , false);
1041 if (this._tabbedPane._allowTabReorder)
1042 WebInspector.installDragHandle(
1043 tabElement, this._startTabDragging.bind(this), this._tabDragging.bin d(this),
1044 this._endTabDragging.bind(this), '-webkit-grabbing', 'pointer', 200) ;
1045 }
1046
1047 return tabElement;
1048 }
1049
1050 /**
1051 * @param {!Event} event
1052 */
1053 _tabClicked(event) {
1054 var middleButton = event.button === 1;
1055 var shouldClose = this._closeable && (middleButton || event.target.classList .contains('tabbed-pane-close-button'));
1056 if (!shouldClose) {
1057 this._tabbedPane.focus();
1058 return;
1059 }
1060 this._closeTabs([this.id]);
1061 event.consume(true);
1062 }
1063
1064 /**
1065 * @param {!Event} event
1066 */
1067 _tabMouseDown(event) {
1068 if (event.target.classList.contains('tabbed-pane-close-button') || event.but ton === 1)
1069 return;
1070 this._tabbedPane.selectTab(this.id, true);
1071 }
1072
1073 /**
1074 * @param {!Event} event
1075 */
1076 _tabMouseUp(event) {
1077 // This is needed to prevent middle-click pasting on linux when tabs are cli cked.
1078 if (event.button === 1)
1079 event.consume(true);
1080 }
1081
1082 /**
1083 * @param {!Array.<string>} ids
1084 */
1085 _closeTabs(ids) {
1086 if (this._delegate) {
1087 this._delegate.closeTabs(this._tabbedPane, ids);
1088 return;
1089 }
1090 this._tabbedPane.closeTabs(ids, true);
1091 }
1092
1093 _tabContextMenu(event) {
915 /** 1094 /**
916 * @return {string} 1095 * @this {WebInspector.TabbedPaneTab}
917 */ 1096 */
918 get id() 1097 function close() {
919 { 1098 this._closeTabs([this.id]);
920 return this._id; 1099 }
921 },
922 1100
923 /** 1101 /**
924 * @return {string} 1102 * @this {WebInspector.TabbedPaneTab}
925 */ 1103 */
926 get title() 1104 function closeOthers() {
927 { 1105 this._closeTabs(this._tabbedPane.otherTabs(this.id));
928 return this._title; 1106 }
929 },
930
931 set title(title)
932 {
933 if (title === this._title)
934 return;
935 this._title = title;
936 if (this._titleElement)
937 this._titleElement.textContent = title;
938 delete this._measuredWidth;
939 },
940 1107
941 /** 1108 /**
942 * @return {boolean} 1109 * @this {WebInspector.TabbedPaneTab}
943 */ 1110 */
944 isCloseable: function() 1111 function closeAll() {
945 { 1112 this._closeTabs(this._tabbedPane.allTabs());
946 return this._closeable; 1113 }
947 },
948 1114
949 /** 1115 /**
950 * @param {string} iconType 1116 * @this {WebInspector.TabbedPaneTab}
951 * @param {string=} iconTooltip
952 * @return {boolean}
953 */ 1117 */
954 _setIconType: function(iconType, iconTooltip) 1118 function closeToTheRight() {
955 { 1119 this._closeTabs(this._tabbedPane._tabsToTheRight(this.id));
956 if (iconType === this._iconType && iconTooltip === this._iconTooltip) 1120 }
957 return false; 1121
958 this._iconType = iconType; 1122 var contextMenu = new WebInspector.ContextMenu(event);
959 this._iconTooltip = iconTooltip; 1123 if (this._closeable) {
960 if (this._tabElement) 1124 contextMenu.appendItem(WebInspector.UIString.capitalize('Close'), close.bi nd(this));
961 this._createIconElement(this._tabElement, this._titleElement); 1125 contextMenu.appendItem(WebInspector.UIString.capitalize('Close ^others'), closeOthers.bind(this));
962 delete this._measuredWidth; 1126 contextMenu.appendItem(WebInspector.UIString.capitalize('Close ^tabs to th e ^right'), closeToTheRight.bind(this));
963 return true; 1127 contextMenu.appendItem(WebInspector.UIString.capitalize('Close ^all'), clo seAll.bind(this));
964 }, 1128 }
965 1129 if (this._delegate)
966 /** 1130 this._delegate.onContextMenu(this.id, contextMenu);
967 * @param {string} className 1131 contextMenu.show();
968 * @param {boolean=} force 1132 }
969 * @return {boolean} 1133
970 */ 1134 /**
971 _toggleClass: function(className, force) 1135 * @param {!Event} event
972 { 1136 * @return {boolean}
973 var element = this.tabElement; 1137 */
974 var hasClass = element.classList.contains(className); 1138 _startTabDragging(event) {
975 if (hasClass === force) 1139 if (event.target.classList.contains('tabbed-pane-close-button'))
976 return false; 1140 return false;
977 element.classList.toggle(className, force); 1141 this._dragStartX = event.pageX;
978 delete this._measuredWidth; 1142 this._tabElement.classList.add('dragging');
979 return true; 1143 this._tabbedPane._tabSlider.remove();
980 }, 1144 return true;
981 1145 }
982 /** 1146
983 * @return {!WebInspector.Widget} 1147 /**
984 */ 1148 * @param {!Event} event
985 get view() 1149 */
986 { 1150 _tabDragging(event) {
987 return this._view; 1151 var tabElements = this._tabbedPane._tabsElement.childNodes;
988 }, 1152 for (var i = 0; i < tabElements.length; ++i) {
989 1153 var tabElement = tabElements[i];
990 set view(view) 1154 if (tabElement === this._tabElement)
991 { 1155 continue;
992 this._view = view; 1156
993 }, 1157 var intersects = tabElement.offsetLeft + tabElement.clientWidth > this._ta bElement.offsetLeft &&
994 1158 this._tabElement.offsetLeft + this._tabElement.clientWidth > tabElemen t.offsetLeft;
995 /** 1159 if (!intersects)
996 * @return {string|undefined} 1160 continue;
997 */ 1161
998 get tooltip() 1162 if (Math.abs(event.pageX - this._dragStartX) < tabElement.clientWidth / 2 + 5)
999 { 1163 break;
1000 return this._tooltip; 1164
1001 }, 1165 if (event.pageX - this._dragStartX > 0) {
1002 1166 tabElement = tabElement.nextSibling;
1003 set tooltip(tooltip) 1167 ++i;
1004 { 1168 }
1005 this._tooltip = tooltip; 1169
1006 if (this._titleElement) 1170 var oldOffsetLeft = this._tabElement.offsetLeft;
1007 this._titleElement.title = tooltip || ""; 1171 this._tabbedPane._insertBefore(this, i);
1008 }, 1172 this._dragStartX += this._tabElement.offsetLeft - oldOffsetLeft;
1009 1173 break;
1010 /** 1174 }
1011 * @return {!Element} 1175
1012 */ 1176 if (!this._tabElement.previousSibling && event.pageX - this._dragStartX < 0) {
1013 get tabElement() 1177 this._tabElement.style.setProperty('left', '0px');
1014 { 1178 return;
1015 if (!this._tabElement) 1179 }
1016 this._tabElement = this._createTabElement(false); 1180 if (!this._tabElement.nextSibling && event.pageX - this._dragStartX > 0) {
1017 1181 this._tabElement.style.setProperty('left', '0px');
1018 return this._tabElement; 1182 return;
1019 }, 1183 }
1020 1184
1021 /** 1185 this._tabElement.style.setProperty('left', (event.pageX - this._dragStartX) + 'px');
1022 * @return {number} 1186 }
1023 */ 1187
1024 width: function() 1188 /**
1025 { 1189 * @param {!Event} event
1026 return this._width; 1190 */
1027 }, 1191 _endTabDragging(event) {
1028 1192 this._tabElement.classList.remove('dragging');
1029 /** 1193 this._tabElement.style.removeProperty('left');
1030 * @param {number} width 1194 delete this._dragStartX;
1031 */ 1195 this._tabbedPane._updateTabSlider();
1032 setWidth: function(width) 1196 }
1033 {
1034 this.tabElement.style.width = width === -1 ? "" : (width + "px");
1035 this._width = width;
1036 },
1037
1038 /**
1039 * @param {!WebInspector.TabbedPaneTabDelegate} delegate
1040 */
1041 setDelegate: function(delegate)
1042 {
1043 this._delegate = delegate;
1044 },
1045
1046 /**
1047 * @param {!Element} tabElement
1048 * @param {!Element} titleElement
1049 */
1050 _createIconElement: function(tabElement, titleElement)
1051 {
1052 if (tabElement.__iconElement)
1053 tabElement.__iconElement.remove();
1054 if (!this._iconType)
1055 return;
1056
1057 var iconElement = createElementWithClass("label", "tabbed-pane-header-ta b-icon", "dt-icon-label");
1058 iconElement.type = this._iconType;
1059 if (this._iconTooltip)
1060 iconElement.title = this._iconTooltip;
1061 tabElement.insertBefore(iconElement, titleElement);
1062 tabElement.__iconElement = iconElement;
1063 },
1064
1065 /**
1066 * @param {boolean} measuring
1067 * @return {!Element}
1068 */
1069 _createTabElement: function(measuring)
1070 {
1071 var tabElement = createElementWithClass("div", "tabbed-pane-header-tab") ;
1072 tabElement.id = "tab-" + this._id;
1073 tabElement.tabIndex = -1;
1074 tabElement.setAttribute("role", "tab");
1075 tabElement.setAttribute("aria-selected", "false");
1076 tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabb edPane, this.id, true);
1077
1078 var titleElement = tabElement.createChild("span", "tabbed-pane-header-ta b-title");
1079 titleElement.textContent = this.title;
1080 titleElement.title = this.tooltip || "";
1081 this._createIconElement(tabElement, titleElement);
1082 if (!measuring)
1083 this._titleElement = titleElement;
1084
1085 if (this._closeable)
1086 tabElement.createChild("div", "tabbed-pane-close-button", "dt-close- button").gray = true;
1087
1088 if (measuring) {
1089 tabElement.classList.add("measuring");
1090 } else {
1091 tabElement.addEventListener("click", this._tabClicked.bind(this), fa lse);
1092 tabElement.addEventListener("auxclick", this._tabClicked.bind(this), false);
1093 tabElement.addEventListener("mousedown", this._tabMouseDown.bind(thi s), false);
1094 tabElement.addEventListener("mouseup", this._tabMouseUp.bind(this), false);
1095
1096 tabElement.addEventListener("contextmenu", this._tabContextMenu.bind (this), false);
1097 if (this._tabbedPane._allowTabReorder)
1098 WebInspector.installDragHandle(tabElement, this._startTabDraggin g.bind(this), this._tabDragging.bind(this), this._endTabDragging.bind(this), "-w ebkit-grabbing", "pointer", 200);
1099 }
1100
1101 return tabElement;
1102 },
1103
1104 /**
1105 * @param {!Event} event
1106 */
1107 _tabClicked: function(event)
1108 {
1109 var middleButton = event.button === 1;
1110 var shouldClose = this._closeable && (middleButton || event.target.class List.contains("tabbed-pane-close-button"));
1111 if (!shouldClose) {
1112 this._tabbedPane.focus();
1113 return;
1114 }
1115 this._closeTabs([this.id]);
1116 event.consume(true);
1117 },
1118
1119 /**
1120 * @param {!Event} event
1121 */
1122 _tabMouseDown: function(event)
1123 {
1124 if (event.target.classList.contains("tabbed-pane-close-button") || event .button === 1)
1125 return;
1126 this._tabbedPane.selectTab(this.id, true);
1127 },
1128
1129 /**
1130 * @param {!Event} event
1131 */
1132 _tabMouseUp: function(event)
1133 {
1134 // This is needed to prevent middle-click pasting on linux when tabs are clicked.
1135 if (event.button === 1)
1136 event.consume(true);
1137 },
1138
1139 /**
1140 * @param {!Array.<string>} ids
1141 */
1142 _closeTabs: function(ids)
1143 {
1144 if (this._delegate) {
1145 this._delegate.closeTabs(this._tabbedPane, ids);
1146 return;
1147 }
1148 this._tabbedPane.closeTabs(ids, true);
1149 },
1150
1151 _tabContextMenu: function(event)
1152 {
1153 /**
1154 * @this {WebInspector.TabbedPaneTab}
1155 */
1156 function close()
1157 {
1158 this._closeTabs([this.id]);
1159 }
1160
1161 /**
1162 * @this {WebInspector.TabbedPaneTab}
1163 */
1164 function closeOthers()
1165 {
1166 this._closeTabs(this._tabbedPane.otherTabs(this.id));
1167 }
1168
1169 /**
1170 * @this {WebInspector.TabbedPaneTab}
1171 */
1172 function closeAll()
1173 {
1174 this._closeTabs(this._tabbedPane.allTabs());
1175 }
1176
1177 /**
1178 * @this {WebInspector.TabbedPaneTab}
1179 */
1180 function closeToTheRight()
1181 {
1182 this._closeTabs(this._tabbedPane._tabsToTheRight(this.id));
1183 }
1184
1185 var contextMenu = new WebInspector.ContextMenu(event);
1186 if (this._closeable) {
1187 contextMenu.appendItem(WebInspector.UIString.capitalize("Close"), cl ose.bind(this));
1188 contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^othe rs"), closeOthers.bind(this));
1189 contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^tabs to the ^right"), closeToTheRight.bind(this));
1190 contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^all" ), closeAll.bind(this));
1191 }
1192 if (this._delegate)
1193 this._delegate.onContextMenu(this.id, contextMenu);
1194 contextMenu.show();
1195 },
1196
1197 /**
1198 * @param {!Event} event
1199 * @return {boolean}
1200 */
1201 _startTabDragging: function(event)
1202 {
1203 if (event.target.classList.contains("tabbed-pane-close-button"))
1204 return false;
1205 this._dragStartX = event.pageX;
1206 this._tabElement.classList.add("dragging");
1207 this._tabbedPane._tabSlider.remove();
1208 return true;
1209 },
1210
1211 /**
1212 * @param {!Event} event
1213 */
1214 _tabDragging: function(event)
1215 {
1216 var tabElements = this._tabbedPane._tabsElement.childNodes;
1217 for (var i = 0; i < tabElements.length; ++i) {
1218 var tabElement = tabElements[i];
1219 if (tabElement === this._tabElement)
1220 continue;
1221
1222 var intersects = tabElement.offsetLeft + tabElement.clientWidth > th is._tabElement.offsetLeft &&
1223 this._tabElement.offsetLeft + this._tabElement.clientWidth > tab Element.offsetLeft;
1224 if (!intersects)
1225 continue;
1226
1227 if (Math.abs(event.pageX - this._dragStartX) < tabElement.clientWidt h / 2 + 5)
1228 break;
1229
1230 if (event.pageX - this._dragStartX > 0) {
1231 tabElement = tabElement.nextSibling;
1232 ++i;
1233 }
1234
1235 var oldOffsetLeft = this._tabElement.offsetLeft;
1236 this._tabbedPane._insertBefore(this, i);
1237 this._dragStartX += this._tabElement.offsetLeft - oldOffsetLeft;
1238 break;
1239 }
1240
1241 if (!this._tabElement.previousSibling && event.pageX - this._dragStartX < 0) {
1242 this._tabElement.style.setProperty("left", "0px");
1243 return;
1244 }
1245 if (!this._tabElement.nextSibling && event.pageX - this._dragStartX > 0) {
1246 this._tabElement.style.setProperty("left", "0px");
1247 return;
1248 }
1249
1250 this._tabElement.style.setProperty("left", (event.pageX - this._dragStar tX) + "px");
1251 },
1252
1253 /**
1254 * @param {!Event} event
1255 */
1256 _endTabDragging: function(event)
1257 {
1258 this._tabElement.classList.remove("dragging");
1259 this._tabElement.style.removeProperty("left");
1260 delete this._dragStartX;
1261 this._tabbedPane._updateTabSlider();
1262 }
1263 }; 1197 };
1264 1198
1265 /** 1199 /**
1266 * @interface 1200 * @interface
1267 */ 1201 */
1268 WebInspector.TabbedPaneTabDelegate = function() 1202 WebInspector.TabbedPaneTabDelegate = function() {};
1269 { 1203
1204 WebInspector.TabbedPaneTabDelegate.prototype = {
1205 /**
1206 * @param {!WebInspector.TabbedPane} tabbedPane
1207 * @param {!Array.<string>} ids
1208 */
1209 closeTabs: function(tabbedPane, ids) {},
1210
1211 /**
1212 * @param {string} tabId
1213 * @param {!WebInspector.ContextMenu} contextMenu
1214 */
1215 onContextMenu: function(tabId, contextMenu) {}
1270 }; 1216 };
1271
1272 WebInspector.TabbedPaneTabDelegate.prototype = {
1273 /**
1274 * @param {!WebInspector.TabbedPane} tabbedPane
1275 * @param {!Array.<string>} ids
1276 */
1277 closeTabs: function(tabbedPane, ids) { },
1278
1279 /**
1280 * @param {string} tabId
1281 * @param {!WebInspector.ContextMenu} contextMenu
1282 */
1283 onContextMenu: function(tabId, contextMenu) { }
1284 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698