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