зеркало из https://github.com/mozilla/gecko-dev.git
Bug 380960 - "Implement closing tabs animation" [p=dao@mozilla.com (Dão Gottwald) r=Mano ui-r+a1.9=beltzner]
This commit is contained in:
Родитель
1f43e361f6
Коммит
83bad62df0
|
@ -305,6 +305,7 @@ pref("browser.tabs.loadBookmarksInBackground", false);
|
||||||
pref("browser.tabs.tabMinWidth", 100);
|
pref("browser.tabs.tabMinWidth", 100);
|
||||||
pref("browser.tabs.tabMaxWidth", 250);
|
pref("browser.tabs.tabMaxWidth", 250);
|
||||||
pref("browser.tabs.tabClipWidth", 140);
|
pref("browser.tabs.tabClipWidth", 140);
|
||||||
|
pref("browser.tabs.closingAnimation", true);
|
||||||
|
|
||||||
// Where to show tab close buttons:
|
// Where to show tab close buttons:
|
||||||
// 0 on active tab only
|
// 0 on active tab only
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
- Simon Bünzli <zeniko@gmail.com>
|
- Simon Bünzli <zeniko@gmail.com>
|
||||||
- Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
|
- Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
|
||||||
- Mark Pilgrim <pilgrim@gmail.com>
|
- Mark Pilgrim <pilgrim@gmail.com>
|
||||||
|
- Dão Gottwald <dao@mozilla.com>
|
||||||
-
|
-
|
||||||
- Alternatively, the contents of this file may be used under the terms of
|
- Alternatively, the contents of this file may be used under the terms of
|
||||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -108,7 +109,7 @@
|
||||||
<xul:menuseparator/>
|
<xul:menuseparator/>
|
||||||
<xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
|
<xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
|
||||||
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
|
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
|
||||||
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
|
tabbrowser.removeTab(tabbrowser.mContextTab, true);"/>
|
||||||
</xul:menupopup>
|
</xul:menupopup>
|
||||||
|
|
||||||
<xul:tabs class="tabbrowser-tabs" flex="1"
|
<xul:tabs class="tabbrowser-tabs" flex="1"
|
||||||
|
@ -902,7 +903,7 @@
|
||||||
if (event.button != 1 || event.target.localName != 'tab')
|
if (event.button != 1 || event.target.localName != 'tab')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.removeTab(event.target);
|
this.removeTab(event.target, true);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1354,7 +1355,7 @@
|
||||||
|
|
||||||
for (var i = childNodes.length - 1; i >= 0; --i) {
|
for (var i = childNodes.length - 1; i >= 0; --i) {
|
||||||
if (childNodes[i] != aTab)
|
if (childNodes[i] != aTab)
|
||||||
this.removeTab(childNodes[i]);
|
this.removeTab(childNodes[i], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]]>
|
]]>
|
||||||
|
@ -1364,7 +1365,7 @@
|
||||||
<method name="removeCurrentTab">
|
<method name="removeCurrentTab">
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
return this.removeTab(this.mCurrentTab);
|
return this.removeTab(this.mCurrentTab, true);
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
@ -1385,36 +1386,34 @@
|
||||||
|
|
||||||
<method name="removeTab">
|
<method name="removeTab">
|
||||||
<parameter name="aTab"/>
|
<parameter name="aTab"/>
|
||||||
|
<parameter name="aUseAnimation"/>
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
this._browsers = null; // invalidate cache
|
if (aTab == null || aTab.localName != "tab" || aTab.control != this.mTabContainer)
|
||||||
if (aTab.localName != "tab")
|
|
||||||
aTab = this.mCurrentTab;
|
aTab = this.mCurrentTab;
|
||||||
|
|
||||||
var l = this.mTabContainer.childNodes.length;
|
|
||||||
if (l == 1 && this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
|
|
||||||
// hide the tab bar
|
|
||||||
this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
|
|
||||||
this.setStripVisibilityTo(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ds = this.getBrowserForTab(aTab).docShell;
|
var ds = this.getBrowserForTab(aTab).docShell;
|
||||||
if (ds.contentViewer && !ds.contentViewer.permitUnload())
|
if (ds.contentViewer && !ds.contentViewer.permitUnload())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// see notes in addTab
|
if (this._removingTabs) {
|
||||||
var _delayedUpdate = function(aTabContainer) {
|
if (this._removingTabs.indexOf(aTab) > -1)
|
||||||
aTabContainer.adjustTabstrip();
|
return;
|
||||||
aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
|
} else {
|
||||||
|
this._removingTabs = [];
|
||||||
}
|
}
|
||||||
setTimeout(_delayedUpdate, 0, this.mTabContainer);
|
|
||||||
|
|
||||||
|
var l = this.mTabContainer.childNodes.length - this._removingTabs.length;
|
||||||
if (l == 1) {
|
if (l == 1) {
|
||||||
|
if (this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
|
||||||
|
// hide the tab bar
|
||||||
|
this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
|
||||||
|
this.setStripVisibilityTo(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// add a new blank tab to replace the one we're about to close
|
// add a new blank tab to replace the one we're about to close
|
||||||
// (this ensures that the remaining tab is as good as new)
|
// (this ensures that the remaining tab is as good as new)
|
||||||
this.addTab("about:blank");
|
this.addTab("about:blank");
|
||||||
l++;
|
|
||||||
}
|
}
|
||||||
else if (l == 2) {
|
else if (l == 2) {
|
||||||
var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
|
var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
|
||||||
|
@ -1423,6 +1422,69 @@
|
||||||
this.setStripVisibilityTo(false);
|
this.setStripVisibilityTo(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (l == 1 ||
|
||||||
|
!aUseAnimation ||
|
||||||
|
this._removingTabs.length > 2 ||
|
||||||
|
!this.mPrefs.getBoolPref("browser.tabs.closingAnimation")) {
|
||||||
|
this._destroyTab(aTab);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._removingTabs.push(aTab);
|
||||||
|
this._blurTab(aTab);
|
||||||
|
|
||||||
|
aTab.width = aTab.boxObject.width;
|
||||||
|
aTab.minWidth = "";
|
||||||
|
aTab.flex = "";
|
||||||
|
|
||||||
|
function processFrame(tab, tabbrowser, animation) {
|
||||||
|
if (animation.opacities.length) {
|
||||||
|
tab.style.setProperty("opacity", animation.opacity * animation.opacities.shift(), "important");
|
||||||
|
tab.width = animation.width * animation.widths.shift();
|
||||||
|
tabbrowser._fillTrailingGap();
|
||||||
|
} else {
|
||||||
|
clearInterval(animation.id);
|
||||||
|
tabbrowser._removingTabs.splice(tabbrowser._removingTabs.indexOf(tab), 1);
|
||||||
|
tabbrowser._destroyTab(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var animation = {
|
||||||
|
opacity: parseFloat(document.defaultView.getComputedStyle(aTab, null).opacity),
|
||||||
|
width: aTab.width,
|
||||||
|
opacities: [.5, .25],
|
||||||
|
widths: [.85, .6]
|
||||||
|
};
|
||||||
|
animation.id = setInterval(processFrame, 40, aTab, this, animation);
|
||||||
|
processFrame(aTab, this, animation);
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_blurTab">
|
||||||
|
<parameter name="aTab"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
if (this.mCurrentTab == aTab) {
|
||||||
|
var newIndex = -1;
|
||||||
|
if ("owner" in aTab && aTab.owner &&
|
||||||
|
this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose"))
|
||||||
|
newIndex = aTab.owner._tPos;
|
||||||
|
if (newIndex == -1)
|
||||||
|
newIndex = (aTab == this.mTabContainer.lastChild) ? aTab._tPos - 1 : aTab._tPos + 1;
|
||||||
|
this.mTabContainer.selectedIndex = newIndex;
|
||||||
|
|
||||||
|
// clean up the before/afterselected attributes
|
||||||
|
aTab._selected = false;
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_destroyTab">
|
||||||
|
<parameter name="aTab"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
// We're committed to closing the tab now.
|
// We're committed to closing the tab now.
|
||||||
// Dispatch a notification.
|
// Dispatch a notification.
|
||||||
// We dispatch it before any teardown so that event listeners can
|
// We dispatch it before any teardown so that event listeners can
|
||||||
|
@ -1431,19 +1493,19 @@
|
||||||
evt.initEvent("TabClose", true, false);
|
evt.initEvent("TabClose", true, false);
|
||||||
aTab.dispatchEvent(evt);
|
aTab.dispatchEvent(evt);
|
||||||
|
|
||||||
var index = -1;
|
this._blurTab(aTab);
|
||||||
if (this.mCurrentTab == aTab)
|
|
||||||
index = this.mTabContainer.selectedIndex;
|
// Remove this tab as the owner of any other tabs, since it's going away.
|
||||||
else {
|
Array.forEach(this.mTabContainer.childNodes, function (tab) {
|
||||||
// Find and locate the tab in our list.
|
if ("owner" in tab && tab.owner == aTab)
|
||||||
for (var i = 0; i < l; i++)
|
// |tab| is a child of the tab we're removing, make it an orphan
|
||||||
if (this.mTabContainer.childNodes[i] == aTab)
|
tab.owner = null;
|
||||||
index = i;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the tab's filter and progress listener.
|
// Remove the tab's filter and progress listener.
|
||||||
|
var index = aTab._tPos;
|
||||||
const filter = this.mTabFilters[index];
|
const filter = this.mTabFilters[index];
|
||||||
var oldBrowser = this.getBrowserAtIndex(index);
|
var oldBrowser = this.getBrowserForTab(aTab);
|
||||||
oldBrowser.webProgress.removeProgressListener(filter);
|
oldBrowser.webProgress.removeProgressListener(filter);
|
||||||
filter.removeProgressListener(this.mTabListeners[index]);
|
filter.removeProgressListener(this.mTabListeners[index]);
|
||||||
this.mTabFilters.splice(index, 1);
|
this.mTabFilters.splice(index, 1);
|
||||||
|
@ -1455,21 +1517,11 @@
|
||||||
// We are no longer the primary content area.
|
// We are no longer the primary content area.
|
||||||
oldBrowser.setAttribute("type", "content-targetable");
|
oldBrowser.setAttribute("type", "content-targetable");
|
||||||
|
|
||||||
// Get the index of the tab we're removing before unselecting it
|
// see notes in addTab
|
||||||
var currentIndex = this.mTabContainer.selectedIndex;
|
setTimeout(function delayedUpdate(aTabContainer) {
|
||||||
|
aTabContainer.adjustTabstrip();
|
||||||
var oldTab = aTab;
|
aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
|
||||||
|
}, 0, this.mTabContainer);
|
||||||
// clean up the before/afterselected attributes before removing the tab
|
|
||||||
oldTab._selected = false;
|
|
||||||
|
|
||||||
// Remove this tab as the owner of any other tabs, since it's going away.
|
|
||||||
for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
|
|
||||||
var tab = this.mTabContainer.childNodes[i];
|
|
||||||
if ("owner" in tab && tab.owner == oldTab)
|
|
||||||
// |tab| is a child of the tab we're removing, make it an orphan
|
|
||||||
tab.owner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because of the way XBL works (fields just set JS
|
// Because of the way XBL works (fields just set JS
|
||||||
// properties on the element) and the code we have in place
|
// properties on the element) and the code we have in place
|
||||||
|
@ -1486,71 +1538,47 @@
|
||||||
if (oldBrowser == this.mCurrentBrowser)
|
if (oldBrowser == this.mCurrentBrowser)
|
||||||
this.mCurrentBrowser = null;
|
this.mCurrentBrowser = null;
|
||||||
|
|
||||||
|
this._browsers = null; // invalidate cache
|
||||||
|
|
||||||
// Remove the tab
|
// Remove the tab
|
||||||
this.mTabContainer.removeChild(oldTab);
|
this.mTabContainer.removeChild(aTab);
|
||||||
// invalidate cache, because mTabContainer is about to change
|
|
||||||
this._browsers = null;
|
|
||||||
this.mPanelContainer.removeChild(oldBrowser.parentNode);
|
this.mPanelContainer.removeChild(oldBrowser.parentNode);
|
||||||
|
this._fillTrailingGap();
|
||||||
|
|
||||||
try {
|
for (var i = aTab._tPos; i < this.mTabContainer.childNodes.length; i++)
|
||||||
// if we're at the right side (and not the logical end,
|
|
||||||
// which is why this works for both LTR and RTL)
|
|
||||||
// of the tabstrip, we need to ensure that we stay
|
|
||||||
// completely scrolled to the right side
|
|
||||||
var tabStrip = this.mTabContainer.mTabstrip;
|
|
||||||
var scrollPos = {};
|
|
||||||
tabStrip.scrollBoxObject.getPosition(scrollPos, {});
|
|
||||||
var scrolledSize = {};
|
|
||||||
tabStrip.scrollBoxObject.getScrolledSize(scrolledSize, {});
|
|
||||||
|
|
||||||
if (scrollPos.value + tabStrip.boxObject.width >=
|
|
||||||
scrolledSize.value) {
|
|
||||||
tabStrip.scrollByPixels(-1 * this.mTabContainer.firstChild
|
|
||||||
.boxObject.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the tab to select
|
|
||||||
var newIndex = -1;
|
|
||||||
if (currentIndex > index)
|
|
||||||
newIndex = currentIndex-1;
|
|
||||||
else if (currentIndex < index)
|
|
||||||
newIndex = currentIndex;
|
|
||||||
else {
|
|
||||||
if ("owner" in oldTab && oldTab.owner &&
|
|
||||||
this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
|
|
||||||
for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
|
|
||||||
tab = this.mTabContainer.childNodes[i];
|
|
||||||
if (tab == oldTab.owner) {
|
|
||||||
newIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newIndex == -1)
|
|
||||||
newIndex = (index == l - 1) ? index - 1 : index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the new tab
|
|
||||||
this.selectedTab = this.mTabContainer.childNodes[newIndex];
|
|
||||||
|
|
||||||
for (i = oldTab._tPos; i < this.mTabContainer.childNodes.length; i++) {
|
|
||||||
this.mTabContainer.childNodes[i]._tPos = i;
|
this.mTabContainer.childNodes[i]._tPos = i;
|
||||||
}
|
|
||||||
this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
|
|
||||||
this.mCurrentTab._selected = true;
|
|
||||||
|
|
||||||
|
this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
|
||||||
this.updateCurrentBrowser();
|
this.updateCurrentBrowser();
|
||||||
|
|
||||||
// see comment above destroy above
|
// see comment about destroy above
|
||||||
oldBrowser.focusedWindow = null;
|
oldBrowser.focusedWindow = null;
|
||||||
oldBrowser.focusedElement = null;
|
oldBrowser.focusedElement = null;
|
||||||
]]>
|
]]>
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name="_fillTrailingGap">
|
||||||
|
<body><![CDATA[
|
||||||
|
try {
|
||||||
|
// if we're at the right side (and not the logical end,
|
||||||
|
// which is why this works for both LTR and RTL)
|
||||||
|
// of the tabstrip, we need to ensure that we stay
|
||||||
|
// completely scrolled to the right side
|
||||||
|
var tabStrip = this.mTabContainer.mTabstrip;
|
||||||
|
var scrollPos = {};
|
||||||
|
tabStrip.scrollBoxObject.getPosition(scrollPos, {});
|
||||||
|
var scrolledSize = {};
|
||||||
|
tabStrip.scrollBoxObject.getScrolledSize(scrolledSize, {});
|
||||||
|
|
||||||
|
if (scrollPos.value + tabStrip.boxObject.width >=
|
||||||
|
scrolledSize.value) {
|
||||||
|
tabStrip.scrollByPixels(-1);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
<method name="reloadAllTabs">
|
<method name="reloadAllTabs">
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
@ -1884,7 +1912,7 @@
|
||||||
|
|
||||||
var remoteBrowser = draggedTab.ownerDocument.defaultView.getBrowser();
|
var remoteBrowser = draggedTab.ownerDocument.defaultView.getBrowser();
|
||||||
var tabCount = remoteBrowser.tabContainer.childNodes.length;
|
var tabCount = remoteBrowser.tabContainer.childNodes.length;
|
||||||
remoteBrowser.removeTab(draggedTab);
|
remoteBrowser.removeTab(draggedTab, false);
|
||||||
// close the other window if this was its last tab
|
// close the other window if this was its last tab
|
||||||
if (tabCount == 1)
|
if (tabCount == 1)
|
||||||
draggedTab.ownerDocument.defaultView.close();
|
draggedTab.ownerDocument.defaultView.close();
|
||||||
|
@ -2461,7 +2489,7 @@
|
||||||
var i = 0;
|
var i = 0;
|
||||||
for (; i < browsers.length; ++i) {
|
for (; i < browsers.length; ++i) {
|
||||||
if (this.getBrowserAtIndex(i).contentWindow == event.target) {
|
if (this.getBrowserAtIndex(i).contentWindow == event.target) {
|
||||||
this.removeTab(this.mTabContainer.childNodes[i]);
|
this.removeTab(this.mTabContainer.childNodes[i], false);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -2755,7 +2783,7 @@
|
||||||
this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
|
this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
<field name="_mPrefs">null</field>
|
<field name="_mPrefs">null</field>
|
||||||
<property name="mPrefs" readonly="true">
|
<property name="mPrefs" readonly="true">
|
||||||
<getter>
|
<getter>
|
||||||
|
@ -2769,7 +2797,7 @@
|
||||||
]]>
|
]]>
|
||||||
</getter>
|
</getter>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
<method name="_handleTabSelect">
|
<method name="_handleTabSelect">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
this.mTabstrip.ensureElementIsVisible(this.selectedItem);
|
this.mTabstrip.ensureElementIsVisible(this.selectedItem);
|
||||||
|
@ -2792,8 +2820,7 @@
|
||||||
var width = this.mTabstrip.boxObject.width;
|
var width = this.mTabstrip.boxObject.width;
|
||||||
if (width != this.mTabstripWidth) {
|
if (width != this.mTabstripWidth) {
|
||||||
this.adjustTabstrip();
|
this.adjustTabstrip();
|
||||||
// XXX without this line the tab bar won't budge
|
this._fillTrailingGap();
|
||||||
this.mTabstrip.scrollByPixels(1);
|
|
||||||
this._handleTabSelect();
|
this._handleTabSelect();
|
||||||
this.mTabstripWidth = width;
|
this.mTabstripWidth = width;
|
||||||
}
|
}
|
||||||
|
@ -3178,7 +3205,7 @@
|
||||||
// Reset the "ignored click" flag
|
// Reset the "ignored click" flag
|
||||||
this._ignoredClick = false;
|
this._ignoredClick = false;
|
||||||
|
|
||||||
tabbedBrowser.removeTab(bindingParent);
|
tabbedBrowser.removeTab(bindingParent, true);
|
||||||
tabbedBrowser._blockDblClick = true;
|
tabbedBrowser._blockDblClick = true;
|
||||||
|
|
||||||
/* XXXmano hack (see bug 343628):
|
/* XXXmano hack (see bug 343628):
|
||||||
|
|
Загрузка…
Ссылка в новой задаче