diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/TabBar.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/TabBar.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Views/TabBar.js | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/TabBar.js b/Source/WebInspectorUI/UserInterface/Views/TabBar.js new file mode 100644 index 000000000..d79393d8a --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Views/TabBar.js @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.TabBar = class TabBar extends WebInspector.View +{ + constructor(element, tabBarItems) + { + super(element); + + this.element.classList.add("tab-bar"); + this.element.setAttribute("role", "tablist"); + this.element.addEventListener("mousedown", this._handleMouseDown.bind(this)); + this.element.addEventListener("click", this._handleClick.bind(this)); + this.element.addEventListener("mouseleave", this._handleMouseLeave.bind(this)); + + this.element.createChild("div", "top-border"); + + this._tabBarItems = []; + + if (tabBarItems) { + for (let tabBarItem in tabBarItems) + this.addTabBarItem(tabBarItem); + } + + this.addTabBarItem(WebInspector.settingsTabContentView.tabBarItem, true); + + this._newTabTabBarItem = new WebInspector.PinnedTabBarItem("Images/NewTabPlus.svg", WebInspector.UIString("Create a new tab")); + this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this)); + this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this)); + this.addTabBarItem(this._newTabTabBarItem, true); + } + + // Public + + get newTabTabBarItem() { return this._newTabTabBarItem; } + + updateNewTabTabBarItemState() + { + let newTabExists = !WebInspector.isNewTabWithTypeAllowed(WebInspector.NewTabContentView.Type); + this._newTabTabBarItem.disabled = newTabExists; + } + + addTabBarItem(tabBarItem, doNotAnimate) + { + return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, doNotAnimate); + } + + insertTabBarItem(tabBarItem, index, doNotAnimate) + { + console.assert(tabBarItem instanceof WebInspector.TabBarItem); + if (!(tabBarItem instanceof WebInspector.TabBarItem)) + return null; + + if (tabBarItem.parentTabBar === this) + return; + + if (this._tabAnimatedClosedSinceMouseEnter) { + // Delay adding the new tab until we can expand the tabs after a closed tab. + this._finishExpandingTabsAfterClose().then(() => { + this.insertTabBarItem(tabBarItem, index, doNotAnimate); + }); + return; + } + + if (tabBarItem.parentTabBar) + tabBarItem.parentTabBar.removeTabBarItem(tabBarItem); + + tabBarItem.parentTabBar = this; + + index = Number.constrain(index, 0, this.normalTabCount); + + if (this.element.classList.contains("animating")) { + requestAnimationFrame(removeStyles.bind(this)); + doNotAnimate = true; + } + + var beforeTabSizesAndPositions; + if (!doNotAnimate) + beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions(); + + this._tabBarItems.splice(index, 0, tabBarItem); + + var nextSibling = this._tabBarItems[index + 1]; + let nextSiblingElement = nextSibling ? nextSibling.element : this._tabBarItems.lastValue.element; + + if (this.element.isAncestor(nextSiblingElement)) + this.element.insertBefore(tabBarItem.element, nextSiblingElement); + else + this.element.appendChild(tabBarItem.element); + + this.element.classList.toggle("single-tab", !this._hasMoreThanOneNormalTab()); + + tabBarItem.element.style.left = null; + tabBarItem.element.style.width = null; + + function animateTabs() + { + this.element.classList.add("animating"); + this.element.classList.add("inserting-tab"); + + this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions); + + this.element.addEventListener("webkitTransitionEnd", removeStylesListener); + } + + function removeStyles() + { + this.element.classList.remove("static-layout"); + this.element.classList.remove("animating"); + this.element.classList.remove("inserting-tab"); + + tabBarItem.element.classList.remove("being-inserted"); + + this._clearTabBarItemSizesAndPositions(); + + this.element.removeEventListener("webkitTransitionEnd", removeStylesListener); + } + + if (!doNotAnimate) { + var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions(); + + this.updateLayout(); + + let tabBarItems = this._tabBarItemsFromLeftToRight(); + let previousTabBarItem = tabBarItems[tabBarItems.indexOf(tabBarItem) - 1] || null; + let previousTabBarItemSizeAndPosition = previousTabBarItem ? beforeTabSizesAndPositions.get(previousTabBarItem) : null; + + if (previousTabBarItemSizeAndPosition) + beforeTabSizesAndPositions.set(tabBarItem, {left: previousTabBarItemSizeAndPosition.left + previousTabBarItemSizeAndPosition.width, width: 0}); + else + beforeTabSizesAndPositions.set(tabBarItem, {left: 0, width: 0}); + + this.element.classList.add("static-layout"); + tabBarItem.element.classList.add("being-inserted"); + + this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions); + + var removeStylesListener = removeStyles.bind(this); + + requestAnimationFrame(animateTabs.bind(this)); + } else + this.needsLayout(); + + if (!(tabBarItem instanceof WebInspector.PinnedTabBarItem)) + this.updateNewTabTabBarItemState(); + + this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemAdded, {tabBarItem}); + + return tabBarItem; + } + + removeTabBarItem(tabBarItemOrIndex, doNotAnimate, doNotExpand) + { + let tabBarItem = this._findTabBarItem(tabBarItemOrIndex); + if (!tabBarItem || tabBarItem instanceof WebInspector.PinnedTabBarItem) + return null; + + tabBarItem.parentTabBar = null; + + if (this._selectedTabBarItem === tabBarItem) { + var index = this._tabBarItems.indexOf(tabBarItem); + var nextTabBarItem = this._tabBarItems[index + 1]; + if (!nextTabBarItem || nextTabBarItem instanceof WebInspector.PinnedTabBarItem) + nextTabBarItem = this._tabBarItems[index - 1]; + + this.selectedTabBarItem = nextTabBarItem; + } + + if (this.element.classList.contains("animating")) { + requestAnimationFrame(removeStyles.bind(this)); + doNotAnimate = true; + } + + var beforeTabSizesAndPositions; + if (!doNotAnimate) + beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions(); + + // Subtract 1 from normalTabCount since arrays begin indexing at 0. + let wasLastNormalTab = this._tabBarItems.indexOf(tabBarItem) === this.normalTabCount - 1; + + this._tabBarItems.remove(tabBarItem); + tabBarItem.element.remove(); + + var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab(); + this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab); + + const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount; + if (shouldOpenDefaultTab) + doNotAnimate = true; + + if (!hasMoreThanOneNormalTab || wasLastNormalTab || !doNotExpand) { + if (!doNotAnimate) { + this._tabAnimatedClosedSinceMouseEnter = true; + this._finishExpandingTabsAfterClose(beforeTabSizesAndPositions); + } else + this.needsLayout(); + + this.updateNewTabTabBarItemState(); + + this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemRemoved, {tabBarItem}); + + if (shouldOpenDefaultTab) + this._openDefaultTab(); + + return tabBarItem; + } + + var lastNormalTabBarItem; + + function animateTabs() + { + this.element.classList.add("animating"); + this.element.classList.add("closing-tab"); + + // For RTL, we need to place extra space between pinned tab and first normal tab. + // From left to right there is pinned tabs, extra space, then normal tabs. Compute + // how much extra space we need to additionally add for normal tab items. + let extraSpaceBetweenNormalAndPinnedTabs = 0; + if (WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.RTL) { + extraSpaceBetweenNormalAndPinnedTabs = this.element.getBoundingClientRect().width; + for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) + extraSpaceBetweenNormalAndPinnedTabs -= currentTabBarItem.element.getBoundingClientRect().width; + } + + let left = 0; + for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) { + let sizeAndPosition = beforeTabSizesAndPositions.get(currentTabBarItem); + + if (!(currentTabBarItem instanceof WebInspector.PinnedTabBarItem)) { + currentTabBarItem.element.style.left = extraSpaceBetweenNormalAndPinnedTabs + left + "px"; + left += sizeAndPosition.width; + lastNormalTabBarItem = currentTabBarItem; + } else + left = sizeAndPosition.left + sizeAndPosition.width; + } + + // The selected tab and last tab need to draw a right border as well, so make them 1px wider. + if (this._selectedTabBarItem) + this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) + 1) + "px"; + + if (lastNormalTabBarItem !== this._selectedTabBarItem) + lastNormalTabBarItem.element.style.width = (parseFloat(lastNormalTabBarItem.element.style.width) + 1) + "px"; + + this.element.addEventListener("webkitTransitionEnd", removeStylesListener); + } + + function removeStyles() + { + // The selected tab needs to stop drawing the right border, so make it 1px smaller. Only if it isn't the last. + if (this._selectedTabBarItem && this._selectedTabBarItem !== lastNormalTabBarItem) + this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) - 1) + "px"; + + this.element.classList.remove("animating"); + this.element.classList.remove("closing-tab"); + + this.updateLayout(); + + this.element.removeEventListener("webkitTransitionEnd", removeStylesListener); + } + + if (!doNotAnimate) { + this.element.classList.add("static-layout"); + + this._tabAnimatedClosedSinceMouseEnter = true; + + this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions); + + var removeStylesListener = removeStyles.bind(this); + + requestAnimationFrame(animateTabs.bind(this)); + } else + this.needsLayout(); + + this.updateNewTabTabBarItemState(); + + this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemRemoved, {tabBarItem}); + + if (shouldOpenDefaultTab) + this._openDefaultTab(); + + return tabBarItem; + } + + selectPreviousTab() + { + if (this._tabBarItems.length <= 1) + return; + + var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem); + var newIndex = startIndex; + do { + if (newIndex === 0) + newIndex = this._tabBarItems.length - 1; + else + newIndex--; + + if (!(this._tabBarItems[newIndex] instanceof WebInspector.PinnedTabBarItem)) + break; + } while (newIndex !== startIndex); + + if (newIndex === startIndex) + return; + + this.selectedTabBarItem = this._tabBarItems[newIndex]; + } + + selectNextTab() + { + if (this._tabBarItems.length <= 1) + return; + + var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem); + var newIndex = startIndex; + do { + if (newIndex === this._tabBarItems.length - 1) + newIndex = 0; + else + newIndex++; + + if (!(this._tabBarItems[newIndex] instanceof WebInspector.PinnedTabBarItem)) + break; + } while (newIndex !== startIndex); + + if (newIndex === startIndex) + return; + + this.selectedTabBarItem = this._tabBarItems[newIndex]; + } + + get selectedTabBarItem() + { + return this._selectedTabBarItem; + } + + set selectedTabBarItem(tabBarItemOrIndex) + { + let tabBarItem = this._findTabBarItem(tabBarItemOrIndex); + if (tabBarItem === this._newTabTabBarItem) { + // Get the item before the New-Tab item since it is not selectable. + tabBarItem = this._tabBarItems[this.normalTabCount - 1]; + } + + if (this._selectedTabBarItem === tabBarItem) + return; + + if (this._selectedTabBarItem) + this._selectedTabBarItem.selected = false; + + this._selectedTabBarItem = tabBarItem || null; + + if (this._selectedTabBarItem) + this._selectedTabBarItem.selected = true; + + this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemSelected); + } + + get tabBarItems() + { + return this._tabBarItems; + } + + get normalTabCount() + { + return this._tabBarItems.filter((item) => !(item instanceof WebInspector.PinnedTabBarItem)).length; + } + + // Protected + + layout() + { + if (this.element.classList.contains("static-layout")) + return; + + this.element.classList.remove("hide-titles"); + this.element.classList.remove("collapsed"); + + let firstNormalTabItem = null; + for (let tabItem of this._tabBarItems) { + if (tabItem instanceof WebInspector.PinnedTabBarItem) + continue; + + firstNormalTabItem = tabItem; + break; + } + + if (!firstNormalTabItem) + return; + + if (firstNormalTabItem.element.offsetWidth >= 120) + return; + + this.element.classList.add("collapsed"); + + if (firstNormalTabItem.element.offsetWidth >= 75) + return; + + this.element.classList.add("hide-titles"); + } + + // Private + + _tabBarItemsFromLeftToRight() + { + return WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.LTR ? this._tabBarItems : this._tabBarItems.slice().reverse(); + } + + _findTabBarItem(tabBarItemOrIndex) + { + if (typeof tabBarItemOrIndex === "number") + return this._tabBarItems[tabBarItemOrIndex] || null; + + if (tabBarItemOrIndex instanceof WebInspector.TabBarItem) { + if (this._tabBarItems.includes(tabBarItemOrIndex)) + return tabBarItemOrIndex; + } + + return null; + } + + _hasMoreThanOneNormalTab() + { + let normalTabCount = 0; + for (let tabBarItem of this._tabBarItems) { + if (tabBarItem instanceof WebInspector.PinnedTabBarItem) + continue; + + ++normalTabCount; + if (normalTabCount >= 2) + return true; + } + + return false; + } + + _openDefaultTab() + { + this.dispatchEventToListeners(WebInspector.TabBar.Event.OpenDefaultTab); + } + + _recordTabBarItemSizesAndPositions() + { + var tabBarItemSizesAndPositions = new Map; + + const barRect = this.element.getBoundingClientRect(); + + for (var tabBarItem of this._tabBarItems) { + var boundingRect = tabBarItem.element.getBoundingClientRect(); + tabBarItemSizesAndPositions.set(tabBarItem, {left: boundingRect.left - barRect.left, width: boundingRect.width}); + } + + return tabBarItemSizesAndPositions; + } + + _applyTabBarItemSizesAndPositions(tabBarItemSizesAndPositions, skipTabBarItem) + { + for (var [tabBarItem, sizeAndPosition] of tabBarItemSizesAndPositions) { + if (skipTabBarItem && tabBarItem === skipTabBarItem) + continue; + tabBarItem.element.style.left = sizeAndPosition.left + "px"; + tabBarItem.element.style.width = sizeAndPosition.width + "px"; + } + } + + _clearTabBarItemSizesAndPositions(skipTabBarItem) + { + for (var tabBarItem of this._tabBarItems) { + if (skipTabBarItem && tabBarItem === skipTabBarItem) + continue; + tabBarItem.element.style.left = null; + tabBarItem.element.style.width = null; + } + } + + _finishExpandingTabsAfterClose(beforeTabSizesAndPositions) + { + return new Promise(function(resolve, reject) { + console.assert(this._tabAnimatedClosedSinceMouseEnter); + this._tabAnimatedClosedSinceMouseEnter = false; + + if (!beforeTabSizesAndPositions) + beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions(); + + this.element.classList.remove("static-layout"); + this._clearTabBarItemSizesAndPositions(); + + var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions(); + + this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions); + this.element.classList.add("static-layout"); + + function animateTabs() + { + this.element.classList.add("static-layout"); + this.element.classList.add("animating"); + this.element.classList.add("expanding-tabs"); + + this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions); + + this.element.addEventListener("webkitTransitionEnd", removeStylesListener); + } + + function removeStyles() + { + this.element.classList.remove("static-layout"); + this.element.classList.remove("animating"); + this.element.classList.remove("expanding-tabs"); + + this._clearTabBarItemSizesAndPositions(); + + this.updateLayout(); + + this.element.removeEventListener("webkitTransitionEnd", removeStylesListener); + + resolve(); + } + + var removeStylesListener = removeStyles.bind(this); + + requestAnimationFrame(animateTabs.bind(this)); + }.bind(this)); + } + + _handleMouseDown(event) + { + // Only consider left mouse clicks for tab movement. + if (event.button !== 0 || event.ctrlKey) + return; + + let itemElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.StyleClassName); + if (!itemElement) + return; + + let tabBarItem = itemElement[WebInspector.TabBarItem.ElementReferenceSymbol]; + if (!tabBarItem) + return; + + if (tabBarItem.disabled) + return; + + if (tabBarItem === this._newTabTabBarItem) + return; + + let closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.CloseButtonStyleClassName); + if (closeButtonElement) + return; + + this.selectedTabBarItem = tabBarItem; + + if (tabBarItem instanceof WebInspector.PinnedTabBarItem || !this._hasMoreThanOneNormalTab()) + return; + + this._firstNormalTabItemIndex = 0; + for (let i = 0; i < this._tabBarItems.length; ++i) { + if (this._tabBarItems[i] instanceof WebInspector.PinnedTabBarItem) + continue; + + this._firstNormalTabItemIndex = i; + break; + } + + this._mouseIsDown = true; + + this._mouseMovedEventListener = this._handleMouseMoved.bind(this); + this._mouseUpEventListener = this._handleMouseUp.bind(this); + + // Register these listeners on the document so we can track the mouse if it leaves the tab bar. + document.addEventListener("mousemove", this._mouseMovedEventListener, true); + document.addEventListener("mouseup", this._mouseUpEventListener, true); + + event.preventDefault(); + event.stopPropagation(); + } + + _handleClick(event) + { + var itemElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.StyleClassName); + if (!itemElement) + return; + + var tabBarItem = itemElement[WebInspector.TabBarItem.ElementReferenceSymbol]; + if (!tabBarItem) + return; + + if (tabBarItem.disabled) + return; + + const clickedMiddleButton = event.button === 1; + + var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.CloseButtonStyleClassName); + if (closeButtonElement || clickedMiddleButton) { + // Disallow closing the default tab if it is the only tab. + if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab")) + return; + + this.removeTabBarItem(tabBarItem, false, true); + } + } + + _handleMouseMoved(event) + { + console.assert(event.button === 0); + console.assert(this._mouseIsDown); + if (!this._mouseIsDown) + return; + + console.assert(this._selectedTabBarItem); + if (!this._selectedTabBarItem) + return; + + event.preventDefault(); + event.stopPropagation(); + + if (!this.element.classList.contains("static-layout")) { + this._applyTabBarItemSizesAndPositions(this._recordTabBarItemSizesAndPositions()); + this.element.classList.add("static-layout"); + this.element.classList.add("dragging-tab"); + } + + if (this._mouseOffset === undefined) + this._mouseOffset = event.pageX - this._selectedTabBarItem.element.totalOffsetLeft; + + var tabBarMouseOffset = event.pageX - this.element.totalOffsetLeft; + var newLeft = tabBarMouseOffset - this._mouseOffset; + + this._selectedTabBarItem.element.style.left = newLeft + "px"; + + var selectedTabMidX = newLeft + (this._selectedTabBarItem.element.realOffsetWidth / 2); + + var currentIndex = this._tabBarItems.indexOf(this._selectedTabBarItem); + var newIndex = currentIndex; + + for (let tabBarItem of this._tabBarItems) { + if (tabBarItem === this._selectedTabBarItem) + continue; + + var tabBarItemRect = tabBarItem.element.getBoundingClientRect(); + + if (selectedTabMidX < tabBarItemRect.left || selectedTabMidX > tabBarItemRect.right) + continue; + + newIndex = this._tabBarItems.indexOf(tabBarItem); + break; + } + + // Subtract 1 from normalTabCount since arrays begin indexing at 0. + newIndex = Number.constrain(newIndex, this._firstNormalTabItemIndex, this.normalTabCount - 1); + + if (currentIndex === newIndex) + return; + + this._tabBarItems.splice(currentIndex, 1); + this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem); + + let nextSibling = this._tabBarItems[newIndex + 1]; + let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element; + + this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement); + + // FIXME: Animate the tabs that move to make room for the selected tab. This was causing me trouble when I tried. + + let left = 0; + for (let tabBarItem of this._tabBarItemsFromLeftToRight()) { + if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left) + tabBarItem.element.style.left = left + "px"; + left += parseFloat(tabBarItem.element.style.width); + } + } + + _handleMouseUp(event) + { + console.assert(event.button === 0); + console.assert(this._mouseIsDown); + if (!this._mouseIsDown) + return; + + this.element.classList.remove("dragging-tab"); + + if (!this._tabAnimatedClosedSinceMouseEnter) { + this.element.classList.remove("static-layout"); + this._clearTabBarItemSizesAndPositions(); + } else { + let left = 0; + for (let tabBarItem of this._tabBarItemsFromLeftToRight()) { + if (tabBarItem === this._selectedTabBarItem) + tabBarItem.element.style.left = left + "px"; + left += parseFloat(tabBarItem.element.style.width); + } + } + + this._mouseIsDown = false; + this._mouseOffset = undefined; + + document.removeEventListener("mousemove", this._mouseMovedEventListener, true); + document.removeEventListener("mouseup", this._mouseUpEventListener, true); + + this._mouseMovedEventListener = null; + this._mouseUpEventListener = null; + + event.preventDefault(); + event.stopPropagation(); + + this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemsReordered); + } + + _handleMouseLeave(event) + { + if (this._mouseIsDown || !this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating")) + return; + + // This event can still fire when the mouse is inside the element if DOM nodes are added, removed or generally change inside. + // Check if the mouse really did leave the element by checking the bounds. + // FIXME: Is this a WebKit bug or correct behavior? + const barRect = this.element.getBoundingClientRect(); + const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect(); + if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right)) + return; + + this._finishExpandingTabsAfterClose(); + } + + _handleNewTabClick(event) + { + const shouldAnimate = true; + WebInspector.showNewTabTab(shouldAnimate); + } + + _handleNewTabMouseEnter(event) + { + if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating")) + return; + + this._finishExpandingTabsAfterClose(); + } +}; + +WebInspector.TabBar.Event = { + TabBarItemSelected: "tab-bar-tab-bar-item-selected", + TabBarItemAdded: "tab-bar-tab-bar-item-added", + TabBarItemRemoved: "tab-bar-tab-bar-item-removed", + TabBarItemsReordered: "tab-bar-tab-bar-items-reordered", + OpenDefaultTab: "tab-bar-open-default-tab" +}; |