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

com.vaadin.client.ui.VTextField 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.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.WidgetUtil;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.textfield.TextFieldConstants;

/**
 * This class represents a basic text input field with one row.
 * 
 * @author Vaadin Ltd.
 * 
 */
public class VTextField extends TextBoxBase implements Field, ChangeHandler,
        FocusHandler, BlurHandler, KeyDownHandler {

    /**
     * The input node CSS classname.
     */
    public static final String CLASSNAME = "v-textfield";
    /**
     * This CSS classname is added to the input node on hover.
     */
    public static final String CLASSNAME_FOCUS = "focus";

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

    /** 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 String valueBeforeEdit = null;

    /**
     * Set to false if a text change event has been sent since the last value
     * change event. This means that {@link #valueBeforeEdit} should not be
     * trusted when determining whether a text change even should be sent.
     */
    private boolean valueBeforeEditIsSynced = true;

    private boolean immediate = false;
    private int maxLength = -1;

    private static final String CLASSNAME_PROMPT = "prompt";
    private static final String TEXTCHANGE_MODE_TIMEOUT = "TIMEOUT";

    private String inputPrompt = null;
    private boolean prompting = false;
    private int lastCursorPos = -1;

    // used while checking if FF has set input prompt as value
    private boolean possibleInputError = false;

    public VTextField() {
        this(DOM.createInputText());
    }

    protected VTextField(Element node) {
        super(node);
        setStyleName(CLASSNAME);
        addChangeHandler(this);
        if (BrowserInfo.get().isIE() || BrowserInfo.get().isFirefox()) {
            addKeyDownHandler(this);
        }
        addFocusHandler(this);
        addBlurHandler(this);
    }

    /**
     * For internal use only. May be removed or replaced in the future.
     * 

* TODO When GWT adds ONCUT, add it there and remove workaround. See * http://code.google.com/p/google-web-toolkit/issues/detail?id=4030 *

* Also note that the cut/paste are not totally crossbrowsers compatible. * E.g. in Opera mac works via context menu, but on via File->Paste/Cut. * Opera might need the polling method for 100% working textchanceevents. * Eager polling for a change is bit dum and heavy operation, so I guess we * should first try to survive without. */ public static final int TEXTCHANGE_EVENTS = Event.ONPASTE | Event.KEYEVENTS | Event.ONMOUSEUP; @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); if (listenTextChangeEvents && (event.getTypeInt() & TEXTCHANGE_EVENTS) == event .getTypeInt()) { deferTextChangeEvent(); } } /* * TODO optimize this so that only changes are sent + make the value change * event just a flag that moves the current text to value */ private String lastTextChangeString = null; private String getLastCommunicatedString() { return lastTextChangeString; } private void communicateTextValueToServer() { String text = getText(); if (prompting) { // Input prompt visible, text is actually "" text = ""; } if (!text.equals(getLastCommunicatedString())) { if (valueBeforeEditIsSynced && text.equals(valueBeforeEdit)) { /* * Value change for the current text has been enqueued since the * last text change event was sent, but we can't know that it * has been sent to the server. Ensure that all pending changes * are sent now. Sending a value change without a text change * will simulate a TextChangeEvent on the server. */ client.sendPendingVariableChanges(); } else { // Default case - just send an immediate text change message client.updateVariable(paintableId, TextFieldConstants.VAR_CUR_TEXT, text, true); // Shouldn't investigate valueBeforeEdit to avoid duplicate text // change events as the states are not in sync any more valueBeforeEditIsSynced = false; } lastTextChangeString = text; } } private Timer textChangeEventTrigger = new Timer() { @Override public void run() { if (isAttached()) { updateCursorPosition(); communicateTextValueToServer(); scheduled = false; } } }; private boolean scheduled = false; /** For internal use only. May be removed or replaced in the future. */ public boolean listenTextChangeEvents; /** For internal use only. May be removed or replaced in the future. */ public String textChangeEventMode; public int textChangeEventTimeout; private void deferTextChangeEvent() { if (textChangeEventMode.equals(TEXTCHANGE_MODE_TIMEOUT) && scheduled) { return; } else { textChangeEventTrigger.cancel(); } textChangeEventTrigger.schedule(getTextChangeEventTimeout()); scheduled = true; } private int getTextChangeEventTimeout() { return textChangeEventTimeout; } @Override public void setReadOnly(boolean readOnly) { boolean wasReadOnly = isReadOnly(); if (readOnly) { setTabIndex(-1); } else if (wasReadOnly && !readOnly && getTabIndex() == -1) { /* * Need to manually set tab index to 0 since server will not send * the tab index if it is 0. */ setTabIndex(0); } super.setReadOnly(readOnly); } /** For internal use only. May be removed or replaced in the future. */ public void updateFieldContent(final String text) { setPrompting(inputPrompt != null && focusedTextField != this && (text.equals(""))); String fieldValue; if (prompting) { fieldValue = isReadOnly() ? "" : inputPrompt; addStyleDependentName(CLASSNAME_PROMPT); } else { fieldValue = text; removeStyleDependentName(CLASSNAME_PROMPT); } setText(fieldValue); lastTextChangeString = valueBeforeEdit = text; valueBeforeEditIsSynced = true; } protected void onCut() { if (listenTextChangeEvents) { deferTextChangeEvent(); } } /** For internal use only. May be removed or replaced in the future. */ public native void attachCutEventListener(Element el) /*-{ var me = this; el.oncut = $entry(function() { [email protected]::onCut()(); }); }-*/; protected native void detachCutEventListener(Element el) /*-{ el.oncut = null; }-*/; private void onDrop() { if (focusedTextField == this) { return; } updateText(false); } private void updateText(boolean blurred) { String text = getText(); setPrompting(inputPrompt != null && (text == null || text.isEmpty())); if (prompting) { setText(isReadOnly() ? "" : inputPrompt); if (blurred) { addStyleDependentName(CLASSNAME_PROMPT); } } valueChange(blurred); } private void scheduleOnDropEvent() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { onDrop(); } }); } private native void attachDropEventListener(Element el) /*-{ var me = this; el.ondrop = $entry(function() { [email protected]::scheduleOnDropEvent()(); }); }-*/; private native void detachDropEventListener(Element el) /*-{ el.ondrop = null; }-*/; @Override protected void onDetach() { super.onDetach(); detachCutEventListener(getElement()); if (focusedTextField == this) { focusedTextField = null; } if (BrowserInfo.get().isFirefox()) { removeOnInputListener(getElement()); detachDropEventListener(getElement()); } } @Override protected void onAttach() { super.onAttach(); if (listenTextChangeEvents) { detachCutEventListener(getElement()); } if (BrowserInfo.get().isFirefox()) { // Workaround for FF setting input prompt as the value if esc is // pressed while the field is focused and empty (#8051). addOnInputListener(getElement()); // Workaround for FF updating component's internal value after // having drag-and-dropped text from another element (#14056) attachDropEventListener(getElement()); } } /** For internal use only. May be removed or replaced in the future. */ public void setMaxLength(int newMaxLength) { if (newMaxLength == maxLength) { return; } maxLength = newMaxLength; updateMaxLength(maxLength); } /** * This method is responsible for updating the DOM or otherwise ensuring * that the given max length is enforced. Called when the max length for the * field has changed. * * @param maxLength * The new max length */ protected void updateMaxLength(int maxLength) { if (maxLength >= 0) { getElement().setPropertyInt("maxLength", maxLength); } else { getElement().removeAttribute("maxLength"); } setMaxLengthToElement(maxLength); } protected void setMaxLengthToElement(int newMaxLength) { if (newMaxLength >= 0) { getElement().setPropertyInt("maxLength", newMaxLength); } else { getElement().removeAttribute("maxLength"); } } public int getMaxLength() { return maxLength; } @Override public void onChange(ChangeEvent event) { valueChange(false); } /** * Called when the field value might have changed and/or the field was * blurred. These are combined so the blur event is sent in the same batch * as a possible value change event (these are often connected). * * @param blurred * true if the field was blurred */ public void valueChange(boolean blurred) { if (client != null && paintableId != null) { boolean sendBlurEvent = false; boolean sendValueChange = false; if (blurred && client.hasEventListeners(this, EventId.BLUR)) { sendBlurEvent = true; client.updateVariable(paintableId, EventId.BLUR, "", false); } String newText = prompting ? "" : getText(); if (newText != null && !newText.equals(valueBeforeEdit)) { sendValueChange = immediate; client.updateVariable(paintableId, "text", newText, false); valueBeforeEdit = newText; valueBeforeEditIsSynced = true; } /* * also send cursor position, no public api yet but for easier * extension */ updateCursorPosition(); if (sendBlurEvent || sendValueChange) { /* * Avoid sending text change event as we will simulate it on the * server side before value change events. */ textChangeEventTrigger.cancel(); scheduled = false; client.sendPendingVariableChanges(); } } } /** * Updates the cursor position variable if it has changed since the last * update. * * @return true iff the value was updated */ protected boolean updateCursorPosition() { if (WidgetUtil.isAttachedAndDisplayed(this)) { int cursorPos = getCursorPos(); if (lastCursorPos != cursorPos) { client.updateVariable(paintableId, TextFieldConstants.VAR_CURSOR, cursorPos, false); lastCursorPos = cursorPos; return true; } } return false; } private static VTextField focusedTextField; public static void flushChangesFromFocusedTextField() { if (focusedTextField != null) { focusedTextField.onChange(null); } } @Override public void onFocus(FocusEvent event) { addStyleDependentName(CLASSNAME_FOCUS); if (prompting) { setText(""); removeStyleDependentName(CLASSNAME_PROMPT); setPrompting(false); } focusedTextField = this; if (client != null && client.hasEventListeners(this, EventId.FOCUS)) { client.updateVariable(paintableId, EventId.FOCUS, "", true); } } @Override public void onBlur(BlurEvent event) { // this is called twice on Chrome when e.g. changing tab while prompting // field focused - do not change settings on the second time if (focusedTextField != this) { return; } removeStyleDependentName(CLASSNAME_FOCUS); focusedTextField = null; updateText(true); } private void setPrompting(boolean prompting) { this.prompting = prompting; } public void setColumns(int columns) { if (columns <= 0) { return; } setWidth(columns + "em"); } @Override public void onKeyDown(KeyDownEvent event) { if (BrowserInfo.get().isIE() && event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { // IE does not send change events when pressing enter in a text // input so we handle it using a key listener instead valueChange(false); } else if (BrowserInfo.get().isFirefox() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE && getText().equals("")) { // check after onInput event if inputPrompt has appeared as the // value of the field possibleInputError = true; } } public void setImmediate(boolean immediate) { this.immediate = immediate; } public void setInputPrompt(String inputPrompt) { this.inputPrompt = inputPrompt; } protected boolean isWordwrap() { String wrap = getElement().getAttribute("wrap"); return !"off".equals(wrap); } private native void addOnInputListener(Element el) /*-{ var self = this; el.oninput = $entry(function() { [email protected]::checkForInputError()(); }); }-*/; private native void removeOnInputListener(Element el) /*-{ el.oninput = null; }-*/; private void checkForInputError() { if (possibleInputError && getText().equals(inputPrompt)) { setText(""); } possibleInputError = false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy