Bug 685839 - Show tabs in a menu for portrait view. r=mfinkle
authorWes Johnston <wjohnston@mozilla.com>
Mon, 19 Sep 2011 20:17:05 -0700
changeset 77175 7f35e447e8a9c0be1f9e978ec66e5e4ec048de5e
parent 77174 1cb463c1c7b78de8103d36c01d5e6ed40efee6cd
child 77177 53cc8910cb1c947f2330aaca16c9476bd803fad6
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersmfinkle
bugs685839
milestone9.0a1
Bug 685839 - Show tabs in a menu for portrait view. r=mfinkle
mobile/chrome/content/TabsPopup.js
mobile/chrome/content/browser.css
mobile/chrome/content/browser.xul
mobile/chrome/content/tabs.xml
mobile/themes/core/honeycomb/browser.css
mobile/themes/core/honeycomb/images/newtab-tabmenu-tablet-hdpi.png
mobile/themes/core/images/checkmark-hdpi.png
mobile/themes/core/jar.mn
mobile/themes/core/tablet.css
--- a/mobile/chrome/content/TabsPopup.js
+++ b/mobile/chrome/content/TabsPopup.js
@@ -40,46 +40,108 @@ var TabsPopup = {
     Elements.tabs.addEventListener("TabOpen", this, true);
     Elements.tabs.addEventListener("TabRemove", this, true);
 
     this._updateTabsCount();
   },
 
   get box() {
     delete this.box;
-    return this.box = document.getElementById("tabs-sidebar");
+    return this.box = document.getElementById("tabs-popup-container");
   },
 
   get list() {
     delete this.list;
-    return this.list = document.getElementById("tabs");
+    return this.list = document.getElementById("tabs-popup-list");
   },
 
   get button() {
     delete this.button;
     return this.button = document.getElementById("tool-tabs");
   },
 
   hide: function hide() {
-    this.box.removeAttribute("open");
+    this.box.hidden = true;
     BrowserUI.popPopup(this);
+    window.removeEventListener("resize", this.resizeHandler, false);
   },
 
   show: function show() {
+    while(this.list.firstChild)
+      this.list.removeChild(this.list.firstChild);
+
+    let tabs = Browser.tabs;
+    tabs.forEach(function(aTab) {
+      let item = document.createElement("richlistitem");
+      item.className = "tab-popup-item";
+      if (aTab.active)
+        item.classList.add("selected");
+
+      let browser = aTab.browser;
+      let icon = browser.mIconURL;
+      let caption = browser.contentTitle || browser.currentURI.spec;
+      if (browser.__SS_restore) {
+        /* if this is a zombie tab, pull the tab title/url out of session store data */
+        let entry = browser.__SS_data.entries[0];
+        caption = entry.title;
+
+        let pageURI = Services.io.newURI(entry.url, null, null);
+        try {
+          let iconURI = gFaviconService.getFaviconImageForPage(pageURI);
+          icon = iconURI.spec;
+        } catch(ex) { }
+      }
+      item.setAttribute("img", icon);
+      item.setAttribute("label", caption);
+
+      this.list.appendChild(item);
+      item.tab = aTab;
+    }, this)
+
     // Set the box position.
-    this.box.setAttribute("open", "true");
-    this.list.resize();
+    this.box.hidden = false;
+    this.box.anchorTo(this.button, "after_end");
     BrowserUI.pushPopup(this, [this.box, this.button]);
+
+    window.addEventListener("resize", this.resizeHandler.bind(this), false);
   },
 
   toggle: function toggle() {
-    if (this.box.hasAttribute("open"))
+    if (this.box.hidden)
+      this.show();
+    else
+      this.hide();
+  },
+
+  resizeHandler: function(aEvent) {
+    if (!Util.isPortrait())
       this.hide();
-    else
-      this.show();
+  },
+
+  closeTab: function(aTab) {
+    messageManager.addMessageListener("Browser:CanUnload:Return", this.closeTabReturn.bind(this));
+  },
+
+  closeTabReturn: function(aMessage) {
+    messageManager.removeMessageListener("Browser:CanUnload:Return", this.closeTabReturn.bind(this));
+
+    if (!aMessage.json.permit)
+      return;
+
+    let removedTab = Browser.getTabForBrowser(aMessage.target);
+    setTimeout(function(self) {
+      let items = self.list.childNodes;
+      let selected = Browser.selectedTab;
+      for (let i = 0; i < items.length; i++) {
+        if (items[i].tab == selected)
+          items[i].classList.add("selected");
+        else if (items[i].tab == removedTab)
+          self.list.removeChild(items[i]);
+      }
+    }, 0, this);
   },
 
   _updateTabsCount: function() {
     let cmd = document.getElementById("cmd_showTabs");
     cmd.setAttribute("label", Browser.tabs.length);
   },
 
   handleEvent: function handleEvent(aEvent) {
--- a/mobile/chrome/content/browser.css
+++ b/mobile/chrome/content/browser.css
@@ -17,16 +17,20 @@ browser[remote="true"] {
 #tabs {
   -moz-binding: url("chrome://browser/content/tabs.xml#tablist");
 }
 
 documenttab {
   -moz-binding: url("chrome://browser/content/tabs.xml#documenttab");
 }
 
+.tab-popup-item {
+  -moz-binding: url("chrome://browser/content/tabs.xml#documenttab-popup");
+}
+
 settings {
   -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings");
 }
 
 setting[type="bool"] {
   -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-bool");
 }
 
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -310,16 +310,27 @@
       </vbox>
     </scrollbox>
 
     <!-- Form Helper suggestions helper popup -->
     <arrowbox id="form-helper-suggestions-container" flex="1" hidden="true" offset="0" top="0" left="0">
       <arrowscrollbox id="form-helper-suggestions" align="center" flex="1" orient="horizontal" onclick="FormHelperUI.doAutoComplete(event.target);"/>
     </arrowbox>
 
+    <!-- Tabs sidebar helper popup -->
+    <arrowbox id="tabs-popup-container" hidden="true" offset="18" type="dialog">
+      <vbox id="tabs-popup-inner-container" flex="1">
+         <richlistbox id="tabs-popup-list" flex="1"
+                onselect="BrowserUI.selectTab(this); TabsPopup.hide();"
+                onreloadtab="BrowserUI.undoCloseTab(); TabsPopup.hide();"
+                onclosetab="BrowserUI.closeTab(this); TabsPopup.closeTab(this);"/>
+         <button id="tabs-popup-newtab-button" class="show-text" command="cmd_newTab"/>
+      </vbox>
+    </arrowbox>
+
     <!-- popup for site identity information -->
     <arrowbox id="identity-container" hidden="true" mode="unknownIdentity" offset="18" flex="1" type="dialog" observes="bcast_urlbarState">
       <box id="identity-popup-container" flex="1" align="top">
         <image id="identity-popup-icon"/>
         <vbox id="identity-popup-content-box" flex="1">
           <box id="identity-popup-connected-box" flex="1">
             <label id="identity-popup-connectedToLabel" value="&identity.connectedTo2;"/>
             <label id="identity-popup-connectedToLabel2" flex="1">&identity.unverifiedsite2;</label>
--- a/mobile/chrome/content/tabs.xml
+++ b/mobile/chrome/content/tabs.xml
@@ -307,9 +307,51 @@
             }
           ]]>
         </body>
       </method>
 
     </implementation>
   </binding>
 
