/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.widget.button.icon;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.Shared;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import org.waveprotocol.wave.client.common.webdriver.DebugClassHelper;
import org.waveprotocol.wave.client.widget.button.ButtonDisplay;
import org.waveprotocol.wave.client.widget.button.MouseListener;
import org.waveprotocol.wave.client.widget.button.StyleAxis;
/**
* Template for a button that is implemented as a single image with no text.
*
*/
public class IconButtonTemplate extends Composite implements ButtonDisplay, ClickHandler,
MouseOverHandler, MouseOutHandler, MouseUpHandler, MouseDownHandler {
/**
* Separate out the down/hover styling, to allow buttons from outside
* this template to be used for icon buttons.
*/
@Shared
public interface ButtonStateCss extends CssResource {
/* Normal state assumed to be an empty modifier */
String down();
String hover();
String disabled();
}
/**
* This is part 1 of the Resource bundle. The bundle is split
* into 2 parts to avoid crashing chrome.
*/
public interface Resources extends ClientBundle {
// Panel icons.
@Source("panel-minimize.png")
@ImageOptions(flipRtl = true)
ImageResource panelMinimize();
@Source("panel-minimize-down.png")
@ImageOptions(flipRtl = true)
ImageResource panelMinimizeDown();
@Source("panel-minimize-hover.png")
@ImageOptions(flipRtl = true)
ImageResource panelMinimizeHover();
@Source("panel-maximize.png")
@ImageOptions(flipRtl = true)
ImageResource panelMaximize();
@Source("panel-maximize-down.png")
@ImageOptions(flipRtl = true)
ImageResource panelMaximizeDown();
@Source("panel-maximize-hover.png")
@ImageOptions(flipRtl = true)
ImageResource panelMaximizeHover();
@Source("panel-restore.png")
@ImageOptions(flipRtl = true)
ImageResource panelRestore();
@Source("panel-restore-down.png")
@ImageOptions(flipRtl = true)
ImageResource panelRestoreDown();
@Source("panel-restore-hover.png")
@ImageOptions(flipRtl = true)
ImageResource panelRestoreHover();
@Source("panel-close.png")
@ImageOptions(flipRtl = true)
ImageResource panelClose();
@Source("panel-close-down.png")
@ImageOptions(flipRtl = true)
ImageResource panelCloseDown();
@Source("panel-close-hover.png")
@ImageOptions(flipRtl = true)
ImageResource panelCloseHover();
@Source("IconButtonTemplate.css")
Css css();
interface Css extends ButtonStateCss {
String panelMinimize();
String panelMaximize();
String panelRestore();
String panelClose();
}
}
/**
* This is part 2 of the Resource bundle. The bundle is split
* into 2 parts to avoid crashing chrome.
*/
public interface Resources1 extends ClientBundle {
@Source("blue-plus.png")
@ImageOptions(flipRtl = true)
ImageResource bluePlus();
@Source("folder-closed.png")
@ImageOptions(flipRtl = true)
ImageResource folderClosed();
@Source("folder-open.png")
@ImageOptions(flipRtl = true)
ImageResource folderOpen();
@Source("alert_close_button.png")
@ImageOptions(flipRtl = true)
ImageResource alertClose();
@Source("alert_close_button_down.png")
@ImageOptions(flipRtl = true)
ImageResource alertCloseDown();
@Source("alert_close_button_hover.png")
@ImageOptions(flipRtl = true)
ImageResource alertCloseHover();
@Source("popup-button.png")
@ImageOptions(flipRtl = true)
ImageResource popupButtonImage();
@Source("view_switcher_feed.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherFeed();
@Source("view_switcher_feed_down.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherFeedDown();
@Source("view_switcher_single.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherSingle();
@Source("view_switcher_single_down.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherSingleDown();
@Source("view_switcher_avatar.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherAvatar();
@Source("view_switcher_avatar_down.png")
@ImageOptions(flipRtl = true)
ImageResource viewSwitcherAvatarDown();
@Source("spelly-dropdown.png")
@ImageOptions(flipRtl = true)
ImageResource spellyDropdown();
@Source("button_digest_next.png")
@ImageOptions(flipRtl = true)
ImageResource nextDigestPage();
@Source("button_digest_next_down.png")
@ImageOptions(flipRtl = true)
ImageResource nextDigestPageDown();
@Source("button_digest_prev.png")
@ImageOptions(flipRtl = true)
ImageResource previousDigestPage();
@Source("button_digest_prev_down.png")
@ImageOptions(flipRtl = true)
ImageResource previousDigestPageDown();
@Source("lightbulb.png")
@ImageOptions(flipRtl = true)
ImageResource lightbulb();
@Source("add.png")
@ImageOptions(flipRtl = true)
ImageResource add();
@Source("addDown.png")
@ImageOptions(flipRtl = true)
ImageResource addDown();
@Source("add-big.png")
@ImageOptions(flipRtl = true)
ImageResource addBig();
@Source("add-big-down.png")
@ImageOptions(flipRtl = true)
ImageResource addBigDown();
@Source("add_small.png")
@ImageOptions(flipRtl = true)
ImageResource addSmall();
@Source("add_small_down.png")
@ImageOptions(flipRtl = true)
ImageResource addSmallDown();
@Source("remove_tag_button.png")
@ImageOptions(flipRtl = true)
ImageResource removeTag();
@Source("remove_tag_button_hover.png")
@ImageOptions(flipRtl = true)
ImageResource removeTagHover();
@Source("split_button_dropdown.png")
@ImageOptions(flipRtl = true)
ImageResource splitButtonDropdown();
@Source("split_button_dropdown_down.png")
@ImageOptions(flipRtl = true)
ImageResource splitButtonDropdownDown();
@Source("IconButtonTemplate1.css")
Css1 css();
interface Css1 extends ButtonStateCss {
String bluePlus();
String folderExpand();
String alertClose();
String viewSwitcherAvatar();
String viewSwitcherSingle();
String nextDigestPage();
String previousDigestPage();
String spellyDropdown();
String lightbulb();
String removeTag();
String popupButton();
String viewSwitcherFeed();
String splitButtonDropdown();
String add();
String addBig();
String addSmall();
String cursorDefault();
String cursorPointer();
}
}
/**
* The styles that this kind of button can have.
*
*/
public static enum IconButtonStyle {
BLUE_PLUS, PANEL_MINIMIZE, PANEL_RESTORE, PANEL_MAXIMIZE, PANEL_CLOSE,
PLUS_MINUS, POPUP_MENU, ALERT_CLOSE, VIEW_SWITCHER_AVATAR,
VIEW_SWITCHER_FEED, VIEW_SWITCHER_SINGLE, NEXT_DIGEST_PAGE, PREVIOUS_DIGEST_PAGE,
SPELLY_DROPDOWN, LIGHTBULB, ADD, ADD_BIG, ADD_SMALL, REMOVE_TAG,
SPLIT_BUTTON_DROPDOWN
}
/** The singleton instance of our resources. */
private static final Resources RESOURCES = GWT.create(Resources.class);
/** The singleton instance of our resources. */
private static final Resources1 RESOURCES1 = GWT.create(Resources1.class);
/**
* The controller of this UI widget.
*/
private MouseListener mouseListener;
/**
* The CSS class that describes the current state of the button.
*/
private StyleAxis buttonStateClassName;
/**
* The CSS class that describes the style of the icon.
*/
private StyleAxis iconStyleClassName;
/**
* The last debug class name set relating to the state.
*/
private String stateDebugClassName = "";
/**
* The CSS class that describes the cursor for this icon.
*/
private StyleAxis cursorStyleClassName;
@UiField HTMLPanel icon;
static {
StyleInjector.inject(RESOURCES.css().getText(), true);
StyleInjector.inject(RESOURCES1.css().getText(), true);
}
/**
* @param style Dictates what the button should look like.
* @param tooltip The tooltip for this button.
*/
public IconButtonTemplate(IconButtonStyle style, String tooltip) {
this();
init(style, tooltip);
}
@UiConstructor
public IconButtonTemplate() {
Label label = new Label();
initWidget(label);
label.addClickHandler(this);
label.addMouseOverHandler(this);
label.addMouseOutHandler(this);
label.addMouseUpHandler(this);
label.addMouseDownHandler(this);
}
/**
* Initialize the state of this widget.
*
* @param style Dictates what the button should look like.
* @param tooltip The tooltip for this button.
*/
public void init(IconButtonStyle style, String tooltip) {
buttonStateClassName = new StyleAxis(getElement());
iconStyleClassName = new StyleAxis(getElement());
cursorStyleClassName = new StyleAxis(getElement());
cursorStyleClassName.setStyle(RESOURCES1.css().cursorPointer());
setStyle(style);
setTitle(tooltip);
}
/**
* TODO(user) to make the style final once UiBinder is in.
*
* @param style The {@link IconButtonStyle} this button should adopt.
*/
public void setStyle(IconButtonStyle style) {
String styleName = null;
switch (style) {
case PANEL_CLOSE:
styleName = RESOURCES.css().panelClose();
DebugClassHelper.addDebugClass(this, "close");
break;
case PANEL_MAXIMIZE:
styleName = RESOURCES.css().panelMaximize();
DebugClassHelper.addDebugClass(this, "maximise");
break;
case PANEL_MINIMIZE:
styleName = RESOURCES.css().panelMinimize();
DebugClassHelper.addDebugClass(this, "minimise");
break;
case PANEL_RESTORE:
styleName = RESOURCES.css().panelRestore();
DebugClassHelper.addDebugClass(this, "restore");
break;
case PLUS_MINUS:
styleName = RESOURCES1.css().folderExpand();
break;
case POPUP_MENU:
styleName = RESOURCES1.css().popupButton();
break;
case ALERT_CLOSE:
styleName = RESOURCES1.css().alertClose();
break;
case BLUE_PLUS:
styleName = RESOURCES1.css().bluePlus();
break;
case VIEW_SWITCHER_AVATAR:
styleName = RESOURCES1.css().viewSwitcherAvatar();
break;
case VIEW_SWITCHER_FEED:
styleName = RESOURCES1.css().viewSwitcherFeed();
break;
case VIEW_SWITCHER_SINGLE:
styleName = RESOURCES1.css().viewSwitcherSingle();
break;
case SPELLY_DROPDOWN:
styleName = RESOURCES1.css().spellyDropdown();
break;
case NEXT_DIGEST_PAGE:
styleName = RESOURCES1.css().nextDigestPage();
break;
case PREVIOUS_DIGEST_PAGE:
styleName = RESOURCES1.css().previousDigestPage();
break;
case LIGHTBULB:
styleName = RESOURCES1.css().lightbulb();
break;
case REMOVE_TAG:
styleName = RESOURCES1.css().removeTag();
break;
case SPLIT_BUTTON_DROPDOWN:
styleName = RESOURCES1.css().splitButtonDropdown();
break;
case ADD:
styleName = RESOURCES1.css().add();
break;
case ADD_BIG:
styleName = RESOURCES1.css().addBig();
break;
case ADD_SMALL:
styleName = RESOURCES1.css().addSmall();
break;
}
iconStyleClassName.setStyle(styleName);
}
/** {@inheritDoc} */
public void setState(ButtonState state) {
String styleName = null;
String newStateDebugClassName = "";
switch (state) {
case DISABLED:
newStateDebugClassName = "disabled";
styleName = internalStateCss.disabled();
cursorStyleClassName.setStyle(RESOURCES1.css().cursorDefault());
break;
case DOWN:
newStateDebugClassName = "down";
styleName = internalStateCss.down();
break;
case HOVER:
newStateDebugClassName = "hover";
styleName = internalStateCss.hover();
break;
case NORMAL:
newStateDebugClassName = "normal";
styleName = null;
break;
}
DebugClassHelper.replaceDebugClass(getElement(),
"ibts_" + stateDebugClassName, "ibts_" + newStateDebugClassName);
stateDebugClassName = newStateDebugClassName;
buttonStateClassName.setStyle(styleName);
}
/** {@inheritDoc} */
public void setUiListener(MouseListener mouseListener) {
this.mouseListener = mouseListener;
}
@Override
public void setTooltip(String tooltip) {
setTitle(tooltip);
}
@Override
public void setText(String text) {
((Label) getWidget()).setText(text);
}
@Override
protected void onDetach() {
super.onDetach();
if (mouseListener != null) {
mouseListener.onMouseLeave();
}
}
/**
* Opens up the ability to use a style (= icon) not within the control of this template
* Requires using style sheet with .down and .hover styles
*/
private ButtonStateCss internalStateCss = RESOURCES.css(); // by default, do the same as always
/**
* Creates a new {@link IconButtonTemplate} with the given style and logic,
* taking the style name directly
*
* @param styleName Dictates what the button should look like.
* @param tooltip The tooltip for this button.
* @return A new {@link IconButtonTemplate} that behaves and looks as
* described.
*/
public static IconButtonTemplate createDirect(String styleName, String tooltip,
ButtonStateCss stateCss) {
IconButtonTemplate template = new IconButtonTemplate();
template.initDirect(styleName, tooltip, stateCss);
return template;
}
/**
* Initialize the state of this widget, taking the style name directly
*
* @param styleName Dictates what the button should look like.
* @param tooltip The tooltip for this button.
*/
private void initDirect(String styleName, String tooltip, ButtonStateCss stateCss) {
internalStateCss = stateCss;
buttonStateClassName = new StyleAxis(getElement());
iconStyleClassName = new StyleAxis(getElement());
iconStyleClassName.setStyle(styleName);
setTitle(tooltip);
}
@Override
public void onClick(ClickEvent event) {
mouseListener.onClick();
event.stopPropagation();
}
@Override
public void onMouseOver(MouseOverEvent event) {
mouseListener.onMouseEnter();
}
@Override
public void onMouseOut(MouseOutEvent event) {
mouseListener.onMouseLeave();
}
@Override
public void onMouseUp(MouseUpEvent event) {
mouseListener.onMouseUp();
}
@Override
public void onMouseDown(MouseDownEvent event) {
mouseListener.onMouseDown();
}
}