From 16aae528a8986c2c25b60341a77366a68f5aaa2f Mon Sep 17 00:00:00 2001 From: Matti Tahvonen Date: Wed, 13 Apr 2011 11:20:07 +0000 Subject: [PATCH] #6440: mode to open root nodes by mouse over only svn changeset:18266/svn branch:6.6 --- .../terminal/gwt/client/ui/VMenuBar.java | 63 ++++++++++++++++++- src/com/vaadin/ui/MenuBar.java | 29 +++++++++ .../tests/components/menubar/MenuBarTest.java | 13 ++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java index 56b7d198e0..c609eb846b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java @@ -8,8 +8,10 @@ import java.util.Iterator; import java.util.List; import java.util.Stack; +import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.FocusEvent; @@ -25,6 +27,7 @@ import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasHTML; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.RootPanel; @@ -36,6 +39,7 @@ import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.TooltipInfo; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.VTooltip; public class VMenuBar extends SimpleFocusablePanel implements Paintable, @@ -63,6 +67,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, // associated protected static final Command emptyCommand = null; + public static final String OPEN_ROOT_MENU_ON_HOWER = "ormoh"; + /** Widget fields **/ protected boolean subMenu; protected ArrayList items; @@ -85,6 +91,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, } }); + private boolean openRootOnHover; + public VMenuBar() { // Create an empty horizontal menubar this(false, null); @@ -182,6 +190,9 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, if (client.updateComponent(this, uidl, true)) { return; } + + openRootOnHover = uidl.getBooleanAttribute(OPEN_ROOT_MENU_ON_HOWER); + enabled = !uidl.getBooleanAttribute("disabled"); // For future connections @@ -478,6 +489,8 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, break; case Event.ONMOUSEOVER: + LazyCloser.cancelClosing(); + if (isEnabled() && targetItem.isEnabled()) { itemOver(targetItem); } @@ -485,6 +498,7 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, case Event.ONMOUSEOUT: itemOut(targetItem); + LazyCloser.schedule(); break; } } else if (subMenu && DOM.eventGetType(e) == Event.ONCLICK && subMenu) { @@ -542,8 +556,12 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, * @param item */ public void itemOver(CustomMenuItem item) { - if ((subMenu || menuVisible) && !item.isSeparator()) { + if ((openRootOnHover || subMenu || menuVisible) && !item.isSeparator()) { setSelected(item); + if (!subMenu && openRootOnHover && !menuVisible) { + menuVisible = true; // start opening menus + LazyCloser.prepare(this); + } } if (menuVisible && visibleChildMenu != item.getSubMenu() @@ -571,6 +589,49 @@ public class VMenuBar extends SimpleFocusablePanel implements Paintable, } } + /** + * Used to autoclose submenus when they the menu is in a mode which opens + * root menus on mouse hover. + */ + private static class LazyCloser extends Timer { + static LazyCloser INSTANCE; + private VMenuBar activeRoot; + + @Override + public void run() { + activeRoot.hideChildren(); + activeRoot.setSelected(null); + activeRoot.menuVisible = false; + activeRoot = null; + } + + public static void cancelClosing() { + if (INSTANCE != null) { + INSTANCE.cancel(); + } + } + + public static void prepare(VMenuBar vMenuBar) { + if (INSTANCE == null) { + INSTANCE = new LazyCloser(); + } + if (INSTANCE.activeRoot == vMenuBar) { + INSTANCE.cancel(); + } else if (INSTANCE.activeRoot != null) { + INSTANCE.cancel(); + INSTANCE.run(); + } + INSTANCE.activeRoot = vMenuBar; + } + + public static void schedule() { + if (INSTANCE != null && INSTANCE.activeRoot != null) { + INSTANCE.schedule(750); + } + } + + } + /** * Shows the child menu of an item. The caller must ensure that the item has * a submenu. diff --git a/src/com/vaadin/ui/MenuBar.java b/src/com/vaadin/ui/MenuBar.java index b7da82675e..d629e912c9 100644 --- a/src/com/vaadin/ui/MenuBar.java +++ b/src/com/vaadin/ui/MenuBar.java @@ -49,6 +49,8 @@ public class MenuBar extends AbstractComponent { private MenuItem moreItem; + private boolean openRootOnHover; + /** Paint (serialise) the component for the client. */ @Override public void paintContent(PaintTarget target) throws PaintException { @@ -56,6 +58,8 @@ public class MenuBar extends AbstractComponent { // Superclass writes any common attributes in the paint target. super.paintContent(target); + target.addAttribute(VMenuBar.OPEN_ROOT_MENU_ON_HOWER, openRootOnHover); + target.startTag("options"); if (submenuIcon != null) { @@ -364,6 +368,31 @@ public class MenuBar extends AbstractComponent { public MenuItem getMoreMenuItem() { return moreItem; } + + /** + * Using this method menubar can be put into a special mode where the root + * level menu opens without clicking on the menu. In this mode the menu also + * closes itself if the mouse is moved out of the opened menu. + * + * @param b + */ + public void setOpenRootOnHover(boolean b) { + if(b != openRootOnHover) { + openRootOnHover = b; + requestRepaint(); + } + } + + /** + * Detects whether the menubar is in a mode where root menus are + * automatically opened when the mouse cursor is moved over the menubar. + * Normally root menu opens only by clicking on the menu. + * + * @return true if the root menus open without click + */ + public boolean isOpenRootOnHover() { + return openRootOnHover; + } /** * This interface contains the layer for menu commands of the diff --git a/tests/src/com/vaadin/tests/components/menubar/MenuBarTest.java b/tests/src/com/vaadin/tests/components/menubar/MenuBarTest.java index 1b11c24f78..2a84b1d0fa 100644 --- a/tests/src/com/vaadin/tests/components/menubar/MenuBarTest.java +++ b/tests/src/com/vaadin/tests/components/menubar/MenuBarTest.java @@ -19,6 +19,7 @@ public class MenuBarTest extends AbstractComponentTest { private int subLevels = -1; private int subMenuDensity = -1; private Integer subMenuSeparatorDensity = null; + private Boolean openRootMenuOnHover = false; private int iconInterval = -1; private Integer iconSize; private Integer disabledDensity; @@ -38,6 +39,9 @@ public class MenuBarTest extends AbstractComponentTest { createSubMenuDensitySelect(CATEGORY_MENU_ITEMS); createSubMenuSeparatorDensitySelect(CATEGORY_MENU_ITEMS); + createBooleanAction("OpenRootMenuOnHover", CATEGORY_FEATURES, + openRootMenuOnHover, setOpenRootOnHover); + createMenuItemIconIntervalSelect(CATEGORY_MENU_ITEM_STATES); createMenuIconsSizeSelect(CATEGORY_MENU_ITEM_STATES); createMenuItemDisabledDensitySelect(CATEGORY_MENU_ITEM_STATES); @@ -185,6 +189,15 @@ public class MenuBarTest extends AbstractComponentTest { } }; + private Command setOpenRootOnHover = new Command() { + + public void execute(MenuBar c, Boolean value, Object data) { + openRootMenuOnHover = value; + c.setOpenRootOnHover(value); + } + + }; + private Command selectIcon = new Command() { public void execute(MenuBar c, Integer value, Object data) { -- 2.39.5