| OLD | NEW |
| 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 }; | |
| OLD | NEW |