mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-30 10:12:59 +00:00
fix for bug #318168, tab browsing improvements such as:
1) when we have "too many" tabs in a window, allow the user to scroll through the tabs. 2) add events for when adding and removing tabs initial patch by mconnor. final patch r=mconnor
This commit is contained in:
parent
38987b729d
commit
1b096a746f
@ -166,6 +166,8 @@ pref("browser.tabs.maxOpenBeforeWarn", 15);
|
||||
// 0 = append, 1 = replace
|
||||
pref("browser.tabs.loadGroup", 1);
|
||||
|
||||
pref("toolkit.scrollbox.scrollIncrement", 20);
|
||||
|
||||
// lets new tab/window load something different than first window
|
||||
// -1 - use navigator startup preference
|
||||
// 0 - loads blank page
|
||||
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
]>
|
||||
|
||||
<bindings id="arrowscrollboxBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
@ -13,7 +18,7 @@
|
||||
|
||||
<binding id="scrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
||||
<content>
|
||||
<xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir">
|
||||
<xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir" flex="1">
|
||||
<children/>
|
||||
</xul:box>
|
||||
</content>
|
||||
@ -21,25 +26,97 @@
|
||||
|
||||
<binding id="arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
||||
<content>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-up" collapsed="true"
|
||||
oncommand="this.parentNode.scrollByIndex(-1); event.stopPropagation();"/>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-up" collapsed="true" xbl:inherits="orient,clicktoscroll"
|
||||
onclick="handleOnClick(event, -1);"
|
||||
oncommand="handleOnCommand(event, -1);"/>
|
||||
<xul:scrollbox xbl:inherits="orient,align,pack,dir" flex="1">
|
||||
<children/>
|
||||
</xul:scrollbox>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-down" collapsed="true"
|
||||
oncommand="this.parentNode.scrollByIndex(1); event.stopPropagation();"/>
|
||||
<xul:autorepeatbutton class="autorepeatbutton-down" collapsed="true" xbl:inherits="orient,clicktoscroll"
|
||||
onclick="handleOnClick(event, 1);"
|
||||
oncommand="handleOnCommand(event, 1);"/>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<field name="_scrollIncrement">0</field>
|
||||
<property name="scrollIncrement" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this._scrollIncrement) {
|
||||
var pb2 =
|
||||
Components.classes['@mozilla.org/preferences-service;1'].
|
||||
getService(Components.interfaces.nsIPrefBranch2);
|
||||
try {
|
||||
this._scrollIncrement = pb2.getIntPref("toolkit.scrollbox.scrollIncrement");
|
||||
}
|
||||
catch (ex) {
|
||||
this._scrollIncrement = 20;
|
||||
}
|
||||
}
|
||||
return this._scrollIncrement;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<field name="_scrollBoxObject">null</field>
|
||||
<property name="scrollBoxObject" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this._scrollBoxObject) {
|
||||
var kids = document.getAnonymousNodes(this);
|
||||
this._scrollBoxObject = kids[1].boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
||||
}
|
||||
return this._scrollBoxObject;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<method name="ensureElementIsVisible">
|
||||
<parameter name="aElement"/>
|
||||
<body><![CDATA[
|
||||
this.scrollBoxObject.ensureElementIsVisible(aElement);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="handleOnCommand">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aMultiple"/>
|
||||
<body><![CDATA[
|
||||
// don't scroll on mouseover if clicktoscroll="true"
|
||||
if (this.getAttribute("clicktoscroll") != "true") {
|
||||
var px = this.scrollIncrement * aMultiple;
|
||||
this.scrollByPixels(px);
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="handleOnClick">
|
||||
<parameter name="aEvent"/>
|
||||
<parameter name="aMultiple"/>
|
||||
<body><![CDATA[
|
||||
// only scroll by index on click if clicktoscroll="true"
|
||||
if (this.getAttribute("clicktoscroll") == "true") {
|
||||
this.scrollByIndex(aMultiple);
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="scrollByIndex">
|
||||
<parameter name="lines"/>
|
||||
<body><![CDATA[
|
||||
if (!("mScrollBoxObject" in this)) {
|
||||
var kids = document.getAnonymousNodes(this);
|
||||
this.mScrollBoxObject = kids[1].boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
||||
}
|
||||
this.scrollBoxObject.scrollByIndex(lines);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
this.mScrollBoxObject.scrollByIndex(lines);
|
||||
<method name="scrollByPixels">
|
||||
<parameter name="px"/>
|
||||
<body><![CDATA[
|
||||
if (this.getAttribute("orient") == "horizontal") {
|
||||
// if not ltr, we want to scroll the other direction
|
||||
var actualPx = window.getComputedStyle(this.parentNode, "")
|
||||
.direction == "ltr" ? px : (-1 * px);
|
||||
this.scrollBoxObject.scrollBy(actualPx, 0);
|
||||
}
|
||||
else
|
||||
this.scrollBoxObject.scrollBy(0, px);
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
@ -64,7 +141,7 @@
|
||||
</binding>
|
||||
|
||||
<binding id="autorepeatbutton" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
||||
<content>
|
||||
<content chromedir="&locale.dir;">
|
||||
<xul:image class="autorepeatbutton-icon"/>
|
||||
</content>
|
||||
</binding>
|
||||
|
@ -293,10 +293,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Fire an onselect event for the tabs element.
|
||||
// Support both the old "select" event and the new, better-named
|
||||
// "TabSelect" event.
|
||||
var event = document.createEvent('Events');
|
||||
event.initEvent('select', true, true);
|
||||
this.dispatchEvent(event);
|
||||
|
||||
event = document.createEvent("Events");
|
||||
event.initEvent("TabSelect", true, false);
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
return val;
|
||||
]]>
|
||||
|
@ -106,7 +106,7 @@
|
||||
<xul:tab selected="true" validate="never"
|
||||
onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
|
||||
this.removeAttribute('image');"
|
||||
maxwidth="250" width="0" minwidth="30" flex="100"
|
||||
maxwidth="250" width="0" minwidth="140" flex="100"
|
||||
class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
|
||||
</xul:tabs>
|
||||
</xul:hbox>
|
||||
@ -1083,6 +1083,10 @@
|
||||
if (!this.mTabbedMode)
|
||||
this.enterTabbedMode();
|
||||
|
||||
// if we're adding tabs, we're past interrupt mode, ditch the owner
|
||||
if (this.mCurrentTab.owner)
|
||||
this.mCurrentTab.owner = null;
|
||||
|
||||
var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"tab");
|
||||
|
||||
@ -1095,13 +1099,17 @@
|
||||
|
||||
t.setAttribute("crop", "end");
|
||||
t.maxWidth = 250;
|
||||
t.minWidth = 30;
|
||||
t.minWidth = 140;
|
||||
t.width = 0;
|
||||
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);
|
||||
// invalidate cache, because mTabContainer is about to change
|
||||
this._browsers = null;
|
||||
|
||||
// If this new tab is owned by another, assert that relationship
|
||||
if (aOwner !== undefined && aOwner !== null) {
|
||||
t.owner = aOwner;
|
||||
@ -1178,6 +1186,13 @@
|
||||
b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
|
||||
}
|
||||
|
||||
// Dispatch a new tab notification. We do this once we're
|
||||
// entirely done, so that things are in a consistent state
|
||||
// even if the event listener opens or closes tabs.
|
||||
var evt = document.createEvent("Events");
|
||||
evt.initEvent("TabOpen", true, false);
|
||||
t.dispatchEvent(evt);
|
||||
|
||||
return t;
|
||||
]]>
|
||||
</body>
|
||||
@ -1291,6 +1306,14 @@
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload())
|
||||
return;
|
||||
|
||||
// We're committed to closing the tab now.
|
||||
// Dispatch a notification.
|
||||
// We dispatch it before any teardown so that event listeners can
|
||||
// inspect the tab that's about to close.
|
||||
var evt = document.createEvent("Events");
|
||||
evt.initEvent("TabClose", true, false);
|
||||
aTab.dispatchEvent(evt);
|
||||
|
||||
if (l == 1) {
|
||||
// add a new blank tab to replace the one being closed
|
||||
// (this ensures that the remaining tab is as good as new)
|
||||
@ -1358,6 +1381,8 @@
|
||||
|
||||
// Remove the tab
|
||||
this.mTabContainer.removeChild(oldTab);
|
||||
// invalidate cache, because mTabContainer is about to change
|
||||
this._browsers = null;
|
||||
this.mPanelContainer.removeChild(oldBrowser.parentNode);
|
||||
|
||||
// Find the tab to select
|
||||
@ -1432,8 +1457,8 @@
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aEvent.button == 0 &&
|
||||
// Only capture clicks on tabbox.xml's <spacer>
|
||||
aEvent.originalTarget.localName == "spacer") {
|
||||
aEvent.originalTarget.localName == "box") {
|
||||
// xxx this needs to check that we're in the empty area of the tabstrip
|
||||
var e = document.createEvent("Events");
|
||||
e.initEvent("NewTab", true, true);
|
||||
this.dispatchEvent(e);
|
||||
@ -1581,30 +1606,114 @@
|
||||
<parameter name="aDragSession"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aDragSession.canDrop && aDragSession.sourceNode &&
|
||||
aDragSession.sourceNode.parentNode == this.mTabContainer) {
|
||||
if (aDragSession.canDrop && aDragSession.sourceNode) {
|
||||
// autoscroll the tab strip if we drag over the autorepeat
|
||||
// buttons, even if we aren't dragging a tab, but then
|
||||
// return to avoid drawing the drop indicator
|
||||
var isTabDrag = (aDragSession.sourceNode.parentNode == this.mTabContainer);
|
||||
var pixelsToScroll = 0;
|
||||
var tabStrip = this.mTabContainer.mTabstrip;
|
||||
if (aEvent.originalTarget.localName == "autorepeatbutton") {
|
||||
if (aEvent.originalTarget.getAttribute("class") ==
|
||||
"autorepeatbutton-up")
|
||||
pixelsToScroll = tabStrip.scrollIncrement * -1;
|
||||
else
|
||||
pixelsToScroll = tabStrip.scrollIncrement;
|
||||
tabStrip.scrollByPixels(pixelsToScroll);
|
||||
}
|
||||
|
||||
if (!isTabDrag)
|
||||
return;
|
||||
|
||||
var newIndex = this.getNewIndex(aEvent);
|
||||
|
||||
var ib = this.mTabDropIndicatorBar;
|
||||
var ind = ib.firstChild;
|
||||
ib.setAttribute('dragging','true');
|
||||
|
||||
if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
|
||||
var tabStripBoxObject = tabStrip.scrollBoxObject;
|
||||
var halfIndWidth = Math.floor((ind.boxObject.width + 1) / 2);
|
||||
if (window.getComputedStyle(this.parentNode, null)
|
||||
.direction == "ltr") {
|
||||
var newMarginLeft;
|
||||
var minMarginLeft = tabStripBoxObject.x - halfIndWidth;
|
||||
// make sure we don't place the tab drop indicator past the
|
||||
// edge, or the containing box will flex and stretch
|
||||
// the tab drop indicator bar, which will flex the url bar.
|
||||
// XXX todo
|
||||
// just use first value if you can figure out how to get
|
||||
// the tab drop indicator to crop instead of flex and stretch
|
||||
// the tab drop indicator bar.
|
||||
var maxMarginLeft = Math.min(
|
||||
(minMarginLeft + tabStripBoxObject.width),
|
||||
(ib.boxObject.x + ib.boxObject.width - ind.boxObject.width));
|
||||
|
||||
// if we are scrolling, put the drop indicator at the edge
|
||||
// so that it doesn't jump while scrolling
|
||||
if (pixelsToScroll > 0)
|
||||
newMarginLeft = maxMarginLeft;
|
||||
else if (pixelsToScroll < 0)
|
||||
newMarginLeft = minMarginLeft;
|
||||
else {
|
||||
if (newIndex == this.mTabs.length) {
|
||||
ind.style.marginLeft = this.mTabs[newIndex-1].boxObject.x +
|
||||
this.mTabs[newIndex-1].boxObject.width - this.boxObject.x - 7 + 'px';
|
||||
newMarginLeft = this.mTabs[newIndex-1].boxObject.screenX +
|
||||
this.mTabs[newIndex-1].boxObject.width -
|
||||
this.boxObject.screenX - halfIndWidth;
|
||||
} else {
|
||||
ind.style.marginLeft = this.mTabs[newIndex].boxObject.x - this.boxObject.x - 7 + 'px';
|
||||
newMarginLeft = this.mTabs[newIndex].boxObject.screenX -
|
||||
this.boxObject.screenX - halfIndWidth;
|
||||
}
|
||||
|
||||
// ensure we never place the drop indicator beyond
|
||||
// our limits
|
||||
if (newMarginLeft < minMarginLeft)
|
||||
newMarginLeft = minMarginLeft;
|
||||
else if (newMarginLeft > maxMarginLeft)
|
||||
newMarginLeft = maxMarginLeft;
|
||||
}
|
||||
ind.style.marginLeft = newMarginLeft + 'px';
|
||||
} else {
|
||||
var newMarginRight;
|
||||
var minMarginRight = tabStripBoxObject.x - halfIndWidth;
|
||||
// make sure we don't place the tab drop indicator past the
|
||||
// edge, or the containing box will flex and stretch
|
||||
// the tab drop indicator bar, which will flex the url bar.
|
||||
// XXX todo
|
||||
// just use first value if you can figure out how to get
|
||||
// the tab drop indicator to crop instead of flex and stretch
|
||||
// the tab drop indicator bar.
|
||||
var maxMarginRight = Math.min(
|
||||
(minMarginRight + tabStripBoxObject.width),
|
||||
(ib.boxObject.x + ib.boxObject.width - ind.boxObject.width));
|
||||
|
||||
// if we are scrolling, put the drop indicator at the edge
|
||||
// so that it doesn't jump while scrolling
|
||||
if (pixelsToScroll > 0)
|
||||
newMarginRight = maxMarginRight;
|
||||
else if (pixelsToScroll < 0)
|
||||
newMarginRight = minMarginRight;
|
||||
else {
|
||||
if (newIndex == this.mTabs.length) {
|
||||
ind.style.marginRight = this.boxObject.width + this.boxObject.x -
|
||||
this.mTabs[newIndex-1].boxObject.x + 'px';
|
||||
newMarginRight = this.boxObject.width +
|
||||
this.boxObject.screenX -
|
||||
this.mTabs[newIndex-1].boxObject.screenX -
|
||||
halfIndWidth;
|
||||
} else {
|
||||
ind.style.marginRight = this.boxObject.width + this.boxObject.x -
|
||||
this.mTabs[newIndex].boxObject.x -
|
||||
this.mTabs[newIndex].boxObject.width + 'px';
|
||||
newMarginRight = this.boxObject.width +
|
||||
this.boxObject.screenX -
|
||||
this.mTabs[newIndex].boxObject.screenX -
|
||||
this.mTabs[newIndex].boxObject.width -
|
||||
halfIndWidth;
|
||||
}
|
||||
|
||||
// ensure we never place the drop indicator beyond
|
||||
// our limits
|
||||
if (newMarginRight < minMarginRight)
|
||||
newMarginRight = minMarginRight;
|
||||
else if (newMarginRight > maxMarginRight)
|
||||
newMarginRight = maxMarginRight;
|
||||
}
|
||||
ind.style.marginRight = newMarginRight + 'px';
|
||||
}
|
||||
}
|
||||
]]>
|
||||
@ -1696,15 +1805,24 @@
|
||||
this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
|
||||
this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
|
||||
|
||||
var oldPosition = aTab._tPos;
|
||||
|
||||
aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
|
||||
this.mCurrentTab.selected = false;
|
||||
this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes[aIndex]);
|
||||
// invalidate cache, because mTabContainer is about to change
|
||||
this._browsers = null;
|
||||
|
||||
var i;
|
||||
for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
|
||||
this.mTabContainer.childNodes[i]._tPos = i;
|
||||
}
|
||||
this.mCurrentTab.selected = true;
|
||||
|
||||
var evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("TabMove", true, false, window, oldPosition);
|
||||
aTab.dispatchEvent(evt);
|
||||
|
||||
return aTab;
|
||||
]]>
|
||||
</body>
|
||||
@ -1717,11 +1835,11 @@
|
||||
var i;
|
||||
if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
|
||||
for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
|
||||
if (aEvent.clientX < this.mTabs[i].boxObject.x + this.mTabs[i].boxObject.width / 2)
|
||||
if (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
|
||||
return i;
|
||||
} else {
|
||||
for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
|
||||
if (aEvent.clientX > this.mTabs[i].boxObject.x + this.mTabs[i].boxObject.width / 2)
|
||||
if (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
|
||||
return i;
|
||||
}
|
||||
|
||||
@ -2264,9 +2382,13 @@
|
||||
<binding id="tabbrowser-tabs"
|
||||
extends="chrome://global/content/bindings/tabbox.xml#tabs">
|
||||
<content>
|
||||
<xul:hbox flex="1" style="min-width: 1px;">
|
||||
<xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1" style="min-width: 1px;" clicktoscroll="true">
|
||||
<children includes="tab"/>
|
||||
<xul:spacer class="tabs-right" flex="1"/>
|
||||
</xul:arrowscrollbox>
|
||||
<xul:hbox class="tabs-closebutton-box" align="center" pack="end" anonid="tabstrip-closebutton">
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="close-button tabs-closebutton"
|
||||
oncommand="this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.removeCurrentTab()"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
<implementation>
|
||||
@ -2276,20 +2398,32 @@
|
||||
getService(Components.interfaces.nsIPrefBranch2);
|
||||
try {
|
||||
this.mTabClipWidth = pb2.getIntPref("browser.tabs.tabClipWidth");
|
||||
this.mCloseButtons = pb2.getIntPref("browser.tabs.closeButtons");
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
this._updateDisableBackgroundClose();
|
||||
this.adjustTabstrip(false);
|
||||
|
||||
pb2.addObserver("browser.tabs.disableBackgroundClose", this._prefObserver, true);
|
||||
pb2.addObserver("browser.tabs.closeButtons", this._prefObserver, true);
|
||||
|
||||
var self = this;
|
||||
function onResize() {
|
||||
self.adjustCloseButtons(1);
|
||||
self.adjustTabstrip(false);
|
||||
}
|
||||
window.addEventListener("resize", onResize, false);
|
||||
</constructor>
|
||||
|
||||
<field name="mTabstrip">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
|
||||
</field>
|
||||
|
||||
<field name="mTabstripClosebutton">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "tabstrip-closebutton");
|
||||
</field>
|
||||
|
||||
<method name="_updateDisableBackgroundClose">
|
||||
<body><![CDATA[
|
||||
var prefs =
|
||||
@ -2306,13 +2440,26 @@
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<field name="_prefObserver">({
|
||||
tabbox: this,
|
||||
|
||||
observe: function(subject, topic, data)
|
||||
{
|
||||
if (topic == "nsPref:changed")
|
||||
this.tabbox._updateDisableBackgroundClose();
|
||||
if (topic == "nsPref:changed") {
|
||||
switch (data) {
|
||||
case "browser.tabs.disableBackgroundClose":
|
||||
this._updateDisableBackgroundClose();
|
||||
break;
|
||||
case "browser.tabs.closeButtons":
|
||||
var pb2 =
|
||||
Components.classes['@mozilla.org/preferences-service;1'].
|
||||
getService(Components.interfaces.nsIPrefBranch2);
|
||||
this.mCloseButtons = pb2.getIntPref("browser.tabs.closeButtons");
|
||||
this.adjustTabstrip(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface : function(aIID)
|
||||
@ -2325,44 +2472,97 @@
|
||||
}
|
||||
});
|
||||
</field>
|
||||
<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");
|
||||
<field name="mTabClipWidth">130</field>
|
||||
<field name="mCloseButtons">1</field>
|
||||
|
||||
<method name="adjustTabstrip">
|
||||
<parameter name="aRemovingTab"/>
|
||||
<body><![CDATA[
|
||||
// modes for tabstrip
|
||||
// 0 - activetab = close button on active tab only
|
||||
// 1 - alltabs = close buttons on all tabs
|
||||
// 2 - noclose = no close buttons at all
|
||||
// 3 - closeatend = close button at the end of the tabstrip
|
||||
switch (this.mCloseButtons) {
|
||||
case 0:
|
||||
// TabClose fires before the tab closes, so if we have two tabs
|
||||
// and we're removing the tab we should go to no closebutton
|
||||
if ((aRemovingTab && this.childNodes.length == 2) ||
|
||||
this.childNodes.length == 1)
|
||||
this.setAttribute("closebuttons", "noclose");
|
||||
else
|
||||
this.setAttribute("closebuttons", "activetab");
|
||||
break;
|
||||
case 1:
|
||||
try {
|
||||
// if we have only one tab, hide the closebutton
|
||||
if ((aRemovingTab && this.childNodes.length == 2) ||
|
||||
this.childNodes.length == 1)
|
||||
this.setAttribute("closebuttons", "noclose");
|
||||
else {
|
||||
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");
|
||||
this.setAttribute("closebuttons", "alltabs");
|
||||
else
|
||||
this.setAttribute("tiny", "true");
|
||||
this.setAttribute("closebuttons", "activetab");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
this.setAttribute("closebuttons", "noclose");
|
||||
break;
|
||||
}
|
||||
this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
|
||||
if (aRemovingTab) {
|
||||
// if we're at the end of the tabstrip, we need to ensure
|
||||
// that we stay completely scrolled to the end
|
||||
// this is a hack to determine if that's where we are already
|
||||
var tabWidth = this.firstChild.boxObject.width;
|
||||
var scrollPos = {};
|
||||
this.mTabstrip.scrollBoxObject.getPosition(scrollPos, {});
|
||||
if (scrollPos.value + this.mTabstrip.boxObject.width > tabWidth * (this.childNodes.length - 1))
|
||||
this.mTabstrip.scrollByPixels(-1 * this.firstChild.boxObject.width);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<field name="_mPrefs">null</field>
|
||||
<property name="mPrefs" readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
if (!this._mPrefs) {
|
||||
this._mPrefs =
|
||||
Components.classes['@mozilla.org/preferences-service;1'].
|
||||
getService(Components.interfaces.nsIPrefBranch2);
|
||||
}
|
||||
return this._mPrefs;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<method name="_handleTabSelect">
|
||||
<body><![CDATA[
|
||||
this.mTabstrip.scrollBoxObject.ensureElementIsVisible(this.selectedItem);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_handleUnderflow">
|
||||
<body><![CDATA[
|
||||
this.mTabstrip.scrollBoxObject.scrollBy(-2400, 0);
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="DOMNodeInserted" action="this.adjustCloseButtons(1);"/>
|
||||
<handler event="DOMNodeRemoved" action="this.adjustCloseButtons(2);"/>
|
||||
<handler event="TabOpen" action="this.adjustTabstrip(false);"/>
|
||||
<handler event="TabClose" action="this.adjustTabstrip(true);"/>
|
||||
<handler event="TabSelect" action="this._handleTabSelect()"/>
|
||||
<handler event="underflow" action="this._handleUnderflow()"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
@ -625,6 +625,14 @@ tab {
|
||||
}
|
||||
|
||||
.tab-close-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs:not([closebuttons="noclose"]):not([closebuttons="closeatend"]) > .tabbrowser-tab[selected="true"] > .tab-close-button {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[closebuttons="alltabs"] > .tabbrowser-tab > .tab-close-button {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
@ -632,22 +640,15 @@ tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabs-closebutton-box > .tab-close-button {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[disablebackgroundclose="true"] > .tabbrowser-tab:not([selected="true"]) > .tab-close-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[disablebackgroundclose="true"]:not([tiny="true"]):not([singlechild="true"]) > .tabbrowser-tab:not([selected="true"]) .tab-close-button-placeholder {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[tiny="true"] > .tabbrowser-tab > .tab-close-button,
|
||||
.tabbrowser-tabs[tiny="true"] > .tabbrowser-tab .tab-close-button-placeholder,
|
||||
.tabbrowser-tabs[singlechild="true"] > .tabbrowser-tab > .tab-close-button,
|
||||
.tabbrowser-tabs[singlechild="true"] > .tabbrowser-tab .tab-close-button-placeholder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[tiny="true"] > .tabbrowser-tab[selected="true"] > .tab-close-button {
|
||||
.tabbrowser-tabs[disablebackgroundclose="true"]:not([closebuttons="noclose"]):not([closebuttons="closeatend"]) > .tabbrowser-tab:not([selected="true"]) .tab-close-button-placeholder {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
|
@ -59,9 +59,13 @@
|
||||
<xul:stack>
|
||||
<xul:spacer class="tabs-left"/>
|
||||
</xul:stack>
|
||||
<xul:hbox flex="1" style="min-width: 1px;">
|
||||
<xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1" style="min-width: 1px;">
|
||||
<children/>
|
||||
<xul:spacer class="tabs-right" flex="1"/>
|
||||
</xul:arrowscrollbox>
|
||||
<xul:hbox class="tabs-closebutton-box" align="center" pack="end" anonid="tabstrip-closebutton">
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="close-button tabs-closebutton"
|
||||
oncommand="this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.removeCurrentTab()"/>
|
||||
</xul:hbox>
|
||||
</xul:hbox>
|
||||
<xul:spacer class="tabs-bottom-spacer"/>
|
||||
|
@ -5,10 +5,20 @@
|
||||
*/
|
||||
.autorepeatbutton-up
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-up-sharp.gif");
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-lft-sharp.gif");
|
||||
}
|
||||
|
||||
.autorepeatbutton-down
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-rit-sharp.gif");
|
||||
}
|
||||
|
||||
.autorepeatbutton-up[orient="vertical"]
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-up-sharp.gif");
|
||||
}
|
||||
|
||||
.autorepeatbutton-down[orient="vertical"]
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-dn-sharp.gif");
|
||||
}
|
||||
|
@ -43,11 +43,6 @@ tab[selected="true"] {
|
||||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
||||
.tabbrowser-tabs[tiny="true"] > .tabbrowser-tab[selected="true"],
|
||||
.tabborwser-tabs[singlechild="true"] > .tabbrowser-tab[selected="true"] {
|
||||
min-width: 50px; /* approximately favicon + close icon + slop for tab boundaries */
|
||||
}
|
||||
|
||||
tab {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
@ -3,19 +3,32 @@
|
||||
/*
|
||||
* Autorepeatbutton
|
||||
*/
|
||||
.autorepeatbutton-up
|
||||
.autorepeatbutton-up
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-lft.gif");
|
||||
-moz-image-region : auto; /* cut off inheritance */
|
||||
}
|
||||
|
||||
.autorepeatbutton-down
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-rit.gif");
|
||||
-moz-image-region : auto; /* cut off inheritance */
|
||||
}
|
||||
|
||||
.autorepeatbutton-up[orient="vertical"]
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-up.gif");
|
||||
-moz-image-region : auto; /* cut off inheritance */
|
||||
}
|
||||
|
||||
.autorepeatbutton-down
|
||||
.autorepeatbutton-down[orient="vertical"]
|
||||
{
|
||||
list-style-image : url("chrome://global/skin/arrow/arrow-dn.gif");
|
||||
-moz-image-region : auto; /* cut off inheritance */
|
||||
}
|
||||
|
||||
autorepeatbutton
|
||||
autorepeatbutton,
|
||||
autorepeatbutton[clicktoscroll="true"]:hover
|
||||
{
|
||||
-moz-box-align : center;
|
||||
-moz-box-pack : center;
|
||||
@ -27,7 +40,8 @@ autorepeatbutton
|
||||
padding : 1px;
|
||||
}
|
||||
|
||||
autorepeatbutton:hover
|
||||
autorepeatbutton:hover,
|
||||
autorepeatbutton:hover:active
|
||||
{
|
||||
margin : 1px;
|
||||
border : 1px inset ThreeDFace;
|
||||
|
Loading…
x
Reference in New Issue
Block a user