diff options
author | Artur Signell <artur.signell@itmill.com> | 2010-12-07 11:55:09 +0000 |
---|---|---|
committer | Artur Signell <artur.signell@itmill.com> | 2010-12-07 11:55:09 +0000 |
commit | b76d5386b0e0ee8188f1094ec49edbef9e9edd7b (patch) | |
tree | 042d8d4347ce8c318025eeb50d7f8c8a823896fe | |
parent | 8ef4e5d2f73eb4c599f2f72a719cfa75dffe566e (diff) | |
download | vaadin-framework-b76d5386b0e0ee8188f1094ec49edbef9e9edd7b.tar.gz vaadin-framework-b76d5386b0e0ee8188f1094ec49edbef9e9edd7b.zip |
#3752 - Refactor TextFields
Made PasswordField, TextArea extend AbstractTextField instead of TextField
Moved commonalities "columns", "input prompt", "text change event" and "cursor position" to AbstractTextField
svn changeset:16354/svn branch:6.5
-rw-r--r-- | src/com/vaadin/ui/AbstractTextField.java | 520 | ||||
-rw-r--r-- | src/com/vaadin/ui/PasswordField.java | 24 | ||||
-rw-r--r-- | src/com/vaadin/ui/TextArea.java | 81 | ||||
-rw-r--r-- | src/com/vaadin/ui/TextField.java | 419 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/textarea/TextAreaTest.java | 2 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/textfield/TextChangeEvents.java | 13 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/textfield/TextChangeEvents2.java | 2 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/textfield/TextFieldTest.java | 6 |
8 files changed, 557 insertions, 510 deletions
diff --git a/src/com/vaadin/ui/AbstractTextField.java b/src/com/vaadin/ui/AbstractTextField.java index 7fe1306ea4..cd9d99de18 100644 --- a/src/com/vaadin/ui/AbstractTextField.java +++ b/src/com/vaadin/ui/AbstractTextField.java @@ -1,3 +1,7 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + package com.vaadin.ui; import java.text.Format; @@ -5,16 +9,24 @@ import java.util.Map; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.BlurNotifier; import com.vaadin.event.FieldEvents.FocusEvent; import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.event.FieldEvents.FocusNotifier; +import com.vaadin.event.FieldEvents.TextChangeEvent; +import com.vaadin.event.FieldEvents.TextChangeListener; +import com.vaadin.event.FieldEvents.TextChangeNotifier; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.gwt.client.ui.VTextField; -public abstract class AbstractTextField extends AbstractField { +public abstract class AbstractTextField extends AbstractField implements + BlurNotifier, FocusNotifier, TextChangeNotifier { /** * Value formatter used to format the string contents. */ + @Deprecated private Format format; /** @@ -31,7 +43,56 @@ public abstract class AbstractTextField extends AbstractField { */ private int maxLength = -1; - public AbstractTextField() { + /** + * Number of visible columns in the TextField. + */ + private int columns = 0; + + /** + * The prompt to display in an empty field. Null when disabled. + */ + private String inputPrompt = null; + + /** + * The text content when the last messages to the server was sent. + */ + private String lastKnownTextContent; + + /** + * The position of the cursor when the last message to the server was sent. + */ + private int lastKnownCursorPosition; + + /** + * Flag indicating that a text change event is pending to be triggered. + * Cleared by {@link #setInternalValue(Object)} and when the event is fired. + */ + private boolean textChangeEventPending; + + private TextChangeEventMode textChangeEventMode = TextChangeEventMode.LAZY; + + private final int DEFAULT_TEXTCHANGE_TIMEOUT = 400; + + private int textChangeEventTimeout = DEFAULT_TEXTCHANGE_TIMEOUT; + + /** + * Temporarily holds the new selection position. Cleared on paint. + */ + private int selectionPosition = -1; + + /** + * Temporarily holds the new selection length. + */ + private int selectionLength; + + /** + * Flag used to determine whether we are currently handling a state change + * triggered by a user. Used to properly fire text change event before value + * change event triggered by the client side. + */ + private boolean changingVariables; + + protected AbstractTextField() { super(); } @@ -43,6 +104,16 @@ public abstract class AbstractTextField extends AbstractField { target.addAttribute("maxLength", getMaxLength()); } + // Adds the number of column and rows + final int columns = getColumns(); + if (columns != 0) { + target.addAttribute("cols", String.valueOf(columns)); + } + + if (getInputPrompt() != null) { + target.addAttribute("prompt", getInputPrompt()); + } + // Adds the content as variable String value = getFormattedValue(); if (value == null) { @@ -53,6 +124,20 @@ public abstract class AbstractTextField extends AbstractField { "Null values are not allowed if the null-representation is null"); } target.addVariable(this, "text", value); + + if (selectionPosition != -1) { + target.addAttribute("selpos", selectionPosition); + target.addAttribute("sellen", selectionLength); + selectionPosition = -1; + } + + if (hasListeners(TextChangeEvent.class)) { + target.addAttribute(VTextField.ATTR_TEXTCHANGE_EVENTMODE, + getTextChangeEventMode().toString()); + target.addAttribute(VTextField.ATTR_TEXTCHANGE_TIMEOUT, + getTextChangeTimeout()); + } + } /** @@ -88,97 +173,69 @@ public abstract class AbstractTextField extends AbstractField { @Override public void changeVariables(Object source, Map<String, Object> variables) { + changingVariables = true; - super.changeVariables(source, variables); - - // Sets the text - if (variables.containsKey("text") && !isReadOnly()) { - - // Only do the setting if the string representation of the value - // has been updated - String newValue = (String) variables.get("text"); + try { + super.changeVariables(source, variables); - // server side check for max length - if (getMaxLength() != -1 && newValue.length() > getMaxLength()) { - newValue = newValue.substring(0, getMaxLength()); - } - final String oldValue = getFormattedValue(); - if (newValue != null - && (oldValue == null || isNullSettingAllowed()) - && newValue.equals(getNullRepresentation())) { - newValue = null; + if (variables.containsKey(VTextField.VAR_CURSOR)) { + Integer object = (Integer) variables.get(VTextField.VAR_CURSOR); + lastKnownCursorPosition = object.intValue(); } - if (newValue != oldValue - && (newValue == null || !newValue.equals(oldValue))) { - boolean wasModified = isModified(); - setValue(newValue, true); - - // If the modified status changes, or if we have a formatter, - // repaint is needed after all. - if (format != null || wasModified != isModified()) { - requestRepaint(); - } + + if (variables.containsKey(VTextField.VAR_CUR_TEXT)) { + /* + * NOTE, we might want to develop this further so that on a + * value change event the whole text content don't need to be + * sent from the client to server. Just "commit" the value from + * currentText to the value. + */ + textChangeEventPending = true; + handleInputEventTextChange(variables); } - } - if (variables.containsKey(FocusEvent.EVENT_ID)) { - fireEvent(new FocusEvent(this)); - } - if (variables.containsKey(BlurEvent.EVENT_ID)) { - fireEvent(new BlurEvent(this)); - } + // Sets the text + if (variables.containsKey("text") && !isReadOnly()) { - } + // Only do the setting if the string representation of the value + // has been updated + String newValue = (String) variables.get("text"); - /** - * Sets the height of the {@link TextField} instance. - * - * <p> - * Setting height for {@link TextField} also has a side-effect that puts - * {@link TextField} into multiline mode (aka "textarea"). Multiline mode - * can also be achieved by calling {@link #setRows(int)}. The height value - * overrides the number of rows set by {@link #setRows(int)}. - * <p> - * If you want to set height of single line {@link TextField}, call - * {@link #setRows(int)} with value 0 after setting the height. Setting rows - * to 0 resets the side-effect. - * - * @see com.vaadin.ui.AbstractComponent#setHeight(float, int) - */ - @Override - public void setHeight(float height, int unit) { - super.setHeight(height, unit); - if (height > 1 && this instanceof TextField) { - /* - * In html based terminals we most commonly want to make component - * to be textarea if height is defined. Setting row field above 0 - * will render component as textarea. - */ + // server side check for max length + if (getMaxLength() != -1 && newValue.length() > getMaxLength()) { + newValue = newValue.substring(0, getMaxLength()); + } + final String oldValue = getFormattedValue(); + if (newValue != null + && (oldValue == null || isNullSettingAllowed()) + && newValue.equals(getNullRepresentation())) { + newValue = null; + } + if (newValue != oldValue + && (newValue == null || !newValue.equals(oldValue))) { + boolean wasModified = isModified(); + setValue(newValue, true); + + // If the modified status changes, or if we have a + // formatter, repaint is needed after all. + if (format != null || wasModified != isModified()) { + requestRepaint(); + } + } + } + firePendingTextChangeEvent(); + + if (variables.containsKey(FocusEvent.EVENT_ID)) { + fireEvent(new FocusEvent(this)); + } + if (variables.containsKey(BlurEvent.EVENT_ID)) { + fireEvent(new BlurEvent(this)); + } + } finally { + changingVariables = false; - ((TextField) this).setRows(2); } - } - /** - * Sets the height of the {@link TextField} instance. - * - * <p> - * Setting height for {@link TextField} also has a side-effect that puts - * {@link TextField} into multiline mode (aka "textarea"). Multiline mode - * can also be achieved by calling {@link #setRows(int)}. The height value - * overrides the number of rows set by {@link #setRows(int)}. - * <p> - * If you want to set height of single line {@link TextField}, call - * {@link #setRows(int)} with value 0 after setting the height. Setting rows - * to 0 resets the side-effect. - * - * @see com.vaadin.ui.AbstractComponent#setHeight(java.lang.String) - */ - @Override - public void setHeight(String height) { - // will call setHeight(float, int) the actually does the magic. Method - // is overridden just to document side-effects. - super.setHeight(height); } @Override @@ -328,6 +385,303 @@ public abstract class AbstractTextField extends AbstractField { requestRepaint(); } + /** + * Gets the number of columns in the editor. If the number of columns is set + * 0, the actual number of displayed columns is determined implicitly by the + * adapter. + * + * @return the number of columns in the editor. + */ + public int getColumns() { + return columns; + } + + /** + * Sets the number of columns in the editor. If the number of columns is set + * 0, the actual number of displayed columns is determined implicitly by the + * adapter. + * + * @param columns + * the number of columns to set. + */ + public void setColumns(int columns) { + if (columns < 0) { + columns = 0; + } + this.columns = columns; + requestRepaint(); + } + + /** + * Gets the current input prompt. + * + * @see #setInputPrompt(String) + * @return the current input prompt, or null if not enabled + */ + public String getInputPrompt() { + return inputPrompt; + } + + /** + * Sets the input prompt - a textual prompt that is displayed when the field + * would otherwise be empty, to prompt the user for input. + * + * @param inputPrompt + */ + public void setInputPrompt(String inputPrompt) { + this.inputPrompt = inputPrompt; + requestRepaint(); + } + + /* ** Text Change Events ** */ + + private void firePendingTextChangeEvent() { + if (textChangeEventPending) { + fireEvent(new TextChangeEventImpl(this)); + textChangeEventPending = false; + } + } + + @Override + protected void setInternalValue(Object newValue) { + if (changingVariables) { + /* + * Fire text change event before value change event if change is + * coming from the client side. + */ + if (newValue == null && lastKnownTextContent != null + && !lastKnownTextContent.equals(getNullRepresentation())) { + // Value was changed from something to null representation + lastKnownTextContent = getNullRepresentation(); + textChangeEventPending = true; + } else if (newValue != null + && !newValue.toString().equals(lastKnownTextContent)) { + // Value was changed to something else than null representation + lastKnownTextContent = newValue.toString(); + textChangeEventPending = true; + } + + if (textChangeEventPending) { + firePendingTextChangeEvent(); + } + } + super.setInternalValue(newValue); + } + + private void handleInputEventTextChange(Map<String, Object> variables) { + /* + * TODO we could vastly optimize the communication of values by using + * some sort of diffs instead of always sending the whole text content. + * Also on value change events we could use the mechanism. + */ + String object = (String) variables.get(VTextField.VAR_CUR_TEXT); + lastKnownTextContent = object; + textChangeEventPending = true; + } + + /** + * Sets the mode how the TextField triggers {@link TextChangeEvent}s. + * + * @param inputEventMode + * the new mode + * + * @see TextChangeEventMode + */ + public void setTextChangeEventMode(TextChangeEventMode inputEventMode) { + textChangeEventMode = inputEventMode; + requestRepaint(); + } + + /** + * @return the mode used to trigger {@link TextChangeEvent}s. + */ + public TextChangeEventMode getTextChangeEventMode() { + return textChangeEventMode; + } + + /** + * Different modes how the TextField can trigger {@link TextChangeEvent}s. + */ + public enum TextChangeEventMode { + + /** + * An event is triggered on each text content change, most commonly key + * press events. + */ + EAGER, + /** + * Each text change event in the UI causes the event to be communicated + * to the application after a timeout. The length of the timeout can be + * controlled with {@link TextField#setInputEventTimeout(int)}. Only the + * last input event is reported to the server side if several text + * change events happen during the timeout. + * <p> + * In case of a {@link ValueChangeEvent} the schedule is not kept + * strictly. Before a {@link ValueChangeEvent} a {@link TextChangeEvent} + * is triggered if the text content has changed since the previous + * TextChangeEvent regardless of the schedule. + */ + TIMEOUT, + /** + * An event is triggered when there is a pause of text modifications. + * The length of the pause can be modified with + * {@link TextField#setInputEventTimeout(int)}. Like with the + * {@link #TIMEOUT} mode, an event is forced before + * {@link ValueChangeEvent}s, even if the user did not keep a pause + * while entering the text. + * <p> + * This is the default mode. + */ + LAZY + } + + public void addListener(TextChangeListener listener) { + addListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, + listener, TextChangeListener.EVENT_METHOD); + } + + public void removeListener(TextChangeListener listener) { + removeListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, + listener); + } + + /** + * The text change timeout modifies how often text change events are + * communicated to the application when {@link #getTextChangeEventMode()} is + * {@link TextChangeEventMode#LAZY} or {@link TextChangeEventMode#TIMEOUT}. + * + * + * @see #getTextChangeEventMode() + * + * @param timeout + * the timeout in milliseconds + */ + public void setTextChangeTimeout(int timeout) { + textChangeEventTimeout = timeout; + requestRepaint(); + } + + /** + * Gets the timeout used to fire {@link TextChangeEvent}s when the + * {@link #getTextChangeEventMode()} is {@link TextChangeEventMode#LAZY} or + * {@link TextChangeEventMode#TIMEOUT}. + * + * @return the timeout value in milliseconds + */ + public int getTextChangeTimeout() { + return textChangeEventTimeout; + } + + public class TextChangeEventImpl extends TextChangeEvent { + private String curText; + private int cursorPosition; + + private TextChangeEventImpl(final AbstractTextField tf) { + super(tf); + curText = tf.getCurrentTextContent(); + cursorPosition = tf.getCursorPosition(); + } + + @Override + public AbstractTextField getComponent() { + return (AbstractTextField) super.getComponent(); + } + + @Override + public String getText() { + return curText; + } + + @Override + public int getCursorPosition() { + return cursorPosition; + } + + } + + /** + * Gets the current (or the last known) text content in the field. + * <p> + * Note the text returned by this method is not necessary the same that is + * returned by the {@link #getValue()} method. The value is updated when the + * terminal fires a value change event via e.g. blurring the field or by + * pressing enter. The value returned by this method is updated also on + * {@link TextChangeEvent}s. Due to this high dependency to the terminal + * implementation this method is (at least at this point) not published. + * + * @return the text which is currently displayed in the field. + */ + private String getCurrentTextContent() { + if (lastKnownTextContent != null) { + return lastKnownTextContent; + } else { + Object text = getValue(); + if (text == null) { + return getNullRepresentation(); + } + return text.toString(); + } + } + + /** + * Selects all text in the field. + * + * @since 6.4 + */ + public void selectAll() { + String text = getValue() == null ? "" : getValue().toString(); + setSelectionRange(0, text.length()); + } + + /** + * Sets the range of text to be selected. + * + * As a side effect the field will become focused. + * + * @since 6.4 + * + * @param pos + * the position of the first character to be selected + * @param length + * the number of characters to be selected + */ + public void setSelectionRange(int pos, int length) { + selectionPosition = pos; + selectionLength = length; + focus(); + requestRepaint(); + } + + /** + * Sets the cursor position in the field. As a side effect the field will + * become focused. + * + * @since 6.4 + * + * @param pos + * the position for the cursor + * */ + public void setCursorPosition(int pos) { + setSelectionRange(pos, 0); + lastKnownCursorPosition = pos; + } + + /** + * Returns the last known cursor position of the field. + * + * <p> + * Note that due to the client server nature or the GWT terminal, Vaadin + * cannot provide the exact value of the cursor position in most situations. + * The value is updated only when the client side terminal communicates to + * TextField, like on {@link ValueChangeEvent}s and {@link TextChangeEvent} + * s. This may change later if a deep push integration is built to Vaadin. + * + * @return the cursor position + */ + public int getCursorPosition() { + return lastKnownCursorPosition; + } + public void addListener(FocusListener listener) { addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod); diff --git a/src/com/vaadin/ui/PasswordField.java b/src/com/vaadin/ui/PasswordField.java index 0c9da78cd5..609108c62f 100644 --- a/src/com/vaadin/ui/PasswordField.java +++ b/src/com/vaadin/ui/PasswordField.java @@ -1,3 +1,6 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ package com.vaadin.ui; import com.vaadin.data.Property; @@ -5,27 +8,26 @@ import com.vaadin.terminal.gwt.client.ui.VPasswordField; /** * A field that is used to enter secret text information like passwords. The - * clear text is not displayed in the screen. + * entered text is not displayed on the screen. */ @ClientWidget(VPasswordField.class) -@SuppressWarnings("serial") -public class PasswordField extends TextField { +public class PasswordField extends AbstractTextField { /** * Constructs an empty PasswordField. */ public PasswordField() { - super(); + setValue(""); } /** * Constructs a PasswordField with given property data source. * * @param dataSource - * the property dato source for the field + * the property data source for the field */ public PasswordField(Property dataSource) { - super(dataSource); + setPropertyDataSource(dataSource); } /** @@ -37,7 +39,8 @@ public class PasswordField extends TextField { * the property data source for the field */ public PasswordField(String caption, Property dataSource) { - super(caption, dataSource); + this(dataSource); + setCaption(caption); } /** @@ -49,7 +52,8 @@ public class PasswordField extends TextField { * the value for the field */ public PasswordField(String caption, String value) { - super(caption, value); + setValue(value); + setCaption(caption); } /** @@ -59,7 +63,7 @@ public class PasswordField extends TextField { * the caption for the field */ public PasswordField(String caption) { - super(caption); + this(); + setCaption(caption); } - } diff --git a/src/com/vaadin/ui/TextArea.java b/src/com/vaadin/ui/TextArea.java index 9665a7a98c..83cdf8f050 100644 --- a/src/com/vaadin/ui/TextArea.java +++ b/src/com/vaadin/ui/TextArea.java @@ -1,21 +1,34 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + package com.vaadin.ui; import com.vaadin.data.Property; import com.vaadin.terminal.gwt.client.ui.VTextArea; /** - * A text field that supports multiline editing. + * A text field that supports multi line editing. */ @ClientWidget(VTextArea.class) -public class TextArea extends TextField { +public class TextArea extends AbstractTextField { private static final int DEFAULT_ROWS = 5; /** + * Number of visible rows in the text area. + */ + private int rows = DEFAULT_ROWS; + + /** + * Tells if word-wrapping should be used in the text area. + */ + private boolean wordwrap = true; + + /** * Constructs an empty TextArea. */ public TextArea() { - setRows(DEFAULT_ROWS); } /** @@ -25,8 +38,8 @@ public class TextArea extends TextField { * the caption for the field. */ public TextArea(String caption) { - super(caption); - setRows(DEFAULT_ROWS); + this(); + setCaption(caption); } /** @@ -36,8 +49,8 @@ public class TextArea extends TextField { * the data source for the field */ public TextArea(Property dataSource) { - super(dataSource); - setRows(DEFAULT_ROWS); + this(); + setPropertyDataSource(dataSource); } /** @@ -46,11 +59,11 @@ public class TextArea extends TextField { * @param caption * the caption for the field * @param dataSource - * the dato source for the field + * the data source for the field */ public TextArea(String caption, Property dataSource) { - super(caption, dataSource); - setRows(DEFAULT_ROWS); + this(dataSource); + setCaption(caption); } /** @@ -62,58 +75,60 @@ public class TextArea extends TextField { * the value for the field */ public TextArea(String caption, String value) { - super(caption, value); - setRows(DEFAULT_ROWS); + this(caption); + setValue(value); + } /** - * Sets the number of rows in the editor. + * Sets the number of rows in the text area. * * @param rows - * the number of rows for this editor. + * the number of rows for this text area. */ - @Override public void setRows(int rows) { - // TODO implement here once the API from TextField is removed - super.setRows(rows); + if (rows < 0) { + rows = 0; + } + if (this.rows != rows) { + this.rows = rows; + requestRepaint(); + } } /** - * Gets the number of rows in the editor. If the number of rows is set to 0, - * the actual number of displayed rows is determined implicitly by the + * Gets the number of rows in the text area. If the number of rows is set to + * 0, the actual number of displayed rows is determined implicitly by the * adapter. * * @return number of explicitly set rows. */ - @Override public int getRows() { - // TODO implement here once the API from TextField is removed - return super.getRows(); + return rows; } /** - * Sets the editor's word-wrap mode on or off. + * Sets the text area's word-wrap mode on or off. * * @param wordwrap - * the boolean value specifying if the editor should be in - * word-wrap mode after the call or not. + * the boolean value specifying if the text area should be in + * word-wrap mode. */ - @Override public void setWordwrap(boolean wordwrap) { - // TODO implement here once the API from TextField is removed - super.setWordwrap(wordwrap); + if (this.wordwrap != wordwrap) { + this.wordwrap = wordwrap; + requestRepaint(); + } } /** - * Tests if the editor is in word-wrap mode. + * Tests if the text area is in word-wrap mode. * - * @return <code>true</code> if the component is in the word-wrap mode, + * @return <code>true</code> if the component is in word-wrap mode, * <code>false</code> if not. */ - @Override public boolean isWordwrap() { - // TODO implement here once the API from TextField is removed - return super.isWordwrap(); + return wordwrap; } } diff --git a/src/com/vaadin/ui/TextField.java b/src/com/vaadin/ui/TextField.java index 2a3b20a117..67d9e005c8 100644 --- a/src/com/vaadin/ui/TextField.java +++ b/src/com/vaadin/ui/TextField.java @@ -4,12 +4,7 @@ package com.vaadin.ui; -import java.util.Map; - import com.vaadin.data.Property; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.TextChangeEvent; -import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.gwt.client.ui.VTextField; @@ -37,61 +32,29 @@ import com.vaadin.ui.ClientWidget.LoadStyle; */ @SuppressWarnings("serial") @ClientWidget(value = VTextField.class, loadStyle = LoadStyle.EAGER) -public class TextField extends AbstractTextField implements - FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, - FieldEvents.TextChangeNotifier { +public class TextField extends AbstractTextField { /** * Tells if input is used to enter sensitive information that is not echoed * to display. Typically passwords. */ + @Deprecated private boolean secret = false; /** * Number of visible rows in a multiline TextField. Value 0 implies a * single-line text-editor. */ + @Deprecated private int rows = 0; /** * Tells if word-wrapping should be used in multiline mode. */ + @Deprecated private boolean wordwrap = true; /** - * Number of visible columns in the TextField. - */ - private int columns = 0; - - private String inputPrompt = null; - - private int selectionPosition = -1; - private int selectionLength; - - private int lastKnownCursorPosition; - - private String lastKnownTextContent; - - /** - * Flag indicating that a text change event is pending to be triggered. - * Cleared by {@link #setInternalValue(Object)} and when the event is fired. - */ - private boolean textChangeEventPending; - - /** - * Flag used to determine wheter we are currently handling a state change - * triggered by a user. Used to properly fire text change event before value - * change event triggered by the client side. - */ - private boolean changingVariables; - - private TextChangeEventMode textChangeEventMode = TextChangeEventMode.LAZY; - - private final int DEFAULT_TEXTCHANGE_TIMEOUT = 400; - - private int textChangeEventTimeout = DEFAULT_TEXTCHANGE_TIMEOUT; - - /** * Constructs an empty <code>TextField</code> with no caption. */ public TextField() { @@ -152,13 +115,13 @@ public class TextField extends AbstractTextField implements } /** - * Gets the secret property on and off. If a field is used to enter - * secretinformation the information is not echoed to display. + * Gets the secret property. If a field is used to enter secret information + * the information is not echoed to display. * * @return <code>true</code> if the field is used to enter secret * information, <code>false</code> otherwise. * - * @deprecated in 6.5 use {@link PasswordField} instead + * @deprecated Starting from 6.5 use {@link PasswordField} instead */ @Deprecated public boolean isSecret() { @@ -166,13 +129,13 @@ public class TextField extends AbstractTextField implements } /** - * Sets the secret property on and off. If a field is used to enter - * secretinformation the information is not echoed to display. + * Sets the secret property on and off. If a field is used to enter secret + * information the information is not echoed to display. * * @param secret * the value specifying if the field is used to enter secret * information. - * @deprecated in 6.5 use {@link PasswordField} instead + * @deprecated Starting from 6.5 use {@link PasswordField} instead */ @Deprecated public void setSecret(boolean secret) { @@ -187,11 +150,7 @@ public class TextField extends AbstractTextField implements if (isSecret()) { target.addAttribute("secret", true); } - // Adds the number of column and rows - final int columns = getColumns(); - if (columns != 0) { - target.addAttribute("cols", String.valueOf(columns)); - } + final int rows = getRows(); if (rows != 0) { target.addAttribute("rows", String.valueOf(rows)); @@ -203,22 +162,6 @@ public class TextField extends AbstractTextField implements } } - if (getInputPrompt() != null) { - target.addAttribute("prompt", getInputPrompt()); - } - - if (selectionPosition != -1) { - target.addAttribute("selpos", selectionPosition); - target.addAttribute("sellen", selectionLength); - selectionPosition = -1; - } - if (hasListeners(TextChangeEvent.class)) { - target.addAttribute(VTextField.ATTR_TEXTCHANGE_EVENTMODE, - getTextChangeEventMode().toString()); - target.addAttribute(VTextField.ATTR_TEXTCHANGE_TIMEOUT, - getTextChangeTimeout()); - } - super.paintContent(target); } @@ -294,326 +237,58 @@ public class TextField extends AbstractTextField implements } /** - * Gets the number of columns in the editor. If the number of columns is set - * 0, the actual number of displayed columns is determined implicitly by the - * adapter. - * - * @return the number of columns in the editor. - */ - public int getColumns() { - return columns; - } - - /** - * Sets the number of columns in the editor. If the number of columns is set - * 0, the actual number of displayed columns is determined implicitly by the - * adapter. - * - * @param columns - * the number of columns to set. - */ - public void setColumns(int columns) { - if (columns < 0) { - columns = 0; - } - this.columns = columns; - requestRepaint(); - } - - /** - * Gets the current input prompt. - * - * @see #setInputPrompt(String) - * @return the current input prompt, or null if not enabled - */ - public String getInputPrompt() { - return inputPrompt; - } - - /** - * Sets the input prompt - a textual prompt that is displayed when the field - * would otherwise be empty, to prompt the user for input. - * - * @param inputPrompt - */ - public void setInputPrompt(String inputPrompt) { - this.inputPrompt = inputPrompt; - requestRepaint(); - } - - /** - * Selects all text in the field. - * - * @since 6.4 - */ - public void selectAll() { - String text = getValue() == null ? "" : getValue().toString(); - setSelectionRange(0, text.length()); - } - - /** - * Sets the range of text to be selected. - * - * As a side effect the field will become focused. - * - * @since 6.4 - * - * @param pos - * the position of the first character to be selected - * @param length - * the number of characters to be selected - */ - public void setSelectionRange(int pos, int length) { - selectionPosition = pos; - selectionLength = length; - focus(); - requestRepaint(); - } - - /** - * Sets the cursor position in the field. As a side effect the field will - * become focused. - * - * @since 6.4 - * - * @param pos - * the position for the cursor - * */ - public void setCursorPosition(int pos) { - setSelectionRange(pos, 0); - lastKnownCursorPosition = pos; - } - - /** - * Returns the last known cursor position of the field. + * Sets the height of the {@link TextField} instance. * * <p> - * Note that due to the client server nature or the GWT terminal, Vaadin - * cannot provide the exact value of the cursor position in most situations. - * The value is updated only when the client side terminal communicates to - * TextField, like on {@link ValueChangeEvent}s and {@link TextChangeEvent} - * s. This may change later if a deep push integration is built to Vaadin. - * - * @return the cursor position - */ - public int getCursorPosition() { - return lastKnownCursorPosition; - } - - /** - * Gets the current (or the last known) text content in the field. + * Setting height for {@link TextField} also has a side-effect that puts + * {@link TextField} into multiline mode (aka "textarea"). Multiline mode + * can also be achieved by calling {@link #setRows(int)}. The height value + * overrides the number of rows set by {@link #setRows(int)}. * <p> - * Note the text returned by this method is not necessary the same what is - * returned by the {@link #getValue()} method. The value is updated when the - * terminal fires a value change event via e.g. blurring the field or by - * pressing enter. The value returned by this method is updated also on - * {@link TextChangeEvent}s. Due to this high dependency to the terminal - * implementation this method is (at least at this point) not published. + * If you want to set height of single line {@link TextField}, call + * {@link #setRows(int)} with value 0 after setting the height. Setting rows + * to 0 resets the side-effect. + * <p> + * Starting from 6.5 you should use {@link TextArea} instead of + * {@link TextField} for multiline text input. + * * - * @return the text which is currently displayed in the field. + * @see com.vaadin.ui.AbstractComponent#setHeight(float, int) */ - private String getCurrentTextContent() { - if (lastKnownTextContent != null) { - return lastKnownTextContent; - } else { - Object text = getValue(); - if (text == null) { - return getNullRepresentation(); - } - return text.toString(); - } - } - - @Override - public void changeVariables(Object source, Map<String, Object> variables) { - if (variables.containsKey(VTextField.VAR_CURSOR)) { - Integer object = (Integer) variables.get(VTextField.VAR_CURSOR); - lastKnownCursorPosition = object.intValue(); - } - if (variables.containsKey(VTextField.VAR_CUR_TEXT)) { - /* - * NOTE, we might want to develop this further so that on a value - * change event the whole text content don't need to be sent from - * the client to server. Just "commit" the value from currentText to - * the value. - */ - textChangeEventPending = true; - handleInputEventTextChange(variables); - } - - changingVariables = true; - try { - super.changeVariables(source, variables); - } finally { - changingVariables = false; - } - firePendingTextChangeEvent(); - } - - private void firePendingTextChangeEvent() { - if (textChangeEventPending) { - fireEvent(new TextChangeEventImpl(this)); - textChangeEventPending = false; - } - } - @Override - protected void setInternalValue(Object newValue) { - if (changingVariables) { + public void setHeight(float height, int unit) { + super.setHeight(height, unit); + if (height > 1 && getClass() == TextField.class) { /* - * Fire text change event before value change event if change is - * coming from the client side. + * In html based terminals we most commonly want to make component + * to be textarea if height is defined. Setting row field above 0 + * will render component as textarea. */ - if (newValue == null && lastKnownTextContent != null - && !lastKnownTextContent.equals(getNullRepresentation())) { - // Value was changed from something to null representation - lastKnownTextContent = getNullRepresentation(); - textChangeEventPending = true; - } else if (newValue != null - && !newValue.toString().equals(lastKnownTextContent)) { - // Value was changed to something else than null representation - lastKnownTextContent = newValue.toString(); - textChangeEventPending = true; - } - if (textChangeEventPending) { - firePendingTextChangeEvent(); - } + setRows(2); } - super.setInternalValue(newValue); } - private void handleInputEventTextChange(Map<String, Object> variables) { - /* - * TODO we could vastly optimize the communication of values by using - * some sort of diffs instead of always sending the whole text content. - * Also on value change events we could use the mechanism. - */ - String object = (String) variables.get(VTextField.VAR_CUR_TEXT); - lastKnownTextContent = object; - textChangeEventPending = true; - } - - /* ** Text Change Events ** */ - /** - * Sets the mode how the TextField triggers {@link TextChangeEvent}s. + * Sets the height of the {@link TextField} instance. * - * @param inputEventMode - * the new mode - * - * @see TextChangeEventMode - */ - public void setTextChangeEventMode(TextChangeEventMode inputEventMode) { - textChangeEventMode = inputEventMode; - requestRepaint(); - } - - /** - * @return the mode used to trigger {@link TextChangeEvent}s. - */ - public TextChangeEventMode getTextChangeEventMode() { - return textChangeEventMode; - } - - /** - * Different modes how the TextField can trigger {@link TextChangeEvent}s. - */ - public enum TextChangeEventMode { - - /** - * An event is triggered on each text content change, most commonly key - * press events. - */ - EAGER, - /** - * Each text change event in the UI causes the event to be communicated - * to the application after a timeout. The length of the timeout can be - * controlled with {@link TextField#setInputEventTimeout(int)}. Only the - * last input event is reported to the server side if several text - * change events happen during the timeout. - * <p> - * In case of a {@link ValueChangeEvent} the schedule is not kept - * strictly. Before a {@link ValueChangeEvent} a {@link TextChangeEvent} - * is triggered if the text content has changed since the previous - * TextChangeEvent regardless of the schedule. - */ - TIMEOUT, - /** - * An event is triggered when there is a pause of text modifications. - * The length of the pause can be modified with - * {@link TextField#setInputEventTimeout(int)}. Like with the - * {@link #TIMEOUT} mode, an event is forced before - * {@link ValueChangeEvent}s, even if the user did not keep a pause - * while entering the text. - * <p> - * This is the default mode. - */ - LAZY - } - - public void addListener(TextChangeListener listener) { - addListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, - listener, TextChangeListener.EVENT_METHOD); - } - - public void removeListener(TextChangeListener listener) { - removeListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, - listener); - } - - /** - * The text change timeout modifies how often text change events are - * communicated to the application when {@link #getTextChangeEventMode()} is - * {@link TextChangeEventMode#LAZY} or {@link TextChangeEventMode#TIMEOUT}. - * - * - * @see #getTextChangeEventMode() - * - * @param timeout - * the timeout in milliseconds - */ - public void setTextChangeTimeout(int timeout) { - textChangeEventTimeout = timeout; - requestRepaint(); - } - - /** - * Gets the timeout used to fire {@link TextChangeEvent}s when the - * {@link #getTextChangeEventMode()} is {@link TextChangeEventMode#LAZY} or - * {@link TextChangeEventMode#TIMEOUT}. + * <p> + * Setting height for {@link TextField} also has a side-effect that puts + * {@link TextField} into multiline mode (aka "textarea"). Multiline mode + * can also be achieved by calling {@link #setRows(int)}. The height value + * overrides the number of rows set by {@link #setRows(int)}. + * <p> + * If you want to set height of single line {@link TextField}, call + * {@link #setRows(int)} with value 0 after setting the height. Setting rows + * to 0 resets the side-effect. * - * @return the timeout value in milliseconds + * @see com.vaadin.ui.AbstractComponent#setHeight(java.lang.String) */ - public int getTextChangeTimeout() { - return textChangeEventTimeout; - } - - public class TextChangeEventImpl extends TextChangeEvent { - private String curText; - private int cursorPosition; - - private TextChangeEventImpl(final TextField tf) { - super(tf); - curText = tf.getCurrentTextContent(); - cursorPosition = tf.getCursorPosition(); - } - - @Override - public TextField getComponent() { - return (TextField) super.getComponent(); - } - - @Override - public String getText() { - return curText; - } - - @Override - public int getCursorPosition() { - return cursorPosition; - } - + @Override + public void setHeight(String height) { + // will call setHeight(float, int) the actually does the magic. Method + // is overridden just to document side-effects. + super.setHeight(height); } } diff --git a/tests/src/com/vaadin/tests/components/textarea/TextAreaTest.java b/tests/src/com/vaadin/tests/components/textarea/TextAreaTest.java index 34ed567b78..1977067a19 100644 --- a/tests/src/com/vaadin/tests/components/textarea/TextAreaTest.java +++ b/tests/src/com/vaadin/tests/components/textarea/TextAreaTest.java @@ -7,8 +7,8 @@ import java.util.List; import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.tests.components.abstractfield.AbstractTextFieldTest;
+import com.vaadin.ui.AbstractTextField.TextChangeEventMode;
import com.vaadin.ui.TextArea;
-import com.vaadin.ui.TextField.TextChangeEventMode;
public class TextAreaTest extends AbstractTextFieldTest<TextArea> implements
TextChangeListener {
diff --git a/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents.java b/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents.java index 4317000812..f754beb792 100644 --- a/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents.java +++ b/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents.java @@ -5,9 +5,9 @@ import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.tests.components.TestBase; import com.vaadin.tests.util.Log; import com.vaadin.tests.util.TestUtils; +import com.vaadin.ui.AbstractTextField.TextChangeEventMode; import com.vaadin.ui.TextArea; import com.vaadin.ui.TextField; -import com.vaadin.ui.TextField.TextChangeEventMode; public class TextChangeEvents extends TestBase { Log l = new Log(10); @@ -22,9 +22,8 @@ public class TextChangeEvents extends TestBase { public void textChange(TextChangeEvent event) { l.log("Text change event for " + event.getComponent().getCaption() - + ", text content currently:'" - + event.getText() + "' Cursor at index:" - + event.getCursorPosition()); + + ", text content currently:'" + event.getText() + + "' Cursor at index:" + event.getCursorPosition()); } }; @@ -47,7 +46,7 @@ public class TextChangeEvents extends TestBase { ta.addListener(inputEventListener); getLayout().addComponent(ta); - VaadinDeveloeprNameField vd = new VaadinDeveloeprNameField(); + VaadinDeveloperNameField vd = new VaadinDeveloperNameField(); vd.addListener(inputEventListener); getLayout().addComponent(vd); @@ -73,13 +72,13 @@ public class TextChangeEvents extends TestBase { * 2010-10 * */ - private class VaadinDeveloeprNameField extends TextField implements + private class VaadinDeveloperNameField extends TextField implements TextChangeListener { private String[] names = new String[] { "Matti Tahvonen", "Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita", "Marko Grönroos", "Artur Signell" }; - public VaadinDeveloeprNameField() { + public VaadinDeveloperNameField() { setCaption("Start typing 'old' Vaadin developers."); addListener((TextChangeListener) this); setStyleName("nomatch"); diff --git a/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents2.java b/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents2.java index 523d8f4057..b40ebb2434 100644 --- a/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents2.java +++ b/tests/src/com/vaadin/tests/components/textfield/TextChangeEvents2.java @@ -9,10 +9,10 @@ import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.event.FieldEvents.TextChangeEvent; import com.vaadin.event.FieldEvents.TextChangeListener; import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.AbstractTextField.TextChangeEventMode; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.TextField; -import com.vaadin.ui.TextField.TextChangeEventMode; public class TextChangeEvents2 extends TestBase { @Override diff --git a/tests/src/com/vaadin/tests/components/textfield/TextFieldTest.java b/tests/src/com/vaadin/tests/components/textfield/TextFieldTest.java index 1b1041f053..5937993e83 100644 --- a/tests/src/com/vaadin/tests/components/textfield/TextFieldTest.java +++ b/tests/src/com/vaadin/tests/components/textfield/TextFieldTest.java @@ -7,11 +7,11 @@ import java.util.List; import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.tests.components.abstractfield.AbstractTextFieldTest;
+import com.vaadin.ui.AbstractTextField.TextChangeEventMode;
import com.vaadin.ui.TextField;
-import com.vaadin.ui.TextField.TextChangeEventMode;
-public class TextFieldTest extends AbstractTextFieldTest<TextField>
- implements TextChangeListener {
+public class TextFieldTest extends AbstractTextFieldTest<TextField> implements
+ TextChangeListener {
private Command<TextField, Boolean> secretCommand = new Command<TextField, Boolean>() {
@SuppressWarnings("deprecation")
|