From 5babab8d3c70cdc16ce777fcec015138074adb6c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marko=20Gr=C3=B6nroos?= Date: Thu, 26 Jun 2008 13:29:27 +0000 Subject: [PATCH] Copied the new MenuBar component from Incubator to Trunk. Fixes #1852. svn changeset:4955/svn branch:trunk --- WebContent/ITMILL/themes/default/styles.css | 58 ++ .../terminal/gwt/client/DefaultWidgetSet.java | 6 + .../terminal/gwt/client/ui/IMenuBar.java | 152 ++++++ .../toolkit/tests/tickets/Ticket1598.java | 110 ++++ src/com/itmill/toolkit/ui/MenuBar.java | 511 ++++++++++++++++++ 5 files changed, 837 insertions(+) create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/IMenuBar.java create mode 100644 src/com/itmill/toolkit/tests/tickets/Ticket1598.java create mode 100644 src/com/itmill/toolkit/ui/MenuBar.java diff --git a/WebContent/ITMILL/themes/default/styles.css b/WebContent/ITMILL/themes/default/styles.css index a8005c02e6..e3e43cde7a 100644 --- a/WebContent/ITMILL/themes/default/styles.css +++ b/WebContent/ITMILL/themes/default/styles.css @@ -602,6 +602,64 @@ input.i-modified, .i-gridlayout-spacing .i-gridlayout-firstrow { padding-top: 0; } +/*Set the style for the menu bar itself */ +.i-menubar table{ + border-right: 2px solid #c6cbcc; + border-bottom: 2px solid #c6cbcc; + border-top: 1px solid #d0d4d5; + border-left: 1px solid #d0d4d5; + + padding 3px; +} + +/*Set the style for the main menu menu items */ +.i-menubar .gwt-MenuItem { + cursor : hand; + cursor : pointer; + + padding : 1px 10px; + margin : 1px 10px; + } + +/*Set the style for the selected main menu menu item */ +.i-menubar .gwt-MenuItem-selected { + background-color : #fff; + } + + +/*Set the style for the submenus */ +.gwt-MenuBar { + font-size : 12px; + padding : 3px; + font-family: "Trebuchet MS", geneva, helvetica, arial, tahoma, verdana, sans-serif; + color: #464f52; + + border-right: 2px solid #c6cbcc; + border-bottom: 2px solid #c6cbcc; + border-top: 1px solid #d0d4d5; + border-left: 1px solid #d0d4d5; +} + +/*Set style for the submenu menu items */ +.gwt-MenuBar .gwt-MenuItem { + cursor : hand; + cursor : pointer; + + //border : 1px solid #999; + //background-color : #eee; + padding : 2px 10px; +} + +/*Set style for the selected submenu item */ +.gwt-MenuBar .gwt-MenuItem-selected { + background-color : #fff; +} + +.gwt-MenuItem p{ + padding : 0; + margin : 0; + line-height : normal; + } .i-Notification { font-family: "Trebuchet MS", geneva, helvetica, arial, tahoma, verdana, sans-serif; diff --git a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java index d43c26d6b1..227becbe7c 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java @@ -25,6 +25,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.IHorizontalExpandLayout; import com.itmill.toolkit.terminal.gwt.client.ui.ILabel; import com.itmill.toolkit.terminal.gwt.client.ui.ILink; import com.itmill.toolkit.terminal.gwt.client.ui.IListSelect; +import com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar; import com.itmill.toolkit.terminal.gwt.client.ui.INativeSelect; import com.itmill.toolkit.terminal.gwt.client.ui.IOptionGroup; import com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutHorizontal; @@ -190,6 +191,9 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IAccordion" .equals(className)) { return new IAccordion(); + } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar" + .equals(className)) { + return new IMenuBar(); } return new IUnknownComponent(); @@ -320,6 +324,8 @@ public class DefaultWidgetSet implements WidgetSet { } else { return "com.itmill.toolkit.terminal.gwt.client.ui.IExpandLayout"; } + } else if ("menubar".equals(tag)) { + return "com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar"; } return "com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent"; diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IMenuBar.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IMenuBar.java new file mode 100644 index 0000000000..58d6f6fdfc --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IMenuBar.java @@ -0,0 +1,152 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +import java.util.Iterator; +import java.util.Stack; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.ui.MenuBar; +import com.google.gwt.user.client.ui.MenuItem; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; + +public class IMenuBar extends MenuBar implements Paintable { + + /** Set the CSS class name to allow styling. */ + public static final String CLASSNAME = "i-menubar"; + + /** Component identifier in UIDL communications. */ + protected String uidlId; + + /** Reference to the server connection object. */ + protected ApplicationConnection client; + + /** A host reference for the Command objects */ + protected final IMenuBar hostReference = this; + + /** + * The constructor should first call super() to initialize the component and + * then handle any initialization relevant to IT Mill Toolkit. + */ + public IMenuBar() { + // The superclass has a lot of relevant initialization + super(); + + // This method call of the Paintable interface sets the component + // style name in DOM tree + setStyleName(CLASSNAME); + } + + /** + * This method must be implemented to update the client-side component from + * UIDL data received from server. + * + * This method is called when the page is loaded for the first time, and + * every time UI changes in the component are received from the server. + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // This call should be made first. Ensure correct implementation, + // and let the containing layout manage caption, etc. + if (client.updateComponent(this, uidl, true)) { + return; + } + + // Save reference to server connection object to be able to send + // user interaction later + this.client = client; + + // Save the UIDL identifier for the component + uidlId = uidl.getId(); + + // Empty the menu every time it receives new information + if (!this.getItems().isEmpty()) { + this.clearItems(); + } + + /* Get tree received from server and actualize it in the GWT-MenuBar */ + + // For GWT 1.5 + // this.setAnimationEnabled(uidl.getBooleanAttribute("animationEnabled")); + UIDL items = uidl.getChildUIDL(1); + Iterator itr = items.getChildIterator(); + Stack iteratorStack = new Stack(); + Stack menuStack = new Stack(); + MenuBar currentMenu = this; + + // Construct an empty command to be used when the item has no command + // associated + Command emptyCommand = new Command() { + public void execute() { + } + }; + + while (itr.hasNext()) { + UIDL item = (UIDL) itr.next(); + MenuItem menuItem = null; // For receiving the item + + String itemText = item.getStringAttribute("text"); + final int itemId = item.getIntAttribute("id"); + + boolean itemHasCommand = item.getBooleanAttribute("command"); + + // Construct html from the text and the optional icon + if (!item.hasAttribute("icon")) { + itemText = "

