|
|
@@ -14,14 +14,16 @@ import com.google.gwt.user.client.Element; |
|
|
|
import com.google.gwt.user.client.Event; |
|
|
|
import com.google.gwt.user.client.ui.HasHTML; |
|
|
|
import com.google.gwt.user.client.ui.PopupPanel; |
|
|
|
import com.google.gwt.user.client.ui.RootPanel; |
|
|
|
import com.google.gwt.user.client.ui.UIObject; |
|
|
|
import com.google.gwt.user.client.ui.Widget; |
|
|
|
import com.vaadin.terminal.gwt.client.ApplicationConnection; |
|
|
|
import com.vaadin.terminal.gwt.client.ContainerResizedListener; |
|
|
|
import com.vaadin.terminal.gwt.client.Paintable; |
|
|
|
import com.vaadin.terminal.gwt.client.UIDL; |
|
|
|
|
|
|
|
public class VMenuBar extends Widget implements Paintable, |
|
|
|
CloseHandler<PopupPanel> { |
|
|
|
CloseHandler<PopupPanel>, ContainerResizedListener { |
|
|
|
|
|
|
|
/** Set the CSS class name to allow styling. */ |
|
|
|
public static final String CLASSNAME = "v-menubar"; |
|
|
@@ -32,8 +34,8 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
protected final VMenuBar hostReference = this; |
|
|
|
protected String submenuIcon = null; |
|
|
|
protected boolean collapseItems = true; |
|
|
|
protected CustomMenuItem moreItem = null; |
|
|
|
protected VMenuBar collapsedRootItems; |
|
|
|
|
|
|
|
// Construct an empty command to be used when the item has no command |
|
|
|
// associated |
|
|
@@ -83,7 +85,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
/** |
|
|
|
* 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. |
|
|
|
*/ |
|
|
@@ -112,21 +114,22 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
submenuIcon = null; |
|
|
|
} |
|
|
|
|
|
|
|
collapseItems = options.getBooleanAttribute("collapseItems"); |
|
|
|
|
|
|
|
if (collapseItems) { |
|
|
|
if (uidl.hasAttribute("width")) { |
|
|
|
UIDL moreItemUIDL = options.getChildUIDL(0); |
|
|
|
StringBuffer itemHTML = new StringBuffer(); |
|
|
|
|
|
|
|
if (moreItemUIDL.hasAttribute("icon")) { |
|
|
|
itemHTML.append("<img src=\"" |
|
|
|
+ client.translateVaadinUri(moreItemUIDL |
|
|
|
.getStringAttribute("icon")) |
|
|
|
+ "\" align=\"left\" />"); |
|
|
|
.getStringAttribute("icon")) + "\" class=\"" |
|
|
|
+ Icon.CLASSNAME + "\" alt=\"\" />"); |
|
|
|
} |
|
|
|
itemHTML.append(moreItemUIDL.getStringAttribute("text")); |
|
|
|
|
|
|
|
moreItem = new CustomMenuItem(itemHTML.toString(), emptyCommand); |
|
|
|
collapsedRootItems = new VMenuBar(true); |
|
|
|
moreItem.setSubMenu(collapsedRootItems); |
|
|
|
moreItem.addStyleName(CLASSNAME + "-more-menuitem"); |
|
|
|
} |
|
|
|
|
|
|
|
UIDL uidlItems = uidl.getChildUIDL(1); |
|
|
@@ -150,16 +153,24 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
if (item.hasAttribute("icon")) { |
|
|
|
itemHTML.append("<img src=\"" |
|
|
|
+ client.translateVaadinUri(item |
|
|
|
.getStringAttribute("icon")) |
|
|
|
+ "\" align=\"left\" />"); |
|
|
|
.getStringAttribute("icon")) + "\" class=\"" |
|
|
|
+ Icon.CLASSNAME + "\" alt=\"\" />"); |
|
|
|
} |
|
|
|
|
|
|
|
itemHTML.append(itemText); |
|
|
|
|
|
|
|
if (currentMenu != this && item.getChildCount() > 0 |
|
|
|
&& submenuIcon != null) { |
|
|
|
itemHTML.append("<img src=\"" + submenuIcon |
|
|
|
+ "\" align=\"right\" />"); |
|
|
|
// Add submenu indicator |
|
|
|
if (item.getChildCount() > 0) { |
|
|
|
// FIXME For compatibility reasons: remove in version 7 |
|
|
|
String bgStyle = ""; |
|
|
|
if (submenuIcon != null) { |
|
|
|
bgStyle = " style=\"background-image: url(" + submenuIcon |
|
|
|
+ "); text-indent: -999px; width: 1em;\""; |
|
|
|
} |
|
|
|
itemHTML |
|
|
|
.append("<span class=\"" + CLASSNAME |
|
|
|
+ "-submenu-indicator\"" + bgStyle |
|
|
|
+ ">▶</span>"); |
|
|
|
} |
|
|
|
|
|
|
|
Command cmd = null; |
|
|
@@ -190,54 +201,14 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
} |
|
|
|
}// while |
|
|
|
|
|
|
|
// we might need to collapse the top-level menu |
|
|
|
// Only needed if there is more than 1 top level item |
|
|
|
// TODO and if width is defined |
|
|
|
if (collapseItems && getItems().size() > 1) { |
|
|
|
|
|
|
|
int topLevelWidth = 0; |
|
|
|
|
|
|
|
int ourWidth = getOffsetWidth(); |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
for (; i < getItems().size() && topLevelWidth < ourWidth; i++) { |
|
|
|
CustomMenuItem item = getItems().get(i); |
|
|
|
topLevelWidth += item.getOffsetWidth(); |
|
|
|
} |
|
|
|
|
|
|
|
if (topLevelWidth > getOffsetWidth()) { |
|
|
|
ArrayList<CustomMenuItem> toBeCollapsed = new ArrayList<CustomMenuItem>(); |
|
|
|
VMenuBar collapsed = new VMenuBar(true); |
|
|
|
for (int j = i - 2; j < getItems().size(); j++) { |
|
|
|
toBeCollapsed.add(getItems().get(j)); |
|
|
|
} |
|
|
|
|
|
|
|
for (int j = 0; j < toBeCollapsed.size(); j++) { |
|
|
|
CustomMenuItem item = toBeCollapsed.get(j); |
|
|
|
removeItem(item); |
|
|
|
|
|
|
|
// it's ugly, but we have to insert the submenu icon |
|
|
|
if (item.getSubMenu() != null && submenuIcon != null) { |
|
|
|
StringBuffer itemText = new StringBuffer(item.getHTML()); |
|
|
|
itemText.append("<img src=\""); |
|
|
|
itemText.append(submenuIcon); |
|
|
|
itemText.append("\" align=\"right\" />"); |
|
|
|
item.setHTML(itemText.toString()); |
|
|
|
} |
|
|
|
iLayout(); |
|
|
|
|
|
|
|
collapsed.addItem(item); |
|
|
|
} |
|
|
|
|
|
|
|
moreItem.setSubMenu(collapsed); |
|
|
|
addItem(moreItem); |
|
|
|
} |
|
|
|
} |
|
|
|
}// 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 |
|
|
|
*/ |
|
|
@@ -274,7 +245,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns the containing element of the menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public Element getContainingElement() { |
|
|
@@ -283,23 +254,30 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns a new child element to add an item to |
|
|
|
* |
|
|
|
* |
|
|
|
* @param index |
|
|
|
* the index in which point to add a new element in a submenu. -1 |
|
|
|
* will add the new element as the last child (append) |
|
|
|
* |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public Element getNewChildElement() { |
|
|
|
public Element getNewChildElement(int index) { |
|
|
|
if (subMenu) { |
|
|
|
Element tr = DOM.createTR(); |
|
|
|
DOM.appendChild(getContainingElement(), tr); |
|
|
|
if (index == -1) { |
|
|
|
DOM.appendChild(getContainingElement(), tr); |
|
|
|
} else { |
|
|
|
DOM.insertChild(getContainingElement(), tr, index); |
|
|
|
} |
|
|
|
return tr; |
|
|
|
} else { |
|
|
|
return getContainingElement(); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Add a new item to this menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @param html |
|
|
|
* items text |
|
|
|
* @param cmd |
|
|
@@ -314,19 +292,36 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* Add a new item to this menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void addItem(CustomMenuItem item) { |
|
|
|
DOM.appendChild(getNewChildElement(), item.getElement()); |
|
|
|
if (items.contains(item)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
DOM.appendChild(getNewChildElement(-1), item.getElement()); |
|
|
|
item.setParentMenu(this); |
|
|
|
item.setSelected(false); |
|
|
|
items.add(item); |
|
|
|
} |
|
|
|
|
|
|
|
public void addItem(CustomMenuItem item, int index) { |
|
|
|
if (items.contains(item)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (subMenu) { |
|
|
|
DOM.appendChild(getNewChildElement(index), item.getElement()); |
|
|
|
} else { |
|
|
|
DOM.insertChild(getNewChildElement(-1), item.getElement(), index); |
|
|
|
} |
|
|
|
item.setParentMenu(this); |
|
|
|
item.setSelected(false); |
|
|
|
items.add(index, item); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Remove the given item from this menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void removeItem(CustomMenuItem item) { |
|
|
@@ -377,7 +372,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* When an item is clicked |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void itemClick(CustomMenuItem item) { |
|
|
@@ -402,7 +397,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* When the user hovers the mouse over the item |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void itemOver(CustomMenuItem item) { |
|
|
@@ -412,7 +407,6 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
if (menuWasVisible && visibleChildMenu != item.getSubMenu()) { |
|
|
|
popup.hide(); |
|
|
|
visibleChildMenu = null; |
|
|
|
} |
|
|
|
|
|
|
|
if (item.getSubMenu() != null && (parentMenu != null || menuWasVisible) |
|
|
@@ -423,7 +417,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* When the mouse is moved away from an item |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void itemOut(CustomMenuItem item) { |
|
|
@@ -436,34 +430,49 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
/** |
|
|
|
* Shows the child menu of an item. The caller must ensure that the item has |
|
|
|
* a submenu. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void showChildMenu(CustomMenuItem item) { |
|
|
|
popup = new VOverlay(true, false, true); |
|
|
|
popup.setWidget(item.getSubMenu()); |
|
|
|
popup.addCloseHandler(this); |
|
|
|
|
|
|
|
int left = 0; |
|
|
|
int top = 0; |
|
|
|
if (subMenu) { |
|
|
|
popup.setPopupPosition(item.getParentMenu().getAbsoluteLeft() |
|
|
|
+ item.getParentMenu().getOffsetWidth(), item |
|
|
|
.getAbsoluteTop()); |
|
|
|
left = item.getParentMenu().getAbsoluteLeft() |
|
|
|
+ item.getParentMenu().getOffsetWidth(); |
|
|
|
top = item.getAbsoluteTop(); |
|
|
|
} else { |
|
|
|
popup.setPopupPosition(item.getAbsoluteLeft(), item.getParentMenu() |
|
|
|
.getAbsoluteTop() |
|
|
|
+ item.getParentMenu().getOffsetHeight()); |
|
|
|
left = item.getAbsoluteLeft(); |
|
|
|
top = item.getParentMenu().getAbsoluteTop() |
|
|
|
+ item.getParentMenu().getOffsetHeight(); |
|
|
|
} |
|
|
|
popup.setPopupPosition(left, top); |
|
|
|
|
|
|
|
item.getSubMenu().onShow(); |
|
|
|
visibleChildMenu = item.getSubMenu(); |
|
|
|
item.getSubMenu().setParentMenu(this); |
|
|
|
|
|
|
|
popup.show(); |
|
|
|
|
|
|
|
if (left + popup.getOffsetWidth() >= RootPanel.getBodyElement() |
|
|
|
.getOffsetWidth()) { |
|
|
|
if (subMenu) { |
|
|
|
left = item.getParentMenu().getAbsoluteLeft() |
|
|
|
- popup.getOffsetWidth(); |
|
|
|
} else { |
|
|
|
left = RootPanel.getBodyElement().getOffsetWidth() |
|
|
|
- popup.getOffsetWidth(); |
|
|
|
ApplicationConnection.getConsole().log("" + left); |
|
|
|
} |
|
|
|
popup.setPopupPosition(left, top); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Hides the submenu of an item |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void hideChildMenu(CustomMenuItem item) { |
|
|
@@ -478,8 +487,10 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
* When the menu is shown. |
|
|
|
*/ |
|
|
|
public void onShow() { |
|
|
|
if (!items.isEmpty()) { |
|
|
|
(items.get(0)).setSelected(true); |
|
|
|
// remove possible previous selection |
|
|
|
if (selected != null) { |
|
|
|
selected.setSelected(false); |
|
|
|
selected = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -511,7 +522,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
/** |
|
|
|
* Returns the parent menu of this menu, or null if this is the top-level |
|
|
|
* menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public VMenuBar getParentMenu() { |
|
|
@@ -520,7 +531,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the parent menu of this menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @param parent |
|
|
|
*/ |
|
|
|
public void setParentMenu(VMenuBar parent) { |
|
|
@@ -530,7 +541,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
/** |
|
|
|
* Returns the currently selected item of this menu, or null if nothing is |
|
|
|
* selected |
|
|
|
* |
|
|
|
* |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public CustomMenuItem getSelected() { |
|
|
@@ -539,7 +550,7 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the currently selected item of this menu |
|
|
|
* |
|
|
|
* |
|
|
|
* @param item |
|
|
|
*/ |
|
|
|
public void setSelected(CustomMenuItem item) { |
|
|
@@ -563,16 +574,15 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
if (event.isAutoClosed()) { |
|
|
|
hideParents(); |
|
|
|
} |
|
|
|
// setSelected(null); |
|
|
|
visibleChildMenu = null; |
|
|
|
popup = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* |
|
|
|
* A class to hold information on menu items |
|
|
|
* |
|
|
|
* |
|
|
|
*/ |
|
|
|
private class CustomMenuItem extends UIObject implements HasHTML { |
|
|
|
|
|
|
@@ -646,4 +656,73 @@ public class VMenuBar extends Widget implements Paintable, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}// class VMenuBar |
|
|
|
/** |
|
|
|
* @author Jouni Koivuviita / IT Mill Ltd. |
|
|
|
*/ |
|
|
|
|
|
|
|
public void iLayout() { |
|
|
|
// Only collapse if there is more than one item in the root menu and the |
|
|
|
// menu has an explicit size |
|
|
|
if ((getItems().size() > 1 || collapsedRootItems.getItems().size() > 0) |
|
|
|
&& getElement().getStyle().getProperty("width") != null) { |
|
|
|
|
|
|
|
// Measure the width of the "more" item |
|
|
|
final boolean morePresent = getItems().contains(moreItem); |
|
|
|
addItem(moreItem); |
|
|
|
final int moreItemWidth = moreItem.getOffsetWidth(); |
|
|
|
if (!morePresent) { |
|
|
|
removeItem(moreItem); |
|
|
|
} |
|
|
|
|
|
|
|
// Measure available space |
|
|
|
int availableWidth = getElement().getClientWidth(); |
|
|
|
final int rootWidth = getElement().getFirstChildElement() |
|
|
|
.getOffsetWidth(); |
|
|
|
int diff = availableWidth - rootWidth; |
|
|
|
|
|
|
|
removeItem(moreItem); |
|
|
|
|
|
|
|
if (diff < 0) { |
|
|
|
// Too many items: collapse last items from root menu |
|
|
|
final int widthNeeded = moreItemWidth - diff; |
|
|
|
int widthReduced = 0; |
|
|
|
|
|
|
|
while (widthReduced < widthNeeded && getItems().size() > 0) { |
|
|
|
// Move last root menu item to collapsed menu |
|
|
|
CustomMenuItem collapse = getItems().get( |
|
|
|
getItems().size() - 1); |
|
|
|
widthReduced += collapse.getOffsetWidth(); |
|
|
|
removeItem(collapse); |
|
|
|
collapsedRootItems.addItem(collapse, 0); |
|
|
|
} |
|
|
|
} else if (collapsedRootItems.getItems().size() > 0) { |
|
|
|
// Space available for items: expand first items from collapsed |
|
|
|
// menu |
|
|
|
int widthAvailable = diff + moreItemWidth; |
|
|
|
int widthGrowth = 0; |
|
|
|
|
|
|
|
while (widthAvailable > widthGrowth) { |
|
|
|
// Move first item from collapsed menu to the root menu |
|
|
|
CustomMenuItem expand = collapsedRootItems.getItems() |
|
|
|
.get(0); |
|
|
|
collapsedRootItems.removeItem(expand); |
|
|
|
addItem(expand); |
|
|
|
widthGrowth += expand.getOffsetWidth(); |
|
|
|
if (collapsedRootItems.getItems().size() > 0) { |
|
|
|
widthAvailable -= moreItemWidth; |
|
|
|
} |
|
|
|
if (widthGrowth > widthAvailable) { |
|
|
|
removeItem(expand); |
|
|
|
collapsedRootItems.addItem(expand, 0); |
|
|
|
} else { |
|
|
|
widthAvailable = diff; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (collapsedRootItems.getItems().size() > 0) { |
|
|
|
addItem(moreItem); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |