package com.google.appinventor.client.widgets;
import com.google.appinventor.client.utils.PZAwarePositionCallback;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.Image;
import java.util.ArrayList;
import java.util.List;
/**
* Class representing a drop-down button with its associated menu. Note
* that all items in the menu should have unique captions for removeItem
* and setItemEnabled to work properly.
*/
public class DropDownButton extends TextButton {
private final ContextMenu menu;
private final List<MenuItem> items;
private final boolean rightAlign;
public static class DropDownItem {
private final String widgetName;
private final String caption;
private final Command command;
public DropDownItem(String widgetName, String caption, Command command) {
this.widgetName = widgetName;
this.caption = caption;
this.command = command;
}
}
/**
* A subclass of PZAwarePositionCallback designed to position the ContextMenu
* of a DropDownButton.
*/
private class DropDownPositionCallback extends PZAwarePositionCallback {
public DropDownPositionCallback(Element elem) {
super(elem);
}
/**
* @param offsetWidth width of the ContextMenu being positioned on the parent element
* @param offsetHeight height of the ContextMenu being positioned on the parent element
* Sets the position of the ContextMenu on the screen given it's dimensions
*/
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
// getAbsoluteLeft/Right() gives the top coordinate of the parent element
// getOffsetWidth/Height() gives the width/height of the parent element
int left = Window.Navigator.getUserAgent().contains("Chrome") && isPinchZoomed()
? getTrueAbsoluteLeft() : getAbsoluteLeft();
if (rightAlign) {
left += getOffsetWidth() - offsetWidth;
}
int top = Window.Navigator.getUserAgent().contains("Chrome") && isPinchZoomed()
? getTrueAbsoluteTop() + getOffsetHeight()
: getAbsoluteTop() + getOffsetHeight();
// Values to determine how to display the ContextMenu - above or below
int dropDownBottom = top + offsetHeight;
int screenBottom = Window.getScrollTop()+Window.getClientHeight();
// if the bottom will go off the current browser screen, display
// the dropdown as a 'dropup' where the ContextMenu appears
// above instead
if(dropDownBottom > screenBottom) {
int newTop = Window.Navigator.getUserAgent().contains("Chrome") && isPinchZoomed()
? getTrueAbsoluteTop() -offsetHeight
: getAbsoluteTop() - offsetHeight;
// account for the extreeeemely unlikely case where newTop
// also goes off the screen in this case, it makes more
// sense to just go off the bottom of the screen (the screen
// won't grow up, and so the menu would get completely cut
// off at the top
if(newTop >= 0) {
top = newTop;
}
}
menu.setPopupPosition(left, top);
}
}
// Create a new drop-down menu button (with text), initially populated with items. Null
// items in the list cause a separator to be added at that position.
public DropDownButton(String widgetName, String caption, List<DropDownItem> toolbarItems,
boolean rightAlign) {
super(caption + " \u25BE "); // drop down triangle
this.menu = new ContextMenu();
this.items = new ArrayList<MenuItem>();
this.rightAlign = rightAlign;
for (DropDownItem item : toolbarItems) {
if (item != null) {
this.items.add(menu.addItem(item.caption, true, item.command));
} else {
menu.addSeparator();
}
}
addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
menu.setPopupPositionAndShow(new DropDownPositionCallback(getElement()));
}
});
}
public DropDownButton(String widgetName, String caption, List<DropDownItem> toolbarItems,
boolean rightAlign, boolean hasTriangle) {
this(widgetName, caption, toolbarItems, rightAlign);
if (!hasTriangle) {
setText(caption);
}
}
public DropDownButton(String widgetName, String caption, List<DropDownItem> toolbarItems,
boolean rightAlign, boolean hasTriangle, boolean hasHtmlCaption) {
this(widgetName, caption, toolbarItems, rightAlign);
if (hasHtmlCaption) {
// Set the button's caption as an HTML String with or without a dropdown triangle
if (hasTriangle)
setCaption(caption);
else
setHTML(caption);
}
}
// Create a new drop-down menu button (with image), initially populated with items. Null
// items in the list cause a separator to be added at that position.
public DropDownButton(String widgetName, Image icon, List<DropDownItem> toolbarItems,
boolean rightAlign) {
super(icon); // icon for button
this.menu = new ContextMenu();
this.items = new ArrayList<MenuItem>();
this.rightAlign = rightAlign;
for (DropDownItem item : toolbarItems) {
if (item != null) {
addItem(item);
} else {
menu.addSeparator();
}
}
addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
menu.setPopupPositionAndShow(new DropDownPositionCallback(getElement()));
}
});
}
public void clearAllItems() {
for (MenuItem item : items) {
menu.removeItem(item);
}
items.clear();
}
public void addItem(DropDownItem item) {
items.add(menu.addItem(item.caption, true, item.command));
}
public void removeItem(String itemName) {
for (MenuItem item : items) {
if (item.getText().equals(itemName)) {
menu.removeItem(item);
items.remove(item);
break;
}
}
}
public void setItemEnabled(String itemName, boolean enabled) {
for (MenuItem item : items) {
if (item.getText().equals(itemName)) {
item.setEnabled(enabled);
break;
}
}
}
public void setCaption(String caption) {
this.setText(caption + " \u25BE ");
}
public ContextMenu getContextMenu() {
return menu;
}
}