308396 - UE improvements for tabbed browsing in response to usability studies conducted. In particular, add close buttons to tabs, improve the reselection behavior of new tabs opened especially in single window mode, simplify the preferences available for tabs, and improve the visual appearance of keyboard-focused tabs. r=mconnor.

This commit is contained in:
beng%bengoodger.com 2007-08-22 05:04:10 +00:00
Родитель 0a76a4fe5c
Коммит 9113e743df
2 изменённых файлов: 276 добавлений и 26 удалений

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

@ -43,6 +43,8 @@
<!DOCTYPE bindings [
<!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
%tabBrowserDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<bindings id="tabBrowserBindings"
@ -67,7 +69,7 @@
ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
<xul:tooltip onpopupshowing="event.preventBubble(); if (document.tooltipNode.hasAttribute('label')) { this.setAttribute('label', document.tooltipNode.getAttribute('label')); return true; } return false;"/>
<xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
<xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
<xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
xbl:inherits="oncommand=onnewtab"/>
@ -90,8 +92,7 @@
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
</xul:menupopup>
<xul:tabs class="tabbrowser-tabs" closebutton="true" flex="1"
setfocus="false"
<xul:tabs class="tabbrowser-tabs" flex="1" setfocus="false"
onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
xbl:inherits="onnewtab"
ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
@ -1010,7 +1011,8 @@
<parameter name="aLoadInBackground"/>
<body>
<![CDATA[
var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData);
var owner = aLoadInBackground ? null : this.selectedTab;
var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner);
var bgLoad = (typeof(aLoadInBackground) != "undefined") ? aLoadInBackground :
this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
// Set newly selected tab after quick timeout, otherwise hideous focus problems
@ -1021,16 +1023,55 @@
}
setTimeout(selectNewForegroundTab, 0, getBrowser(), tab);
}
if (!bgLoad)
this.selectedTab = tab;
return tab;
]]>
</body>
</method>
<method name="loadTabs">
<parameter name="aURIs"/>
<parameter name="aLoadInBackground"/>
<parameter name="aReplace"/>
<body><![CDATA[
// The tab selected after this new tab is closed (i.e. the new tab's
// "owner") is the next adjacent tab (i.e. not the previously viewed tab)
// when several urls are opened here (i.e. closing the first should select
// the next of many URLs opened) or if the pref to have UI links opened in
// the background is set (i.e. the link is not being opened modally)
//
// i.e.
// Number of URLs Load UI Links in BG Focus Last Viewed?
// == 1 false YES
// == 1 true NO
// > 1 false/true NO
var owner = (aURIs.length > 1) || aLoadInBackground ? null : gBrowser.selectedTab;
var firstTabAdded = null;
if (aReplace)
this.loadURI(aURIs[0], null, null);
else
firstTabAdded = gBrowser.addTab(aURIs[0], null, null, null, owner);
for (var i = 1; i < aURIs.length; ++i)
gBrowser.addTab(aURIs[i]);
if (!aLoadInBackground) {
if (firstTabAdded) {
// .selectedTab setter focuses the content area
this.selectedTab = firstTabAdded;
}
else
window.content.focus();
}
]]></body>
</method>
<method name="addTab">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<parameter name="aPostData"/>
<parameter name="aOwner"/>
<body>
<![CDATA[
if (!this.mTabbedMode)
@ -1053,7 +1094,23 @@
t.setAttribute("flex", "100");
t.setAttribute("validate", "never");
t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
t.className = "tabbrowser-tab";
this.mTabContainer.appendChild(t);
// If this new tab is owned by another, assert that relationship
if (aOwner !== undefined && aOwner !== null) {
t.owner = aOwner;
var self = this;
function attrChanged(event) {
if (event.attrName == "selectedIndex" &&
event.prevValue != event.newValue)
self.resetOwner(parseInt(event.prevValue));
}
if (!this.mTabChangedListenerAdded) {
this.mTabBox.addEventListener("DOMAttrModified", attrChanged, false);
this.mTabChangedListenerAdded = true;
}
}
var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"browser");
@ -1190,6 +1247,20 @@
]]>
</body>
</method>
<method name="resetOwner">
<parameter name="oldIndex"/>
<body>
<![CDATA[
// Reset the owner property, since we're leaving the modally opened
// tab for another.
if (oldIndex < this.mTabContainer.childNodes.length) {
var tab = this.mTabContainer.childNodes[oldIndex];
tab.owner = null;
}
]]>
</body>
</method>
<method name="removeTab">
<parameter name="aTab"/>
@ -1255,15 +1326,32 @@
newIndex = currentIndex-1;
else if (currentIndex < index)
newIndex = currentIndex;
else if (index == l - 1)
newIndex = index-1;
else
newIndex = index;
else {
if ("owner" in aTab && aTab.owner &&
this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
var tab = this.mTabContainer.childNodes[i];
if (tab == aTab.owner) {
newIndex = i;
break;
}
}
}
if (newIndex == -1)
newIndex = (index == l - 1) ? index - 1 : index;
}
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) {
tab = this.mTabContainer.childNodes[i];
if (tab.owner == oldTab)
tab.owner = null;
}
// Because of the way XBL works (fields just set JS
// properties on the element) and the code we have in place
@ -1339,7 +1427,7 @@
]]>
</body>
</method>
<method name="addProgressListener">
<parameter name="aListener"/>
<parameter name="aMask"/>
@ -1456,7 +1544,8 @@
<parameter name="aDragAction"/>
<body>
<![CDATA[
if (aEvent.target.localName == "tab") {
if (aEvent.target.localName == "tab" &&
aEvent.originalTarget.localName != "toolbarbutton") {
aXferData.data = new TransferData();
var URI = this.getBrowserForTab(aEvent.target).currentURI;
@ -1548,7 +1637,7 @@
}
else {
// Load in an existing tab.
var tab = aEvent.originalTarget;
var tab = aEvent.target;
this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
if (this.mCurrentTab != tab && !bgLoad)
@ -2045,6 +2134,27 @@
<property name="forceSyncURLBarUpdate"
onget="return this.mModalDialogShowing;"/>
<method name="createTooltip">
<parameter name="event"/>
<body>
<![CDATA[
event.preventBubble();
var tn = document.tooltipNode;
if (tn.localName != "tab")
return false; // Not a tab, so cancel the tooltip
if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
event.target.setAttribute("label", tn.getAttribute("closetabtext"));
return true;
}
if (tn.hasAttribute("label")) {
event.target.setAttribute("label", tn.getAttribute("label"));
return true;
}
return false;
]]>
</body>
</method>
<constructor>
<![CDATA[
@ -2136,5 +2246,150 @@
</handler>
</handlers>
</binding>
<binding id="tabbrowser-tabs"
extends="chrome://global/content/bindings/tabbox.xml#tabs">
<content>
<xul:hbox flex="1" style="min-width: 1px;">
<children includes="tab"/>
<xul:spacer class="tabs-right" flex="1"/>
</xul:hbox>
</content>
<implementation implements="nsIObserver">
<constructor>
var pb2 =
Components.classes['@mozilla.org/preferences-service;1'].
getService(Components.interfaces.nsIPrefBranch2);
try {
this.mTabClipWidth = pb2.getIntPref("browser.tabs.tabClipWidth");
}
catch (e) {
}
this._updateDisableBackgroundClose();
pb2.addObserver("browser.tabs.disableBackgroundClose", this, false);
var self = this;
function onResize() {
self.adjustCloseButtons(1);
}
window.addEventListener("resize", onResize, false);
</constructor>
<destructor>
var pb2 =
Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch2);
pb2.removeObserver("browser.tabs.disableBackgroundClose", this);
</destructor>
<method name="_updateDisableBackgroundClose">
<body><![CDATA[
var prefs =
Components.classes['@mozilla.org/preferences-service;1'].
getService(Components.interfaces.nsIPrefBranch);
try {
if (prefs.getBoolPref("browser.tabs.disableBackgroundClose"))
this.setAttribute("disablebackgroundclose", "true");
else
this.removeAttribute("disablebackgroundclose");
}
catch (e) {
this.setAttribute("disablebackgroundclose", "true");
}
]]></body>
</method>
<method name="observe">
<parameter name="subject"/>
<parameter name="topic"/>
<parameter name="data"/>
<body><![CDATA[
if (topic == "nsPref:changed")
this._updateDisableBackgroundClose();
]]></body>
</method>
<field name="mTabClipWidth">140</field>
<method name="adjustCloseButtons">
<parameter name="aNumTabs"/>
<body><![CDATA[
// aNumTabs is the number of tabs that need to be present to cause
// the close button on the last visible tab to disappear when the
// pref for "always show the tab bar, even when only one tab is open"
// is set.
// When tabs are being removed from the tab strip, and the number of
// open tabs approaches 1 (i.e. when the number of open tabs is 2
// and one is removed), we need to set an attribute on the tabstrip
// that will cause the close button on the last item to be hidden.
// When tabs are being added to the tab strip - the number of open
// tabs is increasing (i.e. the number of open tabs is 1 and one is
// added) then we need to remove the attribute on the tab strip which
// will cause the close button to be shown on all tabs.
try {
if (this.childNodes.length == aNumTabs)
this.setAttribute("singlechild", "true");
else
this.removeAttribute("singlechild");
var width = this.firstChild.boxObject.width;
// 0 width is an invalid value and indicates an item without display,
// so ignore.
if (width > this.mTabClipWidth || width == 0)
this.removeAttribute("tiny");
else
this.setAttribute("tiny", "true");
}
catch (e) {
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="DOMNodeInserted" action="this.adjustCloseButtons(1);"/>
<handler event="DOMNodeRemoved" action="this.adjustCloseButtons(2);"/>
</handlers>
</binding>
<binding id="tabbrowser-tab" display="xul:box"
extends="chrome://global/content/bindings/tabbox.xml#tab">
<content chromedir="&locale.dir;" mousethrough="always"
closetabtext="&closeTab.label;">
<xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected" flex="1">
<xul:image class="tab-icon" xbl:inherits="validate,src=image"/>
<xul:label class="tab-text" xbl:inherits="value=label,accesskey,crop,disabled" flex="1"/>
</xul:hbox>
<xul:image anonid="close-button-placeholder" class="tab-close-button-placeholder"/>
<xul:toolbarbutton anonid="close-button" class="tab-close-button"/>
</content>
<implementation>
<field name="mOverCloseButton">false</field>
</implementation>
<handlers>
<handler event="mouseover">
var anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.mOverCloseButton = true;
</handler>
<handler event="mouseout">
var anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.mOverCloseButton = false;
</handler>
<handler event="command">
<![CDATA[
var anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.parentNode.parentNode.parentNode.parentNode.removeTab(this);
]]>
</handler>
<handler event="mousedown" button="0" phase="capturing">
<![CDATA[
if (this.mOverCloseButton)
event.stopPropagation();
]]>
</handler>
</handlers>
</binding>
</bindings>

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

@ -5,17 +5,18 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tab" extends="chrome://global/content/bindings/tabbox.xml#tab">
<binding id="tabbrowser-tab" extends="chrome://global/content/bindings/tabbrowser.xml#tabbrowser-tab">
<content>
<xul:hbox class="tab-image-left" xbl:inherits="selected"/>
<xul:hbox pack="center" align="center">
<xul:image anonid="close-button-placeholder" class="tab-close-button-placeholder"/>
</xul:hbox>
<xul:toolbarbutton anonid="close-button" class="tab-close-button"/>
<xul:hbox flex="1" class="tab-image-middle" align="center" xbl:inherits="selected">
<xul:stack class="tab-icon">
<xul:image xbl:inherits="validate,src=image" class="tab-icon-image"/>
<xul:image class="tab-extra-status"/>
</xul:stack>
<xul:image xbl:inherits="validate,src=image" class="tab-icon-image"/>
<xul:image class="tab-extra-status"/>
</xul:stack>
<xul:label flex="1" xbl:inherits="value=label,crop,accesskey" crop="right" class="tab-text"/>
</xul:hbox>
<xul:hbox class="tab-image-right" xbl:inherits="selected"/>
@ -37,8 +38,8 @@
</content>
</binding>
<binding id="tabs-closebutton"
extends="chrome://global/content/bindings/tabbox.xml#tabs">
<binding id="tabbrowser-tabs"
extends="chrome://global/content/bindings/tabbrowser.xml#tabbrowser-tabs">
<content>
<xul:stack flex="1" class="tabs-stack">
<xul:vbox>
@ -54,12 +55,6 @@
<children/>
<xul:spacer class="tabs-right" flex="1"/>
</xul:hbox>
<xul:stack>
<xul:spacer class="tabs-right"/>
<xul:hbox class="tabs-closebutton-box" align="center" pack="end">
<xul:toolbarbutton class="tabs-closebutton close-button" xbl:inherits="disabled=disableclose,oncommand=onclosetab"/>
</xul:hbox>
</xul:stack>
</xul:hbox>
<xul:spacer class="tabs-bottom-spacer"/>
</xul:vbox>