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.tabMaxWidth", 250);
pref("browser.tabs.tabClipWidth", 140);
pref("browser.tabs.closingAnimation", true);
// Where to show tab close buttons:
// 0 on active tab only

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

@ -30,6 +30,7 @@
- Simon Bünzli <zeniko@gmail.com>
- Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
- Mark Pilgrim <pilgrim@gmail.com>
- Dão Gottwald <dao@mozilla.com>
-
- 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
@ -108,7 +109,7 @@
<xul:menuseparator/>
<xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
tabbrowser.removeTab(tabbrowser.mContextTab, true);"/>
</xul:menupopup>
<xul:tabs class="tabbrowser-tabs" flex="1"
@ -902,7 +903,7 @@
if (event.button != 1 || event.target.localName != 'tab')
return;
this.removeTab(event.target);
this.removeTab(event.target, true);
event.stopPropagation();
]]>
</body>
@ -1354,7 +1355,7 @@
for (var i = childNodes.length - 1; i >= 0; --i) {
if (childNodes[i] != aTab)
this.removeTab(childNodes[i]);
this.removeTab(childNodes[i], true);
}
}
]]>
@ -1364,7 +1365,7 @@
<method name="removeCurrentTab">
<body>
<![CDATA[
return this.removeTab(this.mCurrentTab);
return this.removeTab(this.mCurrentTab, true);
]]>
</body>
</method>
@ -1385,36 +1386,34 @@
<method name="removeTab">
<parameter name="aTab"/>
<parameter name="aUseAnimation"/>
<body>
<![CDATA[
this._browsers = null; // invalidate cache
if (aTab.localName != "tab")
if (aTab == null || aTab.localName != "tab" || aTab.control != this.mTabContainer)
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;
if (ds.contentViewer && !ds.contentViewer.permitUnload())
return;
// see notes in addTab
var _delayedUpdate = function(aTabContainer) {
aTabContainer.adjustTabstrip();
aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
if (this._removingTabs) {
if (this._removingTabs.indexOf(aTab) > -1)
return;
} else {
this._removingTabs = [];
}
setTimeout(_delayedUpdate, 0, this.mTabContainer);
var l = this.mTabContainer.childNodes.length - this._removingTabs.length;
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
// (this ensures that the remaining tab is as good as new)
this.addTab("about:blank");
l++;
}
else if (l == 2) {
var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
@ -1423,6 +1422,69 @@
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.
// Dispatch a notification.
// We dispatch it before any teardown so that event listeners can
@ -1431,19 +1493,19 @@
evt.initEvent("TabClose", true, false);
aTab.dispatchEvent(evt);
var index = -1;
if (this.mCurrentTab == aTab)
index = this.mTabContainer.selectedIndex;
else {
// Find and locate the tab in our list.
for (var i = 0; i < l; i++)
if (this.mTabContainer.childNodes[i] == aTab)
index = i;
}
this._blurTab(aTab);
// Remove this tab as the owner of any other tabs, since it's going away.
Array.forEach(this.mTabContainer.childNodes, function (tab) {
if ("owner" in tab && tab.owner == aTab)
// |tab| is a child of the tab we're removing, make it an orphan
tab.owner = null;
});
// Remove the tab's filter and progress listener.
var index = aTab._tPos;
const filter = this.mTabFilters[index];
var oldBrowser = this.getBrowserAtIndex(index);
var oldBrowser = this.getBrowserForTab(aTab);
oldBrowser.webProgress.removeProgressListener(filter);
filter.removeProgressListener(this.mTabListeners[index]);
this.mTabFilters.splice(index, 1);
@ -1455,21 +1517,11 @@
// We are no longer the primary content area.
oldBrowser.setAttribute("type", "content-targetable");
// Get the index of the tab we're removing before unselecting it
var currentIndex = this.mTabContainer.selectedIndex;
var oldTab = aTab;
// 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;
}
// see notes in addTab
setTimeout(function delayedUpdate(aTabContainer) {
aTabContainer.adjustTabstrip();
aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
}, 0, this.mTabContainer);
// Because of the way XBL works (fields just set JS
// properties on the element) and the code we have in place
@ -1486,71 +1538,47 @@
if (oldBrowser == this.mCurrentBrowser)
this.mCurrentBrowser = null;
this._browsers = null; // invalidate cache
// Remove the tab
this.mTabContainer.removeChild(oldTab);
// invalidate cache, because mTabContainer is about to change
this._browsers = null;
this.mTabContainer.removeChild(aTab);
this.mPanelContainer.removeChild(oldBrowser.parentNode);
this._fillTrailingGap();
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 * 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++) {
for (var i = aTab._tPos; i < this.mTabContainer.childNodes.length; 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();
// see comment above destroy above
// see comment about destroy above
oldBrowser.focusedWindow = null;
oldBrowser.focusedElement = null;
]]>
</body>
</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">
<body>
<![CDATA[
@ -1884,7 +1912,7 @@
var remoteBrowser = draggedTab.ownerDocument.defaultView.getBrowser();
var tabCount = remoteBrowser.tabContainer.childNodes.length;
remoteBrowser.removeTab(draggedTab);
remoteBrowser.removeTab(draggedTab, false);
// close the other window if this was its last tab
if (tabCount == 1)
draggedTab.ownerDocument.defaultView.close();
@ -2461,7 +2489,7 @@
var i = 0;
for (; i < browsers.length; ++i) {
if (this.getBrowserAtIndex(i).contentWindow == event.target) {
this.removeTab(this.mTabContainer.childNodes[i]);
this.removeTab(this.mTabContainer.childNodes[i], false);
event.preventDefault();
break;
@ -2755,7 +2783,7 @@
this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
]]></body>
</method>
<field name="_mPrefs">null</field>
<property name="mPrefs" readonly="true">
<getter>
@ -2769,7 +2797,7 @@
]]>
</getter>
</property>
<method name="_handleTabSelect">
<body><![CDATA[
this.mTabstrip.ensureElementIsVisible(this.selectedItem);
@ -2792,8 +2820,7 @@
var width = this.mTabstrip.boxObject.width;
if (width != this.mTabstripWidth) {
this.adjustTabstrip();
// XXX without this line the tab bar won't budge
this.mTabstrip.scrollByPixels(1);
this._fillTrailingGap();
this._handleTabSelect();
this.mTabstripWidth = width;
}
@ -3178,7 +3205,7 @@
// Reset the "ignored click" flag
this._ignoredClick = false;
tabbedBrowser.removeTab(bindingParent);
tabbedBrowser.removeTab(bindingParent, true);
tabbedBrowser._blockDblClick = true;
/* XXXmano hack (see bug 343628):