/** * 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(); } }