aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/vaadin/terminal/gwt/client/ui/button/VButton.java')
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/button/VButton.java440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
new file mode 100644
index 0000000000..71581f5a63
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java
@@ -0,0 +1,440 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui.button;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.user.client.ui.FocusWidget;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.MouseEventDetails;
+import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.VTooltip;
+import com.vaadin.terminal.gwt.client.ui.Icon;
+import com.vaadin.terminal.gwt.client.ui.button.ButtonConnector.ButtonServerRpc;
+
+public class VButton extends FocusWidget implements ClickHandler {
+
+ public static final String CLASSNAME = "v-button";
+ private static final String CLASSNAME_PRESSED = "v-pressed";
+
+ // mouse movement is checked before synthesizing click event on mouseout
+ protected static int MOVE_THRESHOLD = 3;
+ protected int mousedownX = 0;
+ protected int mousedownY = 0;
+
+ protected String paintableId;
+
+ protected ApplicationConnection client;
+
+ protected final Element wrapper = DOM.createSpan();
+
+ protected Element errorIndicatorElement;
+
+ protected final Element captionElement = DOM.createSpan();
+
+ protected Icon icon;
+
+ /**
+ * Helper flag to handle special-case where the button is moved from under
+ * mouse while clicking it. In this case mouse leaves the button without
+ * moving.
+ */
+ protected boolean clickPending;
+
+ private boolean enabled = true;
+
+ private int tabIndex = 0;
+
+ protected boolean disableOnClick = false;
+
+ /*
+ * BELOW PRIVATE MEMBERS COPY-PASTED FROM GWT CustomButton
+ */
+
+ /**
+ * If <code>true</code>, this widget is capturing with the mouse held down.
+ */
+ private boolean isCapturing;
+
+ /**
+ * If <code>true</code>, this widget has focus with the space bar down.
+ */
+ private boolean isFocusing;
+
+ /**
+ * Used to decide whether to allow clicks to propagate up to the superclass
+ * or container elements.
+ */
+ private boolean disallowNextClick = false;
+ private boolean isHovering;
+
+ protected int clickShortcut = 0;
+ // TODO Move this to VButtonPaintable
+ ButtonServerRpc buttonRpcProxy;
+
+ public VButton() {
+ super(DOM.createDiv());
+ setTabIndex(0);
+ sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.FOCUSEVENTS
+ | Event.KEYEVENTS);
+ sinkEvents(VTooltip.TOOLTIP_EVENTS);
+
+ setStyleName(CLASSNAME);
+
+ // Add a11y role "button"
+ Accessibility.setRole(getElement(), Accessibility.ROLE_BUTTON);
+
+ wrapper.setClassName(getStylePrimaryName() + "-wrap");
+ getElement().appendChild(wrapper);
+ captionElement.setClassName(getStylePrimaryName() + "-caption");
+ wrapper.appendChild(captionElement);
+
+ addClickHandler(this);
+ }
+
+ public void setText(String text) {
+ captionElement.setInnerText(text);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ /*
+ * Copy-pasted from GWT CustomButton, some minor modifications done:
+ *
+ * -for IE/Opera added CLASSNAME_PRESSED
+ *
+ * -event.preventDefault() commented from ONMOUSEDOWN (Firefox won't apply
+ * :active styles if it is present)
+ *
+ * -Tooltip event handling added
+ *
+ * -onload event handler added (for icon handling)
+ */
+ public void onBrowserEvent(Event event) {
+ if (client != null) {
+ client.handleTooltipEvent(event, this);
+ }
+ if (DOM.eventGetType(event) == Event.ONLOAD) {
+ Util.notifyParentOfSizeChange(this, true);
+ }
+ // Should not act on button if disabled.
+ if (!isEnabled()) {
+ // This can happen when events are bubbled up from non-disabled
+ // children
+ return;
+ }
+
+ int type = DOM.eventGetType(event);
+ switch (type) {
+ case Event.ONCLICK:
+ // If clicks are currently disallowed, keep it from bubbling or
+ // being passed to the superclass.
+ if (disallowNextClick) {
+ event.stopPropagation();
+ disallowNextClick = false;
+ return;
+ }
+ break;
+ case Event.ONMOUSEDOWN:
+ if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) {
+ // This was moved from mouseover, which iOS sometimes skips.
+ // We're certainly hovering at this point, and we don't actually
+ // need that information before this point.
+ setHovering(true);
+ }
+ if (event.getButton() == Event.BUTTON_LEFT) {
+ // save mouse position to detect movement before synthesizing
+ // event later
+ mousedownX = event.getClientX();
+ mousedownY = event.getClientY();
+
+ disallowNextClick = true;
+ clickPending = true;
+ setFocus(true);
+ DOM.setCapture(getElement());
+ isCapturing = true;
+ // Prevent dragging (on some browsers);
+ // DOM.eventPreventDefault(event);
+ if (BrowserInfo.get().isIE() || BrowserInfo.get().isOpera()) {
+ addStyleName(CLASSNAME_PRESSED);
+ }
+ }
+ break;
+ case Event.ONMOUSEUP:
+ if (isCapturing) {
+ isCapturing = false;
+ DOM.releaseCapture(getElement());
+ if (isHovering() && event.getButton() == Event.BUTTON_LEFT) {
+ // Click ok
+ disallowNextClick = false;
+ }
+ if (BrowserInfo.get().isIE() || BrowserInfo.get().isOpera()) {
+ removeStyleName(CLASSNAME_PRESSED);
+ }
+ // Explicitly prevent IE 8 from propagating mouseup events
+ // upward (fixes #6753)
+ if (BrowserInfo.get().isIE8()) {
+ event.stopPropagation();
+ }
+ }
+ break;
+ case Event.ONMOUSEMOVE:
+ clickPending = false;
+ if (isCapturing) {
+ // Prevent dragging (on other browsers);
+ DOM.eventPreventDefault(event);
+ }
+ break;
+ case Event.ONMOUSEOUT:
+ Element to = event.getRelatedTarget();
+ if (getElement().isOrHasChild(DOM.eventGetTarget(event))
+ && (to == null || !getElement().isOrHasChild(to))) {
+ if (clickPending
+ && Math.abs(mousedownX - event.getClientX()) < MOVE_THRESHOLD
+ && Math.abs(mousedownY - event.getClientY()) < MOVE_THRESHOLD) {
+ onClick();
+ break;
+ }
+ clickPending = false;
+ if (isCapturing) {
+ }
+ setHovering(false);
+ if (BrowserInfo.get().isIE() || BrowserInfo.get().isOpera()) {
+ removeStyleName(CLASSNAME_PRESSED);
+ }
+ }
+ break;
+ case Event.ONBLUR:
+ if (isFocusing) {
+ isFocusing = false;
+ }
+ break;
+ case Event.ONLOSECAPTURE:
+ if (isCapturing) {
+ isCapturing = false;
+ }
+ break;
+ }
+
+ super.onBrowserEvent(event);
+
+ // Synthesize clicks based on keyboard events AFTER the normal key
+ // handling.
+ if ((event.getTypeInt() & Event.KEYEVENTS) != 0) {
+ switch (type) {
+ case Event.ONKEYDOWN:
+ if (event.getKeyCode() == 32 /* space */) {
+ isFocusing = true;
+ event.preventDefault();
+ }
+ break;
+ case Event.ONKEYUP:
+ if (isFocusing && event.getKeyCode() == 32 /* space */) {
+ isFocusing = false;
+
+ /*
+ * If click shortcut is space then the shortcut handler will
+ * take care of the click.
+ */
+ if (clickShortcut != 32 /* space */) {
+ onClick();
+ }
+
+ event.preventDefault();
+ }
+ break;
+ case Event.ONKEYPRESS:
+ if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
+
+ /*
+ * If click shortcut is enter then the shortcut handler will
+ * take care of the click.
+ */
+ if (clickShortcut != KeyCodes.KEY_ENTER) {
+ onClick();
+ }
+
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+ }
+
+ final void setHovering(boolean hovering) {
+ if (hovering != isHovering()) {
+ isHovering = hovering;
+ }
+ }
+
+ final boolean isHovering() {
+ return isHovering;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
+ * .dom.client.ClickEvent)
+ */
+ public void onClick(ClickEvent event) {
+ if (paintableId == null || client == null) {
+ return;
+ }
+ if (BrowserInfo.get().isSafari()) {
+ VButton.this.setFocus(true);
+ }
+ if (disableOnClick) {
+ setEnabled(false);
+ buttonRpcProxy.disableOnClick();
+ }
+
+ // Add mouse details
+ MouseEventDetails details = MouseEventDetailsBuilder
+ .buildMouseEventDetails(event.getNativeEvent(), getElement());
+ buttonRpcProxy.click(details);
+
+ clickPending = false;
+ }
+
+ /*
+ * ALL BELOW COPY-PASTED FROM GWT CustomButton
+ */
+
+ /**
+ * Called internally when the user finishes clicking on this button. The
+ * default behavior is to fire the click event to listeners. Subclasses that
+ * override {@link #onClickStart()} should override this method to restore
+ * the normal widget display.
+ * <p>
+ * To add custom code for a click event, override
+ * {@link #onClick(ClickEvent)} instead of this.
+ */
+ protected void onClick() {
+ // Allow the click we're about to synthesize to pass through to the
+ // superclass and containing elements. Element.dispatchEvent() is
+ // synchronous, so we simply set and clear the flag within this method.
+
+ disallowNextClick = false;
+
+ // Mouse coordinates are not always available (e.g., when the click is
+ // caused by a keyboard event).
+ NativeEvent evt = Document.get().createClickEvent(1, 0, 0, 0, 0, false,
+ false, false, false);
+ getElement().dispatchEvent(evt);
+ }
+
+ /**
+ * Sets whether this button is enabled.
+ *
+ * @param enabled
+ * <code>true</code> to enable the button, <code>false</code> to
+ * disable it
+ */
+
+ @Override
+ public final void setEnabled(boolean enabled) {
+ if (isEnabled() != enabled) {
+ this.enabled = enabled;
+ if (!enabled) {
+ cleanupCaptureState();
+ Accessibility.removeState(getElement(),
+ Accessibility.STATE_PRESSED);
+ super.setTabIndex(-1);
+ addStyleName(ApplicationConnection.DISABLED_CLASSNAME);
+ } else {
+ Accessibility.setState(getElement(),
+ Accessibility.STATE_PRESSED, "false");
+ super.setTabIndex(tabIndex);
+ removeStyleName(ApplicationConnection.DISABLED_CLASSNAME);
+ }
+ }
+ }
+
+ @Override
+ public final boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public final void setTabIndex(int index) {
+ super.setTabIndex(index);
+ tabIndex = index;
+ }
+
+ /**
+ * Resets internal state if this button can no longer service events. This
+ * can occur when the widget becomes detached or disabled.
+ */
+ private void cleanupCaptureState() {
+ if (isCapturing || isFocusing) {
+ DOM.releaseCapture(getElement());
+ isCapturing = false;
+ isFocusing = false;
+ }
+ }
+
+ private static native int getHorizontalBorderAndPaddingWidth(Element elem)
+ /*-{
+ // THIS METHOD IS ONLY USED FOR INTERNET EXPLORER, IT DOESN'T WORK WITH OTHERS
+
+ var convertToPixel = function(elem, value) {
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // Remember the original values
+ var left = elem.style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ elem.style.left = value || 0;
+ var ret = elem.style.pixelLeft;
+
+ // Revert the changed values
+ elem.style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+
+ return ret;
+ }
+
+ var ret = 0;
+
+ var sides = ["Right","Left"];
+ for(var i=0; i<2; i++) {
+ var side = sides[i];
+ var value;
+ // Border -------------------------------------------------------
+ if(elem.currentStyle["border"+side+"Style"] != "none") {
+ value = elem.currentStyle["border"+side+"Width"];
+ if ( !/^\d+(px)?$/i.test( value ) && /^\d/.test( value ) ) {
+ ret += convertToPixel(elem, value);
+ } else if(value.length > 2) {
+ ret += parseInt(value.substr(0, value.length-2));
+ }
+ }
+
+ // Padding -------------------------------------------------------
+ value = elem.currentStyle["padding"+side];
+ if ( !/^\d+(px)?$/i.test( value ) && /^\d/.test( value ) ) {
+ ret += convertToPixel(elem, value);
+ } else if(value.length > 2) {
+ ret += parseInt(value.substr(0, value.length-2));
+ }
+ }
+
+ return ret;
+ }-*/;
+
+}