All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.client.ui.VButton Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 8.27.1
Show newest version
/*
 * Copyright 2000-2014 Vaadin Ltd.
 * 
 * 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 com.vaadin.client.ui;

import com.google.gwt.aria.client.Roles;
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.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FocusWidget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil;

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;

    /** For internal use only. May be removed or replaced in the future. */
    public ApplicationConnection client;

    /** For internal use only. May be removed or replaced in the future. */
    public final Element wrapper = DOM.createSpan();

    /** For internal use only. May be removed or replaced in the future. */
    public Element errorIndicatorElement;

    /** For internal use only. May be removed or replaced in the future. */
    public final Element captionElement = DOM.createSpan();

    /** For internal use only. May be removed or replaced in the future. */
    public 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;

    /*
     * BELOW PRIVATE MEMBERS COPY-PASTED FROM GWT CustomButton
     */

    /**
     * If true, this widget is capturing with the mouse held down.
     */
    private boolean isCapturing;

    /**
     * If true, this widget has focus with the space bar down. This
     * means that we will get events when the button is released, but we should
     * trigger the button only if the button is still focused at that point.
     */
    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;

    /** For internal use only. May be removed or replaced in the future. */
    public int clickShortcut = 0;

    private HandlerRegistration focusHandlerRegistration;
    private HandlerRegistration blurHandlerRegistration;
    private long lastClickTime = 0;

    public VButton() {
        super(DOM.createDiv());
        setTabIndex(0);
        sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.FOCUSEVENTS
                | Event.KEYEVENTS);

        // Add a11y role "button"
        Roles.getButtonRole().set(getElement());

        getElement().appendChild(wrapper);
        wrapper.appendChild(captionElement);

        setStyleName(CLASSNAME);

        addClickHandler(this);
    }

    @Override
    public void setStyleName(String style) {
        super.setStyleName(style);
        wrapper.setClassName(getStylePrimaryName() + "-wrap");
        captionElement.setClassName(getStylePrimaryName() + "-caption");
    }

    @Override
    public void setStylePrimaryName(String style) {
        super.setStylePrimaryName(style);
        wrapper.setClassName(getStylePrimaryName() + "-wrap");
        captionElement.setClassName(getStylePrimaryName() + "-caption");
    }

    public void setText(String text) {
        captionElement.setInnerText(text);
    }

    public void setHtml(String html) {
        captionElement.setInnerHTML(html);
    }

    @SuppressWarnings("deprecation")
    @Override
    /*
     * Copy-pasted from GWT CustomButton, some minor modifications done:
     * 
     * -for IE/Opera added CLASSNAME_PRESSED
     * 
     * -event.preventDefault() removed 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 (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:
            // fix for #14632 - on mobile safari 8, if we press the button long
            // enough, we might get two click events, so we are suppressing
            // second if it is too soon
            boolean isPhantomClickPossible = BrowserInfo.get().isSafari()
                    && BrowserInfo.get().isTouchDevice()
                    && BrowserInfo.get().getBrowserMajorVersion() == 8;
            long clickTime = isPhantomClickPossible ? System
                    .currentTimeMillis() : 0;
            // If clicks are currently disallowed or phantom, keep it from
            // bubbling or being passed to the superclass.
            if (disallowNextClick || isPhantomClickPossible
                    && (clickTime - lastClickTime < 100)) { // the maximum
                                                            // interval observed
                                                            // for phantom click
                                                            // is 69, with
                                                            // majority under
                                                            // 50, so we select
                                                            // 100 to be safe
                event.stopPropagation();
                disallowNextClick = false;
                return;
            }
            lastClickTime = clickTime;
            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;
                addStyleName(CLASSNAME_PRESSED);

                if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9()) {
                    /*
                     * We need to prevent the default behavior on these browsers
                     * since user-select is not available.
                     */
                    event.preventDefault();
                }
            }
            break;
        case Event.ONMOUSEUP:
            if (isCapturing) {
                isCapturing = false;
                DOM.releaseCapture(getElement());
                if (isHovering() && event.getButton() == Event.BUTTON_LEFT) {
                    // Click ok
                    disallowNextClick = false;
                }

                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.ONMOUSEOVER:
            if (isCapturing && isTargetInsideButton(event)) {
                // This means a mousedown happened on the button and a mouseup
                // has not happened yet
                setHovering(true);
                addStyleName(CLASSNAME_PRESSED);
            }
            break;
        case Event.ONMOUSEOUT:
            if (isTargetInsideButton(event)) {
                if (clickPending
                        && Math.abs(mousedownX - event.getClientX()) < MOVE_THRESHOLD
                        && Math.abs(mousedownY - event.getClientY()) < MOVE_THRESHOLD) {
                    onClick();
                    break;
                }
                clickPending = false;
                setHovering(false);
                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:
                // Stop propagation when the user starts pressing a button that
                // we are handling to prevent actions from getting triggered
                if (event.getKeyCode() == 32 /* space */) {
                    isFocusing = true;
                    event.preventDefault();
                    event.stopPropagation();
                } else if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
                    event.stopPropagation();
                }
                break;
            case Event.ONKEYUP:
                if (isFocusing && event.getKeyCode() == 32 /* space */) {
                    isFocusing = false;
                    onClick();
                    event.stopPropagation();
                    event.preventDefault();
                }
                break;
            case Event.ONKEYPRESS:
                if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
                    onClick();
                    event.stopPropagation();
                    event.preventDefault();
                }
                break;
            }
        }
    }

    /**
     * Check if the event occurred over an element which is part of this button
     */
    private boolean isTargetInsideButton(Event event) {
        Element to = event.getRelatedTarget();
        return getElement().isOrHasChild(DOM.eventGetTarget(event))
                && (to == null || !getElement().isOrHasChild(to));
    }

    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)
     */
    @Override
    public void onClick(ClickEvent event) {
        if (BrowserInfo.get().isSafari()) {
            VButton.this.setFocus(true);
        }

        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.
     * 

* To add custom code for a click event, override * {@link #onClick(ClickEvent)} instead of this. *

* For internal use only. May be removed or replaced in the future. */ public 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; // Screen coordinates are not always available (e.g., when the click is // caused by a keyboard event). // Set (x,y) client coordinates to the middle of the button int x = getElement().getAbsoluteLeft() - getElement().getScrollLeft() - getElement().getOwnerDocument().getScrollLeft() + WidgetUtil.getRequiredWidth(getElement()) / 2; int y = getElement().getAbsoluteTop() - getElement().getScrollTop() - getElement().getOwnerDocument().getScrollTop() + WidgetUtil.getRequiredHeight(getElement()) / 2; NativeEvent evt = Document.get().createClickEvent(1, 0, 0, x, y, false, false, false, false); getElement().dispatchEvent(evt); } /** * Sets whether this button is enabled. * * @param enabled * true to enable the button, false to * disable it */ @Override public final void setEnabled(boolean enabled) { if (isEnabled() != enabled) { this.enabled = enabled; if (!enabled) { cleanupCaptureState(); Roles.getButtonRole().setAriaDisabledState(getElement(), !enabled); super.setTabIndex(-1); } else { Roles.getButtonRole().removeAriaDisabledState(getElement()); super.setTabIndex(tabIndex); } } } @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; }-*/; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy