Bug 582583 - Expose a small, simple UI for 'Undo Closed Tab' [r=mfinkle]

This commit is contained in:
Vivien Nicolas 2010-10-06 17:08:56 +02:00
parent f3b1ddb5d0
commit 3d7c42a6dd
7 changed files with 136 additions and 53 deletions

View File

@ -409,6 +409,7 @@ var BrowserUI = {
let tabs = document.getElementById("tabs");
tabs.addEventListener("TabSelect", this, true);
tabs.addEventListener("TabOpen", this, true);
window.addEventListener("PanFinished", this, true);
// listen content messages
messageManager.addMessageListener("DOMLinkAdded", this);
@ -770,8 +771,8 @@ var BrowserUI = {
case "TabOpen":
{
let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
if (!(tabsVisibility == 1.0) && Browser.selectedTab.chromeTab != aEvent.target)
NewTabPopup.show(aEvent.target);
if (!(tabsVisibility == 1.0) && Browser.selectedTab.chromeTab != aEvent.originalTarget)
NewTabPopup.show(aEvent.originalTarget);
// Workaround to hide the tabstrip if it is partially visible
// See bug 524469
@ -780,6 +781,11 @@ var BrowserUI = {
break;
}
case "PanFinished":
let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
if (tabsVisibility == 0.0)
document.getElementById("tabs").removeClosedTab();
break;
// Window events
case "keypress":
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)

View File

@ -18,7 +18,7 @@ browser[remote="true"] {
-moz-binding: url("chrome://browser/content/tabs.xml#tablist");
}
box[type="documenttab"] {
documenttab {
-moz-binding: url("chrome://browser/content/tabs.xml#documenttab");
}

View File

@ -211,7 +211,11 @@
<spacer class="toolbar-height"/>
<!-- Left toolbar -->
<vbox id="tabs-container" class="panel-dark" flex="1">
<vbox id="tabs" onselect="BrowserUI.selectTab(this);" onclosetab="BrowserUI.closeTab(this)" flex="1"/>
<vbox id="tabs" flex="1"
onselect="BrowserUI.selectTab(this);"
onreloadtab="BrowserUI.undoCloseTab()"
onclosetab="BrowserUI.closeTab(this)"
onclosereloadtab="this._container.removeTab(this)"/>
<hbox id="tabs-controls">
<toolbarbutton id="newtab-button" class="button-image" command="cmd_newTab"/>
</hbox>

View File

@ -9,35 +9,51 @@
<binding id="documenttab">
<content>
<xul:stack anonid="page" class="documenttab-container" flex="1">
<html:canvas anonid="canvas" class="documenttab-canvas" left="0" width="106" height="64" moz-opaque="true"
onclick="document.getBindingParent(this)._onClick()" xbl:inherits="selected"/>
<xul:hbox class="documenttab-close-container" left="0" top="10" height="64" width="55" align="center" onclick="document.getBindingParent(this)._close()">
<html:canvas anonid="thumbnail" class="documenttab-thumbnail" left="0" width="106" height="64" moz-opaque="true" empty="true"
onclick="document.getBindingParent(this)._onClick()"/>
<xul:hbox class="documenttab-reload" left="0" top="0" width="122" height="80" onclick="document.getBindingParent(this)._onUndo();"/>
<xul:hbox class="documenttab-close-container" left="0" top="10" height="64" width="55" align="center" onclick="document.getBindingParent(this)._onClose()">
<xul:image anonid="close" class="documenttab-close" mousethrough="always"/>
</xul:hbox>
</xul:stack>
</content>
<implementation>
<field name="thumbnail">document.getAnonymousElementByAttribute(this, "anonid", "thumbnail");</field>
<field name="_container">this.parentNode.parentNode;</field>
<method name="_onClick">
<body>
<![CDATA[
this.parentNode.selectedTab = this;
this._container.selectedTab = this;
let selectFn = new Function("event", this.parentNode.getAttribute('onselect'));
let selectFn = new Function("event", this._container.getAttribute("onselect"));
selectFn.call(this);
]]>
</body>
</method>
<method name="_close">
<method name="_onClose">
<body>
<![CDATA[
let closeFn = new Function("event", this.parentNode.getAttribute('onclosetab'));
let callbackFunc = this._container.getAttribute(this.hasAttribute("reload") ? "onclosereloadtab" : "onclosetab");
let closeFn = new Function("event", callbackFunc);
closeFn.call(this);
]]>
</body>
</method>
<method name="_onUndo">
<body>
<![CDATA[
let closeFn = new Function("event", this._container.getAttribute("onreloadtab"));
closeFn.call(this);
this._container.removeTab(this);
]]>
</body>
</method>
<method name="updateThumbnail">
<parameter name="browser"/>
<parameter name="width"/>
@ -49,9 +65,11 @@
let ratio = tabHeight / tabWidth;
height = width * ratio;
let thumbnail = this.thumbnail;
thumbnail.removeAttribute("empty");
let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
let renderer = rendererFactory(browser, canvas)
let renderer = rendererFactory(browser, thumbnail)
renderer.drawContent(function(ctx, callback) {
ctx.save();
ctx.clearRect(0, 0, tabWidth, tabHeight);
@ -66,22 +84,24 @@
</binding>
<binding id="tablist">
<content>
<xul:vbox anonid="tabs-children" flex="1"/>
<xul:box anonid="tabs-undo"/>
</content>
<implementation>
<field name="children">document.getAnonymousElementByAttribute(this, "anonid", "tabs-children");</field>
<field name="_tabsUndo">document.getAnonymousElementByAttribute(this, "anonid", "tabs-undo");</field>
<field name="_closedTab">null</field>
<field name="_selectedTab">null</field>
<property name="selectedTab">
<getter>
<![CDATA[
return this._selectedTab;
]]>
</getter>
<property name="selectedTab" onget="return this._selectedTab;">
<setter>
<![CDATA[
if (this._selectedTab)
this._selectedTab.removeAttribute('selected');
this._selectedTab.removeAttribute("selected");
if (val)
val.setAttribute('selected', 'true');
val.setAttribute("selected", "true");
this._selectedTab = val;
]]>
@ -91,9 +111,8 @@
<method name="addTab">
<body>
<![CDATA[
let tab = document.createElement("box");
tab.setAttribute("type", "documenttab");
this.appendChild(tab);
let tab = document.createElement("documenttab");
this.children.appendChild(tab);
this._updateWidth();
return tab;
]]>
@ -104,21 +123,51 @@
<parameter name="aTab"/>
<body>
<![CDATA[
this.removeChild(aTab);
this._updateWidth();
let closedTab = this._closedTab;
if (closedTab) {
this._tabsUndo.removeChild(closedTab);
this._closedTab = null;
}
if (!closedTab || closedTab != aTab) {
if (aTab.thumbnail && !aTab.thumbnail.hasAttribute("empty")) {
// duplicate the old thumbnail to the new one because moving the
// tab in the dom clear the canvas
let oldThumbnail = aTab.thumbnail.toDataURL("image/png");
this._tabsUndo.appendChild(aTab);
let thumbnailImg = new Image();
thumbnailImg.onload = function() aTab.thumbnail.getContext("2d").drawImage(thumbnailImg, 0, 0);
thumbnailImg.src = oldThumbnail;
}
else
this._tabsUndo.appendChild(aTab);
aTab.setAttribute("reload", "true");
this._closedTab = aTab;
}
this.resize();
]]>
</body>
</method>
<method name="removeClosedTab">
<body><![CDATA[
if (!this._closedTab)
return;
this.removeTab(this._closedTab);
]]></body>
</method>
<method name="resize">
<body>
<![CDATA[
let container = this.parentNode.getBoundingClientRect();
let element = this.getBoundingClientRect();
let height = (element.top - container.top) +
((container.top + container.height) - (element.top + element.height));
this.style.height = height + "px";
let element = this.children.getBoundingClientRect();
let height = (element.top - container.top) + ((container.top + container.height) - (element.top + element.height));
this.children.style.height = height + "px";
this._updateWidth();
]]>
@ -129,12 +178,12 @@
<method name="_updateWidth">
<body>
<![CDATA[
let firstBox = this.firstChild.getBoundingClientRect();
let lastBox = this.lastChild.getBoundingClientRect();
let columnsCount = Math.ceil(this.childNodes.length / Math.floor(this.style.height / firstBox.height));
let firstBox = this.children.firstChild.getBoundingClientRect();
let lastBox = this.children.lastChild.getBoundingClientRect();
let columnsCount = Math.ceil(this.children.childNodes.length / Math.floor(this.children.style.height / firstBox.height));
if (this._columnsCount != columnsCount) {
let width = Math.max(lastBox.right - firstBox.left, firstBox.right - lastBox.left);
this.style.width = width + "px";
this.children.style.width = width + "px";
this._columnsCount = columnsCount;
}
]]>

View File

@ -1007,7 +1007,7 @@ autocompleteresult.noresults > .autocomplete-item-container {
-moz-border-end: 3px solid #262629;
}
#tabs {
#tabs > * {
display: block;
-moz-column-width: 128px;
-moz-column-gap: 0;
@ -1017,12 +1017,16 @@ autocompleteresult.noresults > .autocomplete-item-container {
background-color: transparent;
}
#tabs documenttab:only-child .documenttab-close {
display: none;
}
#tabs-controls {
margin-top: 8px; /* core spacing */
-moz-box-pack: start;
}
box[type="documenttab"] {
documenttab {
/* display:block allow us to change the line-height, it won't work otherwise */
display: block;
width: 128px;
@ -1030,13 +1034,31 @@ box[type="documenttab"] {
line-height: 0;
}
.documenttab-canvas {
documenttab .documenttab-thumbnail {
/* keep the unselected thumbnails aligned with the selected one */
border: 8px solid #36373b;
background-color: white;
}
.documenttab-canvas[selected="true"] {
documenttab .documenttab-close-container {
-moz-margin-end: 65px;
}
documenttab .documenttab-close-container > .documenttab-close {
width: 40px;
height: 40px;
list-style-image: url("chrome://browser/skin/images/close-default-40.png");
}
documenttab .documenttab-close-container:hover:active > .documenttab-close {
list-style-image: url("chrome://browser/skin/images/close-active-40.png");
}
documenttab .documenttab-reload {
display: none;
}
documenttab[selected="true"] .documenttab-thumbnail {
border: 8px solid;
-moz-border-radius: 3px;
-moz-border-top-colors: #8db8d8 #8db8d8 #8db8d8 #8db8d8 #36373b;
@ -1045,24 +1067,25 @@ box[type="documenttab"] {
-moz-border-left-colors: #8db8d8 #8db8d8 #8db8d8 #8db8d8 #36373b;
}
.documenttab-close-container {
-moz-margin-end: 65px;
documenttab[reload="true"] .documenttab-thumbnail {
border: 8px solid;
-moz-border-radius: 3px;
-moz-border-top-colors: #262629 #262629 #262629 #262629 #262629 !important;
-moz-border-right-colors: #262629 #262629 #262629 #262629 #262629 !important;
-moz-border-bottom-colors: #262629 #262629 #262629 #262629 #262629 !important;
-moz-border-left-colors: #262629 #262629 #262629 #262629 #262629 !important;
opacity: 0.5;
}
.documenttab-close {
width: 40px;
height: 40px;
list-style-image: url("chrome://browser/skin/images/close-default-40.png");
}
hbox:hover:active > .documenttab-close {
list-style-image: url("chrome://browser/skin/images/close-active-40.png");
}
box[type="documenttab"]:only-child > stack > hbox > .documenttab-close {
documenttab[reload="true"] .documenttab-close-container {
display: none;
}
documenttab[reload="true"] .documenttab-reload {
background: url("chrome://browser/skin/images/reload-tab.png") no-repeat center center, -moz-radial-gradient(circle, rgba(137,215,21,0.8) 10%, rgba(68,108,17,0) 40%);
display: -moz-box;
}
#newtab-button {
list-style-image: url("images/newtab-default-64.png");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -42,6 +42,7 @@ chrome.jar:
skin/images/folder-32.png (images/folder-32.png)
skin/images/stop-30.png (images/stop-30.png)
skin/images/reload-30.png (images/reload-30.png)
skin/images/reload-tab.png (images/reload-tab.png)
skin/images/alert-addons-30.png (images/alert-addons-30.png)
skin/images/alert-downloads-30.png (images/alert-downloads-30.png)
skin/images/addons-default-64.png (images/addons-default-64.png)