/** * 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.text; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.ImageResource.ImageOptions; import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiConstructor; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Panel; 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 some text with separate images * for the left and right sides, and the x-repeat image behind it. * * +------------+-----------------------+-------------+ * | leftElem | middleElem | rightElem | * | | (xrepeat) | | * +------------+-----------------------+-------------+ * */ public class TextButton extends Composite implements ButtonDisplay { interface Binder extends UiBinder<Panel, TextButton> {} private static Binder binder = GWT.create(Binder.class); @UiField protected Element middle; public interface Resources extends ClientBundle { /* REGULAR_BUTTON images */ @Source("button_left.png") @ImageOptions(flipRtl = true) ImageResource regularLeftImage(); @Source("button_middle.png") @ImageOptions(repeatStyle=RepeatStyle.Horizontal) ImageResource regularMiddleImage(); @Source("button_right.png") @ImageOptions(flipRtl = true) ImageResource regularRightImage(); @Source("button_left_down.png") @ImageOptions(flipRtl = true) ImageResource regularLeftDownImage(); @Source("button_middle_down.png") @ImageOptions(repeatStyle=RepeatStyle.Horizontal) ImageResource regularMiddleDownImage(); @Source("button_right_down.png") @ImageOptions(flipRtl = true) ImageResource regularRightDownImage(); /* PRIMARY_BUTTON images */ @Source("primary_button_left.png") @ImageOptions(flipRtl = true) ImageResource primaryLeftImage(); @Source("primary_button_middle.png") @ImageOptions(repeatStyle=RepeatStyle.Horizontal) ImageResource primaryMiddleImage(); @Source("primary_button_right.png") @ImageOptions(flipRtl = true) ImageResource primaryRightImage(); @Source("primary_button_left_down.png") @ImageOptions(flipRtl = true) ImageResource primaryLeftDownImage(); @Source("primary_button_middle_down.png") @ImageOptions(repeatStyle=RepeatStyle.Horizontal) ImageResource primaryMiddleDownImage(); @Source("primary_button_right_down.png") @ImageOptions(flipRtl = true) ImageResource primaryRightDownImage(); /* ADD_BUTTON images */ @Source("add_button_left.png") @ImageOptions(flipRtl = true) ImageResource addButtonLeftImage(); @Source("add_button_middle.png") @ImageOptions(repeatStyle=RepeatStyle.Horizontal) ImageResource addButtonMiddleImage(); @Source("add_button_right.png") @ImageOptions(flipRtl = true) ImageResource addButtonRightImage(); /* SYSTEM_BUTTON images */ @Source("system_button_left.png") @ImageOptions(flipRtl = true) ImageResource systemButtonLeftImage(); @Source("system_button_middle.png") @ImageOptions(repeatStyle = RepeatStyle.Horizontal) ImageResource systemButtonMiddleImage(); @Source("system_button_right.png") @ImageOptions(flipRtl = true) ImageResource systemButtonRightImage(); @Source("system_button_left_down.png") @ImageOptions(flipRtl = true) ImageResource systemLeftDownImage(); @Source("system_button_middle_down.png") @ImageOptions(repeatStyle = RepeatStyle.Horizontal) ImageResource systemMiddleDownImage(); @Source("system_button_right_down.png") @ImageOptions(flipRtl = true) ImageResource systemRightDownImage(); @Source("TextButton.css") Css css(); interface Css extends CssResource { String addButton(); String primaryButton(); String regularButton(); String systemButton(); String fullWidth(); String full(); String left(); String middle(); String middletd(); String right(); String down(); String hover(); String disabled(); String bold(); String cursorPointer(); } } /** * The styles that this kind of button can have. * */ public enum TextButtonStyle { REGULAR_BUTTON(res.css().regularButton()), PRIMARY_BUTTON(res.css().primaryButton()), ADD_BUTTON(res.css().addButton()), SYSTEM_BUTTON(res.css().systemButton()); private final String style; private TextButtonStyle(String style) { this.style = style; } public String getStyleName() { return style; } } /** The singleton instance of our resources. */ @UiField(provided=true) static final Resources res = GWT.create(Resources.class); static { StyleInjector.inject(res.css().getText()); } TextButtonStyle style; /** * Creates a {@link TextButton} with the given text, style and logic. * * @param text The text that the button should have. * @param style The style that the button should have. * @param tooltip The tooltip for this button. */ @UiConstructor public TextButton(String text, TextButtonStyle style, String tooltip) { this.style = style; initWidget(binder.createAndBindUi(this)); addStyleName(style.getStyleName()); setText(text); setTooltip(tooltip); boldStyle = new StyleAxis(getElement()); cursorStyle = new StyleAxis(getElement()); fullWidthStyle = new StyleAxis(getElement()); cursorStyle.setStyle(res.css().cursorPointer()); } // TODO(schuck,jameskozianski): Combine 0-arg constructor + init() method into // the single 3-arg constructor. Remove 0-arg constr + init(). public TextButton() { this("", TextButtonStyle.REGULAR_BUTTON, ""); } /** * Constructs a TextButton instance. * * @param text The text that the button should have. * @param style The style that the button should have. * @param tooltip The tooltip for this button. */ public void init(String text, TextButtonStyle style, String tooltip) { removeStyleName(this.style.getStyleName()); addStyleName(style.getStyleName()); this.style = style; setText(text); setTooltip(tooltip); } private final StyleAxis boldStyle; private final StyleAxis cursorStyle; private final StyleAxis fullWidthStyle; public void setBold(boolean isBold) { boldStyle.setStyle(isBold ? res.css().bold() : null); } public void setFullWidth(boolean isFullWidth) { fullWidthStyle.setStyle(isFullWidth ? res.css().fullWidth() : null); } /** * The listener for mouse events on this widget. */ private MouseListener mouseListener; /** * The CSS class that we have currently applied to the element because of the * hover / normal / down state. */ private String stateStyleName; /** * Whether or not we should call stopPropagation() on click events. */ private boolean stopPropagation = false; /** {@inheritDoc} */ public void setUiListener(MouseListener mouseListener) { sinkEvents(Event.MOUSEEVENTS); sinkEvents(Event.ONCLICK); this.mouseListener = mouseListener; } @Override public void setTooltip(String tooltip) { setTitle(tooltip); } /** {@inheritDoc} */ public void setState(ButtonState state) { if (stateStyleName != null) { removeStyleName(stateStyleName); } switch (state) { case DISABLED: addStyleName(stateStyleName = res.css().disabled()); break; case DOWN: addStyleName(stateStyleName = res.css().down()); break; case HOVER: addStyleName(stateStyleName = res.css().hover()); break; case NORMAL: stateStyleName = null; break; } } @Override public void onBrowserEvent(Event event) { if (mouseListener == null) { super.onBrowserEvent(event); return; } switch (event.getTypeInt()) { case Event.ONMOUSEDOWN: mouseListener.onMouseDown(); break; case Event.ONMOUSEOUT: mouseListener.onMouseLeave(); break; case Event.ONMOUSEOVER: mouseListener.onMouseEnter(); break; case Event.ONMOUSEUP: mouseListener.onMouseUp(); break; case Event.ONCLICK: mouseListener.onClick(); if (stopPropagation) { event.stopPropagation(); } break; } super.onBrowserEvent(event); } @Override protected void onDetach() { super.onDetach(); if (mouseListener != null) { mouseListener.onMouseLeave(); } } /** * @return The middle element which has an xrepeated background. */ public Element getMiddle() { return middle; } /** * @param stopPropagation Whether or not this button stops propagation when a * click event is received. */ public void setStopPropagation(boolean stopPropagation) { this.stopPropagation = stopPropagation; } /** * Sets the text of this text button. * * @param text Text to show in the button. */ public void setText(String text) { middle.setInnerText(text); } public void setHtml(String html) { middle.setInnerHTML(html); } /** * Makes this button assume the given style. * * @param style The new style for this button. */ public void changeStyle(TextButtonStyle style) { removeStyleName(this.style.getStyleName()); this.style = style; addStyleName(this.style.getStyleName()); } }