+  <binding id="documenttab-popup" extends="chrome://browser/content/bindings.xml#richlistitem">
+    <content>
+      <xul:box class="tab-popup-item-box">
+        <xul:image class="documenttab-popup-checkmark"/>
+        <xul:image class="documenttab-popup-favicon" xbl:inherits="src=img"/>
+        <xul:label class="documenttab-popup-label" xbl:inherits="value=label" crop="center"/>
+        <xul:button class="documenttab-popup-closebutton" onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()" />
+      </xul:box>
+    </content>
+
+    <implementation>
+      <field name="_container" readonly="true">this.parentNode;</field>
+      <field name="tab">null</field>
+
+      <method name="_onClick">
+        <body>
+          <![CDATA[
+            this._container.selectedTab = this;
+            let selectFn = new Function("event", this._container.getAttribute("onselect"));
+            selectFn.call(this.tab);
+          ]]>
+        </body>
+      </method>
+
+      <method name="_onClose">
+        <body>
+          <![CDATA[
+            let callbackFunc = this._container.getAttribute(this.hasAttribute("reload") ? "onclosereloadtab" : "onclosetab");
+            let closeFn = new Function("event", callbackFunc);
+            closeFn.call(this.tab);
+          ]]>
+        </body>
+      </method>
+
+    </implementation>
+
+    <handlers>
+      <handler event="click" action="this._onClick();"/>
+    </handlers>
+
+  </binding>
+
 </bindings>