" + itemText + "

"; + } else { + itemText = "

" + + "" + + itemText + "

"; + } + + // Check if we need to attach a command to this item + if (itemHasCommand) { + // Construct a command that fires onMenuClick(int) with the + // item's id-number + Command normalCommand = new Command() { + public void execute() { + hostReference.onMenuClick(itemId); + } + }; + + menuItem = currentMenu.addItem(itemText, true, normalCommand); + + } else { + menuItem = currentMenu.addItem(itemText, true, emptyCommand); + } + + if (item.getChildCount() > 0) { + menuStack.push(currentMenu); + iteratorStack.push(itr); + itr = item.getChildIterator(); + currentMenu = new MenuBar(true); + menuItem.setSubMenu(currentMenu); + } + + if (!itr.hasNext() && !iteratorStack.empty()) { + itr = (Iterator) iteratorStack.pop(); + currentMenu = (MenuBar) menuStack.pop(); + } + }// while + + }// updateFromUIDL + + /** + * This is called by the items in the menu and it communicates the + * information to the server + * + * @param clickedItemId + * id of the item that was clicked + */ + public void onMenuClick(int clickedItemId) { + // Updating the state to the server can not be done before + // the server connection is known, i.e., before updateFromUIDL() + // has been called. + if (uidlId != null && client != null) { + // Communicate the user interaction parameters to server. This call + // will initiate an AJAX request to the server. + client.updateVariable(uidlId, "clickedId", clickedItemId, true); + } + } +}// class IMenuBar diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket1598.java b/src/com/itmill/toolkit/tests/tickets/Ticket1598.java new file mode 100644 index 0000000000..90ecadb9a0 --- /dev/null +++ b/src/com/itmill/toolkit/tests/tickets/Ticket1598.java @@ -0,0 +1,110 @@ +package com.itmill.toolkit.tests.tickets; + +import java.util.ArrayList; +import java.util.List; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.terminal.ThemeResource; +import com.itmill.toolkit.ui.MenuBar; +import com.itmill.toolkit.ui.Window; +import com.itmill.toolkit.ui.MenuBar.Command; +import com.itmill.toolkit.ui.MenuBar.Item; + +public class Ticket1598 extends Application { + + Window main = new Window("MenuBar test"); + + final MenuBar menuBar = new MenuBar(); + + public void init() { + setMainWindow(main); + setTheme("default"); + + List itemList = new ArrayList(); + // Populate the menubar + for (int i = 0; i < 5; i++) { + itemList.add(menuBar.addItem(new String("Menu " + i), null, null)); + } + + Item first = (Item) itemList.get(0); + + for (int i = 0; i < 5; i++) { + first.addItem(new String("Submenu item" + i), null, new Command() { + + public void menuSelected(Item selected) { + main.showNotification("Action " + selected.getText()); + } + }); + } + + Item firstSecond = (Item) first.getChildren().get(1); + + for (int i = 0; i < 3; i++) { + firstSecond.addItem(new String("Subsubmenu item" + i), null, + new Command() { + + public void menuSelected(Item selected) { + main.showNotification("Action " + + selected.getText()); + } + }); + } + + Item second = (Item) menuBar.getItems().get(1); + + for (int i = 0; i < 5; i++) { + second.addItem(new String("Second submenu item" + i), null, + new Command() { + + public void menuSelected(Item selected) { + main.showNotification("Action " + + selected.getText()); + } + }); + } + + Item third = (Item) menuBar.getItems().get(2); + third.setIcon(new ThemeResource("icons/16/document.png")); + + for (int i = 2; i <= 3; i++) + ((Item) menuBar.getItems().get(i)).setCommand(new Command() { + + public void menuSelected(Item selectedItem) { + main.showNotification("Action " + selectedItem.getText()); + } + }); + + final Item fourth = (Item) menuBar.getItems().get(3); + fourth.setText("Toggle animation"); + + fourth.setCommand(new Command() { + public void menuSelected(Item selected) { + menuBar + .addItemBefore("No animation yet...", null, null, + fourth); + // menuBar.setAnimation(!menuBar.hasAnimation()); + } + }); + + final Item last = (Item) menuBar.getItems().get(menuBar.getSize() - 1); + last.setText("Remove me!"); + + // A command for removing the selected menuitem + Command removeCommand = new Command() { + + public void menuSelected(Item selected) { + Item parent = selected.getParent(); + if (parent != null) { + parent.removeChild(selected); + } else { + menuBar.removeItem(selected); + } + } + }; + + last.setCommand(removeCommand); + + main.addComponent(menuBar); + + } +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/ui/MenuBar.java b/src/com/itmill/toolkit/ui/MenuBar.java new file mode 100644 index 0000000000..da1d4577fa --- /dev/null +++ b/src/com/itmill/toolkit/ui/MenuBar.java @@ -0,0 +1,511 @@ +package com.itmill.toolkit.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import com.itmill.toolkit.terminal.PaintException; +import com.itmill.toolkit.terminal.PaintTarget; +import com.itmill.toolkit.terminal.Resource; + +/** + * The top-level menu class. This can contain MenuItems, which in turn can + * contain Submenus and MenuCommands. + * + */ +public class MenuBar extends AbstractComponent { + + // Items of the top-level menu + private List menuItems; + + // Number of items in this menu + private static int numberOfItems = 0; + + private boolean animationEnabled; + + /** Tag is the UIDL element name for client-server communications. */ + public java.lang.String getTag() { + return "menubar"; + } + + /** Paint (serialize) the component for the client. */ + public void paintContent(PaintTarget target) throws PaintException { + + // Superclass writes any common attributes in the paint target. + super.paintContent(target); + + // Stack for list iterators + Stack iteratorStack = new Stack(); + + target.startTag("options"); + target.addAttribute("animationEnabled", animationEnabled); + target.endTag("options"); + target.startTag("items"); + + Iterator itr = menuItems.iterator(); + + // This generates the tree from the contents of the menu + while (itr.hasNext()) { + + Item item = (Item) itr.next(); + + target.startTag("item"); + + target.addAttribute("text", item.getText()); + target.addAttribute("id", item.getId()); + + Command command = item.getCommand(); + if (command != null) { + target.addAttribute("command", true); + } else { + target.addAttribute("command", false); + } + + Resource icon = item.getIcon(); + if (icon != null) { + target.addAttribute("icon", icon); + } + + if (item.hasChildren()) { + iteratorStack.push(itr); // For later use + + // Go through the children + itr = item.getChildren().iterator(); + } else { + target.endTag("item"); // Item had no children, end description + } + + if (!itr.hasNext() && !iteratorStack.empty()) { // The end submenu + itr = (Iterator) iteratorStack.pop(); + target.endTag("item"); + } + + } + + target.endTag("items"); + } + + /** Deserialize changes received from client. */ + public void changeVariables(Object source, Map variables) { + Stack items = new Stack(); + boolean found = false; + + if (variables.containsKey("clickedId")) { + + Integer clickedId = (Integer) variables.get("clickedId"); + Iterator itr = this.getItems().iterator(); + while (itr.hasNext()) { + items.push((Item) itr.next()); + } + + Item tmpItem = null; + + // Go through all the items in the menu + while (!found && !items.empty()) { + tmpItem = (Item) items.pop(); + found = (clickedId.intValue() == tmpItem.getId()); + + if (tmpItem.hasChildren()) { + itr = tmpItem.getChildren().iterator(); + while (itr.hasNext()) { + items.push(itr.next()); + } + } + + } + // If we got the clicked item, launch the command. + if (found) { + tmpItem.getCommand().menuSelected(tmpItem); + }// while + }// if + }// changeVariables + + /** + * Constructs an empty, horizontal menu + */ + public MenuBar() { + menuItems = new ArrayList(); + animationEnabled = false; + } + + /** + * Add a new item to the menubar. Icon and command can be null, but a + * caption must be given. + * + * @param caption + * the text for the menu item + * @param icon + * the icon for the menu item + * @param command + * the command for the menu item + * @throws IllegalArgumentException + */ + public MenuBar.Item addItem(String caption, Resource icon, + MenuBar.Command command) { + if (caption == null) { + throw new IllegalArgumentException("caption cannot be null"); + } + Item newItem = new Item(caption, icon, command); + menuItems.add(newItem); + requestRepaint(); + + return newItem; + + } + + /** + * Add an item before some item. If the given item does not exist the item + * is added at the end of the menu. Icon and command can be null, but a + * caption must be given. + * + * @param caption + * the text for the menu item + * @param icon + * the icon for the menu item + * @param command + * the command for the menu item + * @param itemToAddBefore + * the item that will be after the new item + * @throws IllegalArgumentException + */ + public MenuBar.Item addItemBefore(String caption, Resource icon, + MenuBar.Command command, MenuBar.Item itemToAddBefore) { + if (caption == null) { + throw new IllegalArgumentException("caption cannot be null"); + } + + Item newItem = new Item(caption, icon, command); + if (menuItems.contains(itemToAddBefore)) { + int index = menuItems.indexOf(itemToAddBefore); + menuItems.add(index, newItem); + + } else { + menuItems.add(newItem); + } + + requestRepaint(); + + return newItem; + } + + /** + * Returns a list with all the MenuItem objects in the menubar + * + * @return a list containing the MenuItem objects in the menubar + */ + public java.util.List getItems() { + return menuItems; + } + + /** + * Remove first occurrence the specified item from the main menu + * + * @param item + * The item to be removed + */ + public void removeItem(MenuBar.Item item) { + if (item != null) { + menuItems.remove(item); + } + requestRepaint(); + } + + /** + * Empty the menubar + */ + public void removeItems() { + menuItems.clear(); + requestRepaint(); + } + + /** + * Returns the size of the menu. + * + * @return The size of the menu + */ + public int getSize() { + return menuItems.size(); + } + + /** + * Enable or disable animated menubar appearance. Animation is disabled by + * default. + * + * @param hasAnimation + */ + public void setAnimation(boolean animation) { + animationEnabled = animation; + } + + /** + * Returns true if the animation is enabled. Animation of this class is + * disabled by default. + * + * @return true if the animation is enabled + * + */ + public boolean hasAnimation() { + return animationEnabled; + } + + /** + * This interface contains the layer for menu commands of the MenuBar class . + * It's method will fire when the user clicks on the containing MenuItem. + * The selected item is given as an argument. + */ + public interface Command { + public void menuSelected(MenuBar.Item selectedItem); + } + + /** + * A composite class for menu items and submenus. You can set commands to be + * fired on user click by implementing the MenuBar.Command interface. + * + */ + public class Item { + + /** Private members * */ + private int itsId; + private Command itsCommand; + private String itsText; + private List itsChildren; + private Resource itsIcon; + private Item itsParent; + + /** + * Constructs a new menu item that can optionally have an icon and a + * command associated with it. Icon and command can be null, but a + * caption must be given. + * + * @param text + * The text associated with the command + * @param command + * The command to be fired + * @throws IllegalArgumentException + */ + public Item(String caption, Resource icon, MenuBar.Command command) { + if (caption == null) { + throw new IllegalArgumentException("caption cannot be null"); + } + itsId = ++numberOfItems; + itsText = caption; + itsIcon = icon; + itsCommand = command; + } + + /** + * Checks if the item has children (if it is a submenu). + * + * @return True if this item has children + */ + public boolean hasChildren() { + return itsChildren != null; + } + + /** + * Add a new item inside this item, thus creating a submenu. Icon and + * command can be null, but a caption must be given. + * + * @param caption + * the text for the menu item + * @param icon + * the icon for the menu item + * @param command + * the command for the menu item + */ + public MenuBar.Item addItem(String caption, Resource icon, + MenuBar.Command command) { + if (caption == null) { + throw new IllegalArgumentException("caption cannot be null"); + } + + if (itsChildren == null) { + itsChildren = new ArrayList(); + } + + Item newItem = new Item(caption, icon, command); + + // The only place where the parent is set + newItem.setParent(this); + itsChildren.add(newItem); + + requestRepaint(); + + return newItem; + } + + /** + * Add an item before some item. If the given item does not exist the + * item is added at the end of the menu. Icon and command can be null, + * but a caption must be given. + * + * @param caption + * the text for the menu item + * @param icon + * the icon for the menu item + * @param command + * the command for the menu item + * @param itemToAddBefore + * the item that will be after the new item + * + */ + public MenuBar.Item addItemBefore(String caption, Resource icon, + MenuBar.Command command, MenuBar.Item itemToAddBefore) { + + Item newItem = null; + + if (hasChildren() && itsChildren.contains(itemToAddBefore)) { + int index = itsChildren.indexOf(itemToAddBefore); + newItem = new Item(caption, icon, command); + newItem.setParent(this); + itsChildren.add(index, newItem); + + } else { + newItem = addItem(caption, icon, command); + } + + requestRepaint(); + + return newItem; + } + + /** + * For the associated command. + * + * @return The associated command, or null if there is none + */ + public Command getCommand() { + return itsCommand; + } + + /** + * Gets the objects icon. + * + * @return The icon of the item, null if the item doesn't have an icon + */ + public Resource getIcon() { + return itsIcon; + } + + /** + * For the containing item. This will return null if the item is in the + * top-level menubar. + * + * @return The containing MenuBar.MenuItem, or null if there is none + */ + public MenuBar.Item getParent() { + return itsParent; + } + + /** + * This will return the children of this item or null if there are none. + * + * @return List of children items, or null if there are none + */ + public java.util.List getChildren() { + return itsChildren; + } + + /** + * Gets the objects text + * + * @return The text + */ + public java.lang.String getText() { + return itsText; + } + + /** + * Returns the number of children. + * + * @return The number of child items + */ + public int getSize() { + return itsChildren.size(); + } + + /** + * Get the unique identifier for this item. + * + * @return The id of this item + */ + public int getId() { + return itsId; + } + + /** + * Set the command for this item. Set null to remove. + * + * @param command + * The MenuCommand of this item + */ + public void setCommand(MenuBar.Command command) { + itsCommand = command; + } + + /** + * Sets the icon. Set null to remove. + * + * @param icon + * The icon for this item + */ + public void setIcon(Resource icon) { + itsIcon = icon; + requestRepaint(); + } + + /** + * Set the text of this object. + * + * @param text + * Text for this object + */ + public void setText(java.lang.String text) { + if (text != null) { + itsText = text; + } + requestRepaint(); + } + + /** + * Remove the first occurrence of the item. + * + * @param item + * The item to be removed + */ + public void removeChild(MenuBar.Item item) { + if (item != null && itsChildren != null) { + itsChildren.remove(item); + if (itsChildren.isEmpty()) { + itsChildren = null; + } + } + requestRepaint(); + } + + /** + * Empty the list of children items. + */ + public void removeChildren() { + if (itsChildren != null) { + itsChildren.clear(); + itsChildren = null; + } + requestRepaint(); + } + + /** + * Set the parent of this item. This is called by the addItem method. + * + * @param parent + * The parent item + */ + protected void setParent(MenuBar.Item parent) { + itsParent = parent; + } + + }// class MenuItem + +}// class MenuBar -- 2.39.5