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:
reed@reedloden.com 2008-03-19 23:41:48 -07:00
Родитель 1f43e361f6
Коммит 83bad62df0
2 изменённых файлов: 132 добавлений и 104 удалений

Просмотреть файл

@ -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):