Bug 589146 - Firefox menu items should display sub-menu on a slightly delayed hover. r=gavin
authorDão Gottwald <dao@mozilla.com>
Sat, 22 Jan 2011 12:21:53 +0100
changeset 61135 dc22e591c18457f09a547ad620960e5fe63d5a3e
parent 61134 0718ec1414744ca58d21982ace95750039652626
child 61136 52d6439d7d19cc07fae8dc50f82c93faecf13a99
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersgavin
bugs589146
milestone2.0b10pre
Bug 589146 - Firefox menu items should display sub-menu on a slightly delayed hover. r=gavin
browser/base/content/browser-appmenu.inc
browser/base/content/browser.css
browser/base/content/urlbarBindings.xml
toolkit/content/xul.css
toolkit/themes/gnomestripe/global/menu.css
toolkit/themes/winstripe/global/menu.css
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -34,27 +34,30 @@
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 <menupopup id="appmenu-popup"
+           onpopupshowing="if (event.target == this) {
+                             updateEditUIVisibility();
 #ifdef MOZ_SERVICES_SYNC
-           onpopupshowing="updateEditUIVisibility();gSyncUI.updateUI();">
-#else
-           onpopupshowing="updateEditUIVisibility();">
+                             gSyncUI.updateUI();
 #endif
+                             return;
+                           }
+                           if (event.target.parentNode.parentNode.parentNode.parentNode == this)
+                             this._currentPopup = event.target;">
   <hbox>
     <vbox id="appmenuPrimaryPane">
       <splitmenu id="appmenu_newTab"
                  label="&tabCmd.label;"
-                 command="cmd_newNavigatorTab"
-                 key="key_newNavigatorTab">
+                 command="cmd_newNavigatorTab">
           <menupopup>
             <menuitem id="appmenu_newTab_popup"
                       label="&tabCmd.label;"
                       command="cmd_newNavigatorTab"
                       key="key_newNavigatorTab"/>
             <menuitem id="appmenu_newNavigator"
                       label="&newNavigatorCmd.label;"
                       command="cmd_newNavigator"
@@ -111,18 +114,17 @@
                 command="Browser:SavePage"
                 key="key_savePage"/>
       <menuitem id="appmenu_sendLink"
                 label="&sendPageCmd.label;"
                 command="Browser:SendLink"/>
       <splitmenu id="appmenu_print"
                  iconic="true"
                  label="&printCmd.label;"
-                 command="cmd_print"
-                 key="printKb">
+                 command="cmd_print">
           <menupopup>
             <menuitem id="appmenu_print_popup"
                       class="menuitem-iconic"
                       label="&printCmd.label;"
                       command="cmd_print"
                       key="printKb"/>
             <menuitem id="appmenu_printPreview"
                       label="&printPreviewCmd.label;"
@@ -198,18 +200,17 @@
                 label="&quitApplicationCmd.label;"
 #endif
                 command="cmd_quitApplication"/>
     </vbox>
     <vbox id="appmenuSecondaryPane">
       <splitmenu id="appmenu_bookmarks"
                  iconic="true"
                  label="&bookmarksMenu.label;"
-                 command="Browser:ShowAllBookmarks"
-                 key="manBookmarkKb">
+                 command="Browser:ShowAllBookmarks">
           <menupopup id="appmenu_bookmarksPopup"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
                      oncommand="BookmarksEventHandler.onCommand(event);"
                      onclick="BookmarksEventHandler.onClick(event);"
                      onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
                                      if (!this.parentNode._placesView)
@@ -262,18 +263,17 @@
                       label="&appMenuUnsorted.label;"
                       oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
                       class="menuitem-iconic"/>
           </menupopup>
       </splitmenu>
       <splitmenu id="appmenu_history"
                  iconic="true"
                  label="&historyMenu.label;"
-                 command="Browser:ShowAllHistory"
-                 key="showAllHistoryKb">
+                 command="Browser:ShowAllHistory">
           <menupopup id="appmenu_historyMenupopup"
                      placespopup="true"
                      oncommand="this.parentNode._placesView._onCommand(event);"
                      onclick="checkForMiddleClick(this, event);"
                      onpopupshowing="if (!this.parentNode._placesView)
                                        new HistoryMenu(event);"
                      tooltip="bhTooltip"
                      popupsinherittooltip="true">
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -150,23 +150,28 @@ toolbar[mode="icons"] > #reload-button[d
   background-repeat: no-repeat;
   background-position: bottom left;
 }
 
 splitmenu {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#splitmenu");
 }
 