--- a/mobile/themes/core/honeycomb/browser.css
+++ b/mobile/themes/core/honeycomb/browser.css
@@ -1825,9 +1825,100 @@ setting {
   
   #search-engines-list > .action-button > .button-box > .button-text {
     text-align: start;
     font-size: @font_tiny@ !important;
   }
 
 }
 
+#tabs-popup-container {
+  max-height: -moz-calc(6 * @touch_row@);
+}
+
+#tabs-popup-container .panel-arrowcontent {
+  background-color: @color_toolbar_background@;
+}
+
+#tabs-popup-list {
+  background-color: transparent;
+}
+
+#tabs-popup-newtab-button,
+.tab-popup-item {
+  min-width: @touch_action_minwidth@;
+  padding: 0px @padding_xnormal@;
+  border: none;
+  height: -moz-calc(0.75 * @touch_row@);
+  min-height: -moz-calc(0.75 * @touch_row@);
+}
+
+.tab-popup-item {
+  margin: @border_width_tiny@ 0px -moz-calc(@border_width_tiny@ * 2) 0px; /* provide some top and bottom margin to shift the borders into */
+}
+
+.tab-popup-item-box {
+  border-top: @border_width_tiny@ solid @color_button_border@;
+  margin-top: -2px; /* make the border stick slightly outside this box */
+}
+
+.tab-popup-item:first-child .tab-popup-item-box {
+  border-top: none;
+}
+
+.documenttab-popup-closebutton {
+  background-image: none;
+  background-color: transparent;
+  min-width: -moz-calc(0.75 * @touch_row@) !important;
+  list-style-image: url("chrome://browser/skin/images/close-default-tablet-hdpi.png");
+  -moz-image-region: rect(14px, 23px, 30px, 7px); /* remove empty space from around the close image */
+  -moz-margin-end: -@margin_snormal@;
+  border: none;
+  -moz-border-start: @border_width_tiny@ solid @color_button_border@;
+}
+
+#tabs-popup-newtab-button:hover:active {
+  background-color: @color_background_highlight@;  
+}
+
+.documenttab-popup-closebutton:hover:active {
+  background-image: none !important;
+}
+
+.documenttab-popup-checkmark {
+  min-width: -moz-calc(26px + 2 * @padding_normal@); /* size of the checkmark image  plus the padding */
+  padding: 0px @padding_normal@;
+}
+
+.tab-popup-item.selected .documenttab-popup-checkmark {
+  list-style-image: url("chrome://browser/skin/images/checkmark-hdpi.png");
+}
+
+.documenttab-popup-label {
+  -moz-box-flex: 1;
+  padding: 0px @padding_normal@;
+  font-size: @font_small@;
+}
+
+#tabs-popup-newtab-button {
+  background-image: none;
+  background-color: transparent;
+  padding: 0px @padding_xnormal@;
+  margin: 0px;
+  -moz-box-flex: 1;
+  border-radius: 0px;
+  list-style-image: url("chrome://browser/skin/images/newtab-tabmenu-tablet-hdpi.png");
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  border: none;
+  min-height: -moz-calc(0.75 * @touch_row@) !important;
+}
+
+#tabs-popup-newtab-button > .button-box {
+  min-height: -moz-calc(0.75 * @touch_row@);
+  border-top: @border_width_tiny@ solid @color_button_border@;
+}
+
+#tabs-popup-newtab-button .button-text {
+  -moz-padding-start: @padding_large@;
+}
+
 %include ../tablet.css
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..76ab5d98d5a4238837bec692c9f37128133b2f2a
GIT binary patch
literal 298
zc$@($0oDGAP)<h;3K|Lk000e1NJLTq000^Q000^Y1^@s6sY*=F0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy%t=H+RCwBA{Qv(y1Cf9Yh(&;y1Iqsa
z#BYH37crVy7>EZTOJsnU56b@prJs>xQAtS&UWGk9Js_X(0oCn>@?Al`gXshE@#<${
zpc5FiXw)M5Tf_p(@z8uiG63pRBA^6Zg0Nu8@uUDHBGiCO5Jq5G2U4sA%kk6(&=TYx
z-Qo!3BPmjX4y~+XATsElut1A?2BhMjlsLIZgdTWe0M#eYh$y|4aQN`vXz4v_(FnJQ
wk%V@g6qe@J6Ox--L;&;><Oh-%K>!e70JRf8^$c4f^#A|>07*qoM6N<$g8n&o6aWAK
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..affc84c69603a663e32bda7b5ca5569897ff3adf
GIT binary patch
literal 537
zc$@(l0_OdRP)<h;3K|Lk000e1NJLTq000^Q000^Y1^@s6sY*=F0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzyGcYrRCwC#*3V1AU>FB*b?VBImqxUd
z4%N+nAUq5_@6tcu)oa8c>mLXTq=N^+Q%5ge1n+GZBZ&TjKt?*4K`imdK(ptSXEDY$
zVO!F{7ru<`W&6zcd0sWTSS%vz7_rEj#s=~|;vqd}8?psSLZ%S?FQ6UBJ|vCM7leWI
z!-NQuB}lDS6Lnob%IEVBJkLvTZ36iW1NucOmCBu}syw+UNm2@+S6s@#&ESZTkw_%Q
zaQMaqf?N@XVU%LAc!PDCdew-sb<e~$zX#cMLrSI6MJAJZgVTY%^Q=(P(mga}gm0!n
zW{7aTUhlZkXq;OSmStbaCqYETdtBR_B~Y{3JkvDoB$vy{P}C=!o0&+xUcVoWMu+B2
z9LLF`C|==94V(}=M3yYw>2$8iNw?b-;7|ta8IFbra*0UaX`?zp%TA#vYCNCMQzWfc
ztEwnUwNNNLu0Y-)L$3~sT7Hj6eX_DFpSIiWTLPIAG);dPhQazp>`)vFe&B=!LG1fz
zhK1~tCJ+RnOqMYW#A(C2VnGo5(%GA2xs154V)$TRh!<pHEVg|NzbS@(kX6v;Kg0ik
begqf*#hBxK)mb;<00000NkvXXu0mjf+6?cL
--- a/mobile/themes/core/jar.mn
+++ b/mobile/themes/core/jar.mn
@@ -44,16 +44,17 @@ chrome.jar:
   skin/images/arrowrightdark-16.png         (images/arrowrightdark-16.png)
   skin/images/arrowupdark-16.png            (images/arrowupdark-16.png)
   skin/images/arrowdowndark-16.png          (images/arrowdowndark-16.png)
   skin/images/popup-bg-hdpi.png             (images/popup-bg-hdpi.png)
   skin/images/popup-selected-item-hdpi.png  (images/popup-selected-item-hdpi.png)
   skin/images/arrowbox-up.png               (images/arrowbox-up.png)
   skin/images/arrowbox-down.png             (images/arrowbox-down.png)
   skin/images/arrowbox-horiz.png            (images/arrowbox-horiz.png)
+  skin/images/checkmark-hdpi.png                 (images/checkmark-hdpi.png)
   skin/images/check-selected-30.png         (images/check-selected-30.png)
   skin/images/check-unselected-30.png       (images/check-unselected-30.png)
   skin/images/dropmarker-hdpi.png           (images/dropmarker-hdpi.png)
   skin/images/ratings-18.png                (images/ratings-18.png)
   skin/images/favicon-default-32.png        (images/favicon-default-32.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-larry-white.png     (images/errorpage-larry-white.png)
@@ -184,16 +185,17 @@ chrome.jar:
   skin/gingerbread/images/arrowleftdark-16.png          (gingerbread/images/arrowleftdark-16.png)
   skin/gingerbread/images/arrowrightdark-16.png         (gingerbread/images/arrowrightdark-16.png)
   skin/gingerbread/images/arrowupdark-16.png            (gingerbread/images/arrowupdark-16.png)
   skin/gingerbread/images/arrowdowndark-16.png          (gingerbread/images/arrowdowndark-16.png)
   skin/gingerbread/images/popup-selected-item-hdpi.png  (gingerbread/images/popup-selected-item-hdpi.png)
   skin/gingerbread/images/arrowbox-up.png               (gingerbread/images/arrowbox-up.png)
   skin/gingerbread/images/arrowbox-down.png             (gingerbread/images/arrowbox-down.png)
   skin/gingerbread/images/arrowbox-horiz.png            (gingerbread/images/arrowbox-horiz.png)
+  skin/gingerbread/images/checkmark-hdpi.png                 (images/checkmark-hdpi.png)
   skin/gingerbread/images/check-selected-hdpi.png       (gingerbread/images/check-selected-hdpi.png)
   skin/gingerbread/images/check-unselected-hdpi.png     (gingerbread/images/check-unselected-hdpi.png)
   skin/gingerbread/images/radio-selected-hdpi.png       (gingerbread/images/radio-selected-hdpi.png)
   skin/gingerbread/images/radio-unselected-hdpi.png     (gingerbread/images/radio-unselected-hdpi.png)
   skin/gingerbread/images/dropmarker-hdpi.png           (gingerbread/images/dropmarker-hdpi.png)
   skin/gingerbread/images/ratings-18.png                (images/ratings-18.png)
   skin/gingerbread/images/favicon-default-32.png        (gingerbread/images/favicon-default-32.png)
   skin/gingerbread/images/endcap-default-bg.png         (gingerbread/images/endcap-default-bg.png)
@@ -353,16 +355,17 @@ chrome.jar:
   skin/honeycomb/images/forward-default-hdpi.png        (honeycomb/images/forward-default-hdpi.png)
   skin/honeycomb/images/downloads-default-hdpi.png      (honeycomb/images/downloads-default-hdpi.png)
   skin/honeycomb/images/settings-default-hdpi.png       (honeycomb/images/settings-default-hdpi.png)
   skin/honeycomb/images/preferences-default-hdpi.png    (honeycomb/images/preferences-default-hdpi.png)
   skin/honeycomb/images/row-header-bg.png             (honeycomb/images/row-header-bg.png)
   skin/honeycomb/images/console-default-hdpi.png        (honeycomb/images/console-default-hdpi.png)
   skin/honeycomb/images/newtab-default-hdpi.png         (honeycomb/images/newtab-default-hdpi.png)
   skin/honeycomb/images/newtab-default-tablet-hdpi.png (honeycomb/images/newtab-default-tablet-hdpi.png)
+  skin/honeycomb/images/newtab-tabmenu-tablet-hdpi.png (honeycomb/images/newtab-tabmenu-tablet-hdpi.png)
   skin/honeycomb/images/tab-active-hdpi.png           (honeycomb/images/tab-active-hdpi.png)
   skin/honeycomb/images/tab-inactive-hdpi.png         (honeycomb/images/tab-inactive-hdpi.png)
   skin/honeycomb/images/tab-closed-hdpi.png           (honeycomb/images/tab-closed-hdpi.png)
   skin/honeycomb/images/tab-reopen-hdpi.png           (honeycomb/images/tab-reopen-hdpi.png)
   skin/honeycomb/images/tab-reopen-tablet-hdpi.png    (honeycomb/images/tab-reopen-tablet-hdpi.png)
   skin/honeycomb/images/remotetabs-48.png             (honeycomb/images/remotetabs-48.png)
   skin/honeycomb/images/remotetabs-32.png             (honeycomb/images/remotetabs-32.png)
   skin/honeycomb/images/mozilla-32.png                (images/mozilla-32.png)
@@ -380,16 +383,17 @@ chrome.jar:
   skin/honeycomb/images/locked-hdpi.png               (honeycomb/images/locked-hdpi.png)
   skin/honeycomb/images/close-default-hdpi.png        (honeycomb/images/close-default-hdpi.png)
   skin/honeycomb/images/close-default-tablet-hdpi.png (honeycomb/images/close-default-tablet-hdpi.png)
   skin/honeycomb/images/close-active-hdpi.png         (honeycomb/images/close-active-hdpi.png)
   skin/honeycomb/images/close-active-tablet-hdpi.png  (honeycomb/images/close-active-tablet-hdpi.png)
   skin/honeycomb/images/close-background-hdpi.png     (honeycomb/images/close-background-hdpi.png)
   skin/honeycomb/images/close-inactive-tab-hdpi.png   (honeycomb/images/close-inactive-tab-hdpi.png)
   skin/honeycomb/images/close-inactive-tab-tablet-hdpi.png   (honeycomb/images/close-inactive-tab-tablet-hdpi.png)
+  skin/honeycomb/images/checkmark-hdpi.png                 (images/checkmark-hdpi.png)
   skin/honeycomb/images/check-30.png                  (images/check-30.png)
   skin/honeycomb/images/check-selected-hdpi.png       (honeycomb/images/check-selected-hdpi.png)
   skin/honeycomb/images/check-unselected-hdpi.png     (honeycomb/images/check-unselected-hdpi.png)
   skin/honeycomb/images/search-glass-30.png           (honeycomb/images/search-glass-30.png)
   skin/honeycomb/images/search-clear-30.png           (honeycomb/images/search-clear-30.png)
   skin/honeycomb/images/section-expanded-16.png       (images/section-expanded-16.png)
   skin/honeycomb/images/section-collapsed-16.png      (images/section-collapsed-16.png)
   skin/honeycomb/images/task-switch-hdpi.png          (honeycomb/images/task-switch-hdpi.png)
--- a/mobile/themes/core/tablet.css
+++ b/mobile/themes/core/tablet.css
@@ -148,18 +148,66 @@ documenttab[selected="true"] > vbox > st
   max-width: -moz-calc(0.75 * @urlbar_max_width@);
   min-width: -moz-calc(0.75 * @urlbar_max_width@);
 }
 
 #identity-container[tablet] pageaction {
   width: 100%;
 }
 
+arrowbox {
+  -moz-stack-sizing: ignore;
+}
+
+#tabs-popup-list {
+  min-width: @dialog_width@;
+  max-width: @dialog_width@;
+  overflow-x: no-display;
+}
+
+#tabs-popup-newtab-button,
+.tab-popup-item {
+  -moz-box-align: center;
+}
+
+.tab-popup-item-box {
+  -moz-box-flex: 1;
+  -moz-box-align: center;
+}
+
+.documenttab-popup-favicon {
+  width: 32px;
+  height: 32px;
+}
+
+.tab-popup-item.selected .documenttab-popup-checkmark {
+  list-style-image: url("chrome://browser/skin/images/checkmark-hdpi.png");
+}
+
+.documenttab-popup-label {
+  -moz-box-flex: 1;
+}
+
+.documenttab-popup-checkmark {
+  min-width: 26px; /* size of the checkmark image */
+}
+
+#tabs-popup-newtab-button {
+  -moz-box-flex: 1;
+}
+
 %ifndef honeycomb
 @media (min-width: @tablet_panel_minwidth@) {
   #awesome-panels {
     -moz-box-shadow: 0px 0px @shadow_width_small@ black;
   }
   #search-engines-popup {
     max-width: -moz-calc(@tablet_panel_minwidth@);
   }
 }
+.documenttab-popup-closebutton {
+  list-style-image: url("chrome://browser/skin/images/close-default-hdpi.png");
+}
+
+#tabs-popup-newtab-button {
+  list-style-image: url("chrome://browser/skin/images/newtab-hdpi.png");
+}
 %endif