package org.gwtoolbox.widget.client.panel.layout;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import org.gwtoolbox.commons.ui.client.event.MouseHoverHandler;
import org.gwtoolbox.commons.ui.client.event.custom.ToggleEvent;
import org.gwtoolbox.widget.client.button.SimpleMenuButton;
import org.gwtoolbox.commons.ui.client.event.CommonMouseHandler;
import org.gwtoolbox.commons.ui.client.event.CompoundHandlerRegistration;
import org.gwtoolbox.commons.ui.client.event.custom.HasToggleHandlers;
import org.gwtoolbox.commons.ui.client.event.custom.ToggleHandler;
import org.gwtoolbox.widget.client.menu.MenuBuilder;
import org.gwtoolbox.widget.client.support.setter.ValueSetter;
/**
* A simple layout that can hold only one widget which is displyed in the main view. The contains widget is decorated
* with a header. The header holds the following UI contructs:
* <ul>
* <li>Text - Holds a title to the widget</li>
* <li>Icon - Holds a small icon next to the text</li>
* <li>Tools - A set of tool buttons that can execute commands</li>
* <li>ToggleTools - A set of toggle tool buttons that can execute commands</li>
* <li>MenuTools - A set of menu tool buttons that can pop up a menu</li>
* </ul>
*
* @author Uri Boness
*/
public class PanelLayout extends ResizeComposite implements HasCloseHandlers<PanelLayout> {
private final static Tool DEFAULT_CLOSE_TOOL = new Tool() {
public String getStyleName() {
return "tool-close";
}
};
private LayoutPanel main;
private TitleBar titleBar;
private SimplePanel content;
private boolean collapsed;
private ValueSetter<Boolean> contentVisibility;
private boolean animationEnabled;
/**
* Constructs a new PanelLayout with a given title.
*
* @param title The title that will be displayed in the header of the layout.
*/
public PanelLayout(String title) {
this(title, null);
}
/**
* Consructs a new PanelLayout with a given title and and icon.
*
* @param title The title that will be displayed in the header of the layout.
* @param icon The icon that will be displayed in the header of the layout.
*/
public PanelLayout(String title, Image icon) {
main = new LayoutPanel();
titleBar = new TitleBar(title, icon);
main.add(titleBar);
main.setWidgetTopHeight(titleBar, 0, Style.Unit.PX, 28, Style.Unit.PX);
content = new SimplePanel();
content.setStylePrimaryName("Content");
content.setSize("100%", "100%");
main.add(content);
main.setWidgetTopBottom(content, 28, Style.Unit.PX, 0, Style.Unit.PX);
contentVisibility = new ValueSetter<Boolean>() {
public void set(Boolean visible) {
if (visible) {
main.setWidgetTopBottom(content, 28, Style.Unit.PX, 0, Style.Unit.PX);
} else {
main.setWidgetTopHeight(content, 28, Style.Unit.PX, 0, Style.Unit.PX);
}
if (animationEnabled) {
main.animate(200);
} else {
main.forceLayout();
}
}
};
initWidget(main);
setStylePrimaryName("PanelLayout");
}
/**
* Sets the title in the header of the layout.
*
* @param title The title to be displayed in the header of the layout.
*/
public void setTitle(String title) {
titleBar.setTitle(title);
}
/**
* Sets the icon in the header of the layout.
*
* @param icon The icon to be displayed in the header of the layout.
*/
public void setIcon(Image icon) {
titleBar.setImage(icon);
}
/**
* Indicates whether animation is enabled for this layout.
*
* @return Whether animation is enabled for this layout.
* @see #setAnimationEnabled(boolean)
*/
public boolean isAnimationEnabled() {
return animationEnabled;
}
/**
* Sets whether animation is enabled for this layout. When set to {@link true}, when the panels collapses, it
* will animation as a roll-up effect, and when it expands it will do so with a roll-down effect.
*
* @param animationEnabled Indicates whether animation is enabled for this layout.
*/
public void setAnimationEnabled(boolean animationEnabled) {
this.animationEnabled = animationEnabled;
}
/**
* Sets the main content of this layout.
*
* @param widget The content of this layout.
*/
public void setContent(Widget widget) {
Widget currentContent = this.content.getWidget();
if (currentContent != null && (currentContent instanceof PanelLayoutAware)) {
((PanelLayoutAware) currentContent).beforePanelLayoutDetach(this);
}
this.content.setWidget(widget);
if (widget instanceof PanelLayoutAware) {
((PanelLayoutAware) widget).afterPanelLayoutAttached(this);
}
}
/**
* Indicates whether the content of this layout can be collapsed.
*
* @return Whether the content of this layout can be collapsed.
* @see #setCollapsable(boolean)
*/
public boolean isCollapsable() {
return titleBar.isCollapsable();
}
/**
* Sets whether this layout is collapsable. If so, a collapse tool button will appear in the header, on which the user
* can click and collapse the content.
*
* @param collapsable Indicates whether this layout is collapsable.
*/
public void setCollapsable(boolean collapsable) {
titleBar.setCollapsable(collapsable);
}
/**
* If the content of this layout can be collapse (see {@link #isCollapsable()}) then calling this method will
* collapse the content.
*/
public void collapse() {
contentVisibility.set(false);
collapsed = true;
addStyleDependentName("collapsed");
}
/**
* If the content of this layout is collapsed, calling this method will expand the content.
*/
public void expand() {
contentVisibility.set(true);
collapsed = false;
removeStyleDependentName("collapsed");
}
/**
* Indicates whether the content of this panel is currently collapsed.
*
* @return Whether the content of this panel is currently collapsed.
*/
public boolean isCollapsed() {
return collapsed;
}
/**
* Adds a close handler for this layout. This handler is notified whenever the user closes this panel using the
* close tool.
*
* @param handler The close handler.
* @return The handler registration for the handler.
* @see #setClosable(boolean)
* @see #setCloseTool(org.gwtoolbox.widget.client.panel.layout.PanelLayout.Tool)
*/
public HandlerRegistration addCloseHandler(CloseHandler<PanelLayout> handler) {
return addHandler(handler, CloseEvent.getType());
}
/**
* Sets the close tool that will be used for closing the panel. By default, the close tool is in the form of an
* [x] icon.
*
* @param closeTool The tool that will be used to close the panel.
* @see org.gwtoolbox.widget.client.panel.layout.PanelLayout.Tool
* @see org.gwtoolbox.widget.client.panel.layout.PanelLayout.Tools
*/
public void setCloseTool(Tool closeTool) {
titleBar.setCloseTool(closeTool);
}
/**
* Sets whether this layout/panel can be closed. When set to {@code true}, a close tool button will be added to the
* header of the layout on which the user can click and trigger a close event.
*
* @param closable Indicates whether this layout/panel can be closed by the user.
* @see #setCloseTool(org.gwtoolbox.widget.client.panel.layout.PanelLayout.Tool)
* @see #addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler)
*/
public void setClosable(boolean closable) {
titleBar.setClosable(closable);
}
/**
* Adds a tool button to the header of this layout.
*
* @param tool The tool type
* @param handler notified when the tool is clicked.
* @return The Widget which represents the button.
*/
public ToolButton addTool(Tool tool, ClickHandler handler) {
return titleBar.addButton(tool, handler);
}
/**
* Adds a toogle tool button to the header of this layout.
*
* @param tool The toggle tool button.
* @param handler The toggle handler for the abutton.
* @return The created ToggleToolButton.
*/
public ToolToggleButton addToggleTool(ToggleTool tool, ToggleHandler handler) {
return titleBar.addToggleButton(tool, handler);
}
/**
* Adds a menu tool to the header of this layout.
*
* @param tool The tool which will trigger the menu.
* @param menuBuilder The menu builder which builds the menu.
* @return The SimpleMenuButton that was created.
*/
public SimpleMenuButton addMenuTool(Tool tool, MenuBuilder menuBuilder) {
return titleBar.addMenuButton(tool, menuBuilder);
}
/**
* Removes the given tool from the header.
*
* @param button The tool button tha was created when it was added to the header.
*/
public void removeTool(Widget button) {
titleBar.removeButton(button);
}
/**
* Removes all the tool buttons from the header.
*/
public void clearTools() {
titleBar.clearButtons();
}
//================================================= Inner Classes ==================================================
/**
* Content widgets that implementing this interface by any widget will recieve notification about their
* attachement/detachements to/from this layout. This will enabled them to add/remove their own tools to/from
* the header.
*/
public static interface PanelLayoutAware {
/**
* Called when the content is attached to the layout. Usually content widget will use this mehtod to add their
* own tools to the header of the passed in layout.
*
* @param layout The PanelLayout that holds the content.
*/
void afterPanelLayoutAttached(PanelLayout layout);
/**
* Called when the content is detached from the layout. Usually content widget will use this method to remove
* their own tools from the header of layout.
*
* @param layout The PanelLayout that holds the content.
*/
void beforePanelLayoutDetach(PanelLayout layout);
}
/**
* A tool represents a tool button type. The only thing that uniquely idefntifies a tool is its style name.
*/
public static interface Tool {
String getStyleName();
}
/**
* A toggle tool holds the style names for both states of the toggle button - up and down.
*/
public static interface ToggleTool {
String getUpStyleName();
String getDownStyleName();
}
/**
* A simple implementation for creating custom tools with custom style names.
*/
public static class CustomTool implements Tool {
private final String styleName;
public CustomTool(String styleName) {
this.styleName = styleName;
}
public String getStyleName() {
return styleName;
}
}
/**
* A simple implementation for creating custom toggle tools with custom style names.
*/
public static class CustomToggleTool implements ToggleTool {
private String upStyleName;
private String downStyleName;
public CustomToggleTool(String upStyleName, String downStyleName) {
this.upStyleName = upStyleName;
this.downStyleName = downStyleName;
}
public String getUpStyleName() {
return upStyleName;
}
public String getDownStyleName() {
return downStyleName;
}
}
/**
* A set of pre-defined tools.
*/
public static enum Tools implements Tool {
ADD("tool-add"),
REMOVE("tool-remove"),
MAXIMIZE("tool-maximize"),
UNMAXIMIZE("tool-unmaximize"),
MINIMIZE("tool-minimize"),
SLIDE_UP("tool-slide-up"),
SLIDE_DOWN("tool-slide-down"),
SLIDE_LEFT("tool-slide-left"),
SLIDE_RIGHT("tool-slide-right"),
REFRESH("tool-refresh"),
GEAR("tool-gear");
private final String styleName;
private Tools(String styleName) {
this.styleName = styleName;
}
public String getStyleName() {
return styleName;
}
}
/**
* A set of pre-defined toggle tools.
*/
public static enum ToggleTools implements ToggleTool {
PIN("tool-pin-down", "tool-pin-up");
private final String upStyleName;
private final String downStyleName;
ToggleTools(String upStyleName, String downStyleName) {
this.upStyleName = upStyleName;
this.downStyleName = downStyleName;
}
public String getUpStyleName() {
return upStyleName;
}
public String getDownStyleName() {
return downStyleName;
}
}
protected class TitleBar extends Composite {
private Label titleLabel;
private SimplePanel imagePane;
private SimplePanel closeButtonPane;
private SimplePanel collapseButtonPane;
private FlowPanel tools;
private Tool closeTool = DEFAULT_CLOSE_TOOL;
public TitleBar(String title, Image image) {
FlowPanel main = new FlowPanel();
imagePane = new SimplePanel();
if (image != null) {
imagePane.setWidget(image);
}
imagePane.setStylePrimaryName("Image");
imagePane.addStyleName("TitleBar-inline");
main.add(imagePane);
titleLabel = new Label(title);
titleLabel.setStylePrimaryName("Label");
titleLabel.addStyleName("TitleBar-inline");
main.add(titleLabel);
closeButtonPane = new SimplePanel();
closeButtonPane.addStyleName("TitleBar-tool");
main.add(closeButtonPane);
collapseButtonPane = new SimplePanel();
collapseButtonPane.addStyleName("TitleBar-tool");
main.add(collapseButtonPane);
tools = new FlowPanel();
tools.setStylePrimaryName("TitleBar-tools");
main.add(tools);
initWidget(main);
setStylePrimaryName("TitleBar");
}
public HandlerRegistration addCommonMouseHandler(CommonMouseHandler handler) {
CompoundHandlerRegistration registration = new CompoundHandlerRegistration();
registration.addRegistration(titleLabel.addMouseMoveHandler(handler));
registration.addRegistration(titleLabel.addMouseOutHandler(handler));
registration.addRegistration(titleLabel.addMouseOverHandler(handler));
registration.addRegistration(titleLabel.addMouseDownHandler(handler));
registration.addRegistration(titleLabel.addMouseUpHandler(handler));
return registration;
}
public void setTitle(String title) {
titleLabel.setText(title);
}
public void setImage(Image image) {
imagePane.setWidget(image);
}
public void setCloseTool(Tool tool) {
this.closeTool = tool;
}
public void setClosable(boolean closable) {
if (closable) {
ToolButton button = new ToolButton(closeTool, new ClickHandler() {
public void onClick(ClickEvent event) {
CloseEvent.fire(PanelLayout.this, PanelLayout.this);
}
});
DOM.setStyleAttribute(button.getElement(), "marginLeft", "3px");
closeButtonPane.setWidget(button);
} else {
closeButtonPane.setWidget(null);
}
}
public boolean isCollapsable() {
return collapseButtonPane.isVisible();
}
public void setCollapsable(boolean collapsable) {
if (!collapsable) {
collapseButtonPane.clear();
collapseButtonPane.setVisible(false);
return;
}
collapseButtonPane.setVisible(true);
final ToolToggleButton button = new ToolToggleButton(new CustomToggleTool("tool-arrow-down", "tool-arrow-up"));
DOM.setStyleAttribute(button.getElement(), "marginLeft", "3px");
button.addToggleHandler(new ToggleHandler() {
public void onToggle(ToggleEvent event) {
if (button.isDown()) {
collapse();
} else {
expand();
}
}
});
collapseButtonPane.setWidget(button);
}
public ToolToggleButton addToggleButton(ToggleTool tool, ToggleHandler handler) {
ToolToggleButton button = new ToolToggleButton(tool, handler);
button.addStyleName("TitleBar-tool");
DOM.setStyleAttribute(button.getElement(), "marginLeft", "3px");
tools.add(button);
return button;
}
public ToolButton addButton(Tool tool, ClickHandler handler) {
ToolButton button = new ToolButton(tool, handler);
button.addStyleName("TitleBar-tool");
DOM.setStyleAttribute(button.getElement(), "marginLeft", "3px");
tools.add(button);
return button;
}
public SimpleMenuButton addMenuButton(Tool tool, MenuBuilder menuBuilder) {
SimpleMenuButton button = new SimpleMenuButton(new ToolButton(tool), true);
button.addStyleName("TitleBar-tool");
DOM.setStyleAttribute(button.getElement(), "marginLeft", "3px");
menuBuilder.build(button.getMenu());
tools.add(button);
return button;
}
public void clearButtons() {
tools.clear();
}
public void removeButton(Widget button) {
tools.remove(button);
}
}
/**
* A simple tool button where the user can click and issue a click event. A tool button will always be 15x15 pixels
* size. Any atttempt to change that will throw an exception.
*/
public static class ToolButton extends ButtonBase {
private boolean enabled = true;
public ToolButton(Tool tool) {
this(tool, null);
}
private ToolButton(Tool tool, ClickHandler clickHandler) {
super(FocusImpl.getFocusImplForPanel().createFocusable());
setStylePrimaryName(tool.getStyleName());
MouseHoverHandler hoverHandler = new MouseHoverHandler() {
@Override
public void onMouseOver(MouseOverEvent event) {
addStyleDependentName("hover");
}
@Override
public void onMouseOut(MouseOutEvent event) {
removeStyleDependentName("hover");
}
};
DOM.setStyleAttribute(getElement(), "width", "15px");
DOM.setStyleAttribute(getElement(), "height", "15px");
addDomHandler(hoverHandler, MouseOverEvent.getType());
addDomHandler(hoverHandler, MouseOutEvent.getType());
if (clickHandler != null) {
addClickHandler(clickHandler);
}
}
@Override
public void onBrowserEvent(Event event) {
if (enabled) {
super.onBrowserEvent(event);
}
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
if (enabled) {
removeStyleName("disabled-tool");
} else {
addStyleName("disabled-tool");
}
this.enabled = enabled;
}
@Override
public void setFocus(boolean focused) {
if (isEnabled()) {
super.setFocus(focused);
}
}
@Override
public void setSize(String width, String height) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
@Override
public void setWidth(String width) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
@Override
public void setHeight(String height) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
}
/**
* A toggle tool button where the user can click and trigger toggle events. Like the simple tool button, the toggle
* tool button can only be 15x15 pixels size (any attempt to change that will throw an exception).
*/
public static class ToolToggleButton extends ButtonBase implements HasToggleHandlers {
private boolean enabled = true;
private boolean down;
private ToggleTool tool;
private ToolToggleButton(ToggleTool tool) {
this(tool, null);
}
private ToolToggleButton(ToggleTool tool, ToggleHandler toggleHandler) {
super(FocusImpl.getFocusImplForPanel().createFocusable());
this.tool = tool;
setStylePrimaryName(tool.getUpStyleName());
DOM.setStyleAttribute(getElement(), "width", "15px");
DOM.setStyleAttribute(getElement(), "height", "15px");
MouseHoverHandler hoverHandler = new MouseHoverHandler() {
@Override
public void onMouseOver(MouseOverEvent event) {
addStyleDependentName("hover");
}
@Override
public void onMouseOut(MouseOutEvent event) {
removeStyleDependentName("hover");
}
};
addDomHandler(hoverHandler, MouseOverEvent.getType());
addDomHandler(hoverHandler, MouseOutEvent.getType());
addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
toggle();
}
});
if (toggleHandler != null) {
addHandler(toggleHandler, ToggleEvent.getType());
}
}
@Override
public void onBrowserEvent(Event event) {
if (enabled) {
super.onBrowserEvent(event);
}
}
public HandlerRegistration addToggleHandler(ToggleHandler handler) {
return addHandler(handler, ToggleEvent.getType());
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
if (enabled) {
removeStyleName("disabled-tool");
} else {
addStyleName("disabled-tool");
}
this.enabled = enabled;
}
@Override
public void setFocus(boolean focused) {
if (isEnabled()) {
super.setFocus(focused);
}
}
public boolean isDown() {
return down;
}
@Override
public void setSize(String width, String height) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
@Override
public void setWidth(String width) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
@Override
public void setHeight(String height) {
throw new UnsupportedOperationException("The size of the ToolButton is fixed to 15x15 pixels");
}
public void toggle() {
down = !down;
if (down) {
setStylePrimaryName(tool.getDownStyleName());
} else {
setStylePrimaryName(tool.getUpStyleName());
}
ToggleEvent.fire(this, down);
}
}
}