-.split-menuitem-item {
+.splitmenu-menuitem {
+  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem");
   list-style-image: inherit;
   -moz-image-region: inherit;
 }
 
-.split-menuitem-menu > .menu-text,
-.split-menuitem-menu > .menu-accel-container {
+.splitmenu-menuitem[iconic="true"] {
+  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
+}
+
+.splitmenu-menu > .menu-text,
+:-moz-any(.splitmenu-menu, .splitmenu-menuitem) > .menu-accel-container {
   display: none;
 }
 
 .menuitem-tooltip {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
 }
 
 .menuitem-iconic-tooltip,
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1294,37 +1294,95 @@
           this.updateProgress();
         ]]></body>
       </method>
     </implementation>
   </binding>
 
   <binding id="splitmenu">
     <content>
-      <xul:menuitem anonid="item" flex="1"
-                    class="menuitem-tooltip split-menuitem-item"
-                    xbl:inherits="label,key"/>
-      <xul:menu anonid="menu" class="split-menuitem-menu"
+      <xul:hbox anonid="menuitem" flex="1"
+                class="splitmenu-menuitem"
+                xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/>
+      <xul:menu anonid="menu" class="splitmenu-menu"
+                xbl:inherits="disabled"
                 oncommand="event.stopPropagation();">
         <children includes="menupopup"/>
       </xul:menu>
     </content>
 
     <implementation>
-      <constructor><![CDATA[
-        if (this.getAttribute("iconic") == "true") {
-          this.item.classList.remove("menuitem-tooltip");
-          this.item.classList.add("menuitem-iconic-tooltip");
-          this.item.classList.add("menuitem-iconic");
+      <field name="menuitem" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
+      </field>
+      <field name="menu" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "menu");
+      </field>
+
+      <field name="_menuDelay">600</field>
+
+      <field name="_parentMenupopup"><![CDATA[
+        let node = this.parentNode;
+        while (node) {
+          if (node.localName == "menupopup")
+            break;
+          node = node.parentNode;
         }
-      ]]></constructor>
-      <field name="item" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "item");
-      </field>
+        node;
+      ]]></field>
     </implementation>
+
+    <handlers>
+      <handler event="mouseover"><![CDATA[
+        if (this.getAttribute("active") != "true" &&
+            this.getAttribute("disabled") != "true") {
+          if (this._parentMenupopup._currentPopup)
+            this._parentMenupopup._currentPopup.hidePopup();
+
+          this.setAttribute("active", "true");
+
+          let self = this;
+          setTimeout(function () {
+            if (self.getAttribute("active") == "true")
+              self.menu.open = true;
+          }, this._menuDelay);
+        }
+      ]]></handler>
+
+      <handler event="mouseout"><![CDATA[
+        if (this.menu.open)
+          return;
+
+        let node = event.relatedTarget;
+        while (node) {
+          if (node == this)
+            return;
+          node = node.parentNode;
+        }
+        this.removeAttribute("active");
+      ]]></handler>
+
+      <handler event="popuphidden"><![CDATA[
+        if (event.target == this.firstChild)
+          this.removeAttribute("active");
+      ]]></handler>
+
+      <handler event="click" phase="capturing"><![CDATA[
+        let node = event.originalTarget;
+        while (true) {
+          if (node == this.menuitem)
+            break;
+          if (node == this)
+            return;
+          node = node.parentNode;
+        }
+
+        this._parentMenupopup.hidePopup();
+      ]]></handler>
+    </handlers>
   </binding>
 
   <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
     <implementation>
       <constructor><![CDATA[
         this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
         // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
         // 592424 is fixed
@@ -1338,10 +1396,10 @@
       <constructor><![CDATA[
         this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
         // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
         // 592424 is fixed
         document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
       ]]></constructor>
     </implementation>
   </binding>
-  
+
 </bindings>
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -359,21 +359,21 @@ menuitem[type="radio"] {
 
 menuitem.menuitem-non-iconic {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menubutton-item");
 }
 
 %ifdef MOZ_WIDGET_GTK2
 /********* detection of system setting to use icons in menus ***********/
 @media not all and (-moz-images-in-menus) {
-  menuitem:not([type]):not(.menuitem-with-favicon) > .menu-iconic-left {
+  .menu-iconic-left {
     visibility: hidden;
   }
-  menu > .menu-iconic-left {
-    visibility: hidden;
+  :-moz-any(menuitem[type], .menuitem-with-favicon) > .menu-iconic-left {
+    visibility: visible;
   }
 }
 %endif
 
 /********* menuseparator ***********/
 
 menuseparator {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menuseparator");
--- a/toolkit/themes/gnomestripe/global/menu.css
+++ b/toolkit/themes/gnomestripe/global/menu.css
@@ -39,17 +39,19 @@
 /* ===== menu.css =======================================================
   == Styles used by XUL menu-related elements.
   ======================================================================= */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* ::::: menu/menuitem ::::: */
 
-menu, menuitem {
+menu,
+menuitem,
+.splitmenu-menuitem {
   -moz-appearance: menuitem;
   -moz-box-align: center;
   max-width: 42em;
   color: MenuText;
   font: menu;
   list-style-image: none;
   -moz-image-region: auto;
   /* 3px is the default padding value. See the big comment under moz_gtk_check_menu_item_paint()
@@ -57,23 +59,25 @@ menu, menuitem {
   padding: 0px 3px;
 }
 
 menuitem[default="true"] {
   font-weight: bold;
 }
 
 menu[_moz-menuactive="true"],
-menuitem[_moz-menuactive="true"] {
+menuitem[_moz-menuactive="true"],
+.splitmenu-menuitem[_moz-menuactive="true"] {
   color: -moz-menuhovertext;
   background-color: -moz-menuhover;
 }
 
 menu[disabled="true"],
-menuitem[disabled="true"] {
+menuitem[disabled="true"],
+.splitmenu-menuitem[disabled="true"] {
   color: GrayText;
 }
 
 menubar > menu {
   padding: 0px 4px;
   color: -moz-menubartext;
 }
 
--- a/toolkit/themes/winstripe/global/menu.css
+++ b/toolkit/themes/winstripe/global/menu.css
@@ -40,41 +40,45 @@
   == Styles used by XUL menu-related elements.
   ======================================================================= */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* ::::: menu/menuitem ::::: */
 
 menu,
-menuitem {
+menuitem,
+.splitmenu-menuitem {
   -moz-appearance: menuitem;
   -moz-box-align: center;
   color: MenuText;
   font: menu;
   list-style-image: none;
   -moz-image-region: auto;
 }
 
 menuitem[default="true"] {
   font-weight: bold;
 }
 
 menu[disabled="true"],
 menuitem[disabled="true"],
+.splitmenu-menuitem[disabled="true"],
 menu[_moz-menuactive="true"][disabled="true"],
-menuitem[_moz-menuactive="true"][disabled="true"] {
+menuitem[_moz-menuactive="true"][disabled="true"],
+.splitmenu-menuitem[_moz-menuactive="true"][disabled="true"] {
   color: GrayText;
   text-shadow: none;
 }
 
 @media all and (-moz-windows-classic) {
   menu[disabled="true"],
   menubar > menu[disabled="true"][_moz-menuactive="true"],
-  menuitem[disabled="true"] {
+  menuitem[disabled="true"],
+  .splitmenu-menuitem[disabled="true"] {
     color: ThreeDShadow;
     text-shadow: 1px 1px ThreeDHighlight;
   }
 }
 
 menuitem.spell-suggestion {
   font-weight: bold;
 }
@@ -120,17 +124,18 @@ menuitem.spell-suggestion {
 }
 
 .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
 menu.menu-iconic > .menu-iconic-left,
-menuitem.menuitem-iconic > .menu-iconic-left {
+menuitem.menuitem-iconic > .menu-iconic-left,
+.splitmenu-menuitem[iconic="true"] > .menu-iconic-left {
   -moz-appearance: menuimage;
   padding-top: 2px;
 }
 
 /* ..... menu arrow box ..... */
 
 .menu-right {
   -moz-appearance: menuarrow;
@@ -189,17 +194,18 @@ menubar > menu:-moz-window-inactive {
 /* ::::: menu/menuitems in popups ::::: */
 
 menupopup > menu,
 menupopup > menuitem {
   max-width: 42em;
 }
 
 menu[_moz-menuactive="true"],
-menuitem[_moz-menuactive="true"] {
+menuitem[_moz-menuactive="true"],
+.splitmenu-menuitem[_moz-menuactive="true"] {
   background-color: -moz-menuhover;
   color: -moz-menuhovertext;
 }
 
 /* ::::: menu/menuitems in menulist popups ::::: */
 
 .menulist-menupopup > menuitem,
 menulist > menupopup > menuitem,