From 849d5041e4d0de462058227f23a4d3ca6d54db2a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Tue, 27 Sep 2011 09:43:44 +0000 Subject: [PATCH] #6588 Repainting in TextChangeListener will send wrong value to client svn changeset:21332/svn branch:6.7 --- .../terminal/gwt/client/ui/VTextField.java | 13 ++- src/com/vaadin/ui/AbstractTextField.java | 34 +++++++- .../textfield/TextFieldEagerRepaint.html | 81 +++++++++++++++++++ .../textfield/TextFieldEagerRepaint.java | 53 ++++++++++++ 4 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.html create mode 100644 tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.java diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index 8bc655f39f..f7edf5705f 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -64,6 +64,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field, private static final String CLASSNAME_PROMPT = "prompt"; private static final String ATTR_INPUTPROMPT = "prompt"; public static final String ATTR_TEXTCHANGE_TIMEOUT = "iet"; + public static final String ATTR_TEXT_CHANGED = "textChanged"; public static final String VAR_CURSOR = "c"; public static final String ATTR_TEXTCHANGE_EVENTMODE = "iem"; private static final String TEXTCHANGE_MODE_EAGER = "EAGER"; @@ -244,8 +245,16 @@ public class VTextField extends TextBoxBase implements Paintable, Field, setColumns(new Integer(uidl.getStringAttribute("cols")).intValue()); } - final String text = uidl.hasVariable("text") ? uidl - .getStringVariable("text") : null; + final String text; + if (uidl.hasAttribute(ATTR_TEXT_CHANGED) + && uidl.getBooleanAttribute(ATTR_TEXT_CHANGED) + && uidl.hasVariable("text")) { + // Use value from UIDL only if something hans changed on the server + text = uidl.getStringVariable("text"); + } else { + // Use what we already have if no change from the server + text = prompting ? null : getText(); + } setPrompting(inputPrompt != null && focusedTextField != this && (text == null || text.equals(""))); diff --git a/src/com/vaadin/ui/AbstractTextField.java b/src/com/vaadin/ui/AbstractTextField.java index 4ed76d367b..c5f24ea4bb 100644 --- a/src/com/vaadin/ui/AbstractTextField.java +++ b/src/com/vaadin/ui/AbstractTextField.java @@ -92,6 +92,12 @@ public abstract class AbstractTextField extends AbstractField implements */ private boolean changingVariables; + /** + * Track whether the value on the server has actually changed to avoid + * updating the text in the input element on every repaint + */ + private boolean localValueChanged = true; + protected AbstractTextField() { super(); } @@ -123,6 +129,11 @@ public abstract class AbstractTextField extends AbstractField implements throw new IllegalStateException( "Null values are not allowed if the null-representation is null"); } + + if (localValueChanged || target.isFullRepaint()) { + target.addAttribute(VTextField.ATTR_TEXT_CHANGED, true); + localValueChanged = false; + } target.addVariable(this, "text", value); if (selectionPosition != -1) { @@ -213,7 +224,8 @@ public abstract class AbstractTextField extends AbstractField implements if (newValue != oldValue && (newValue == null || !newValue.equals(oldValue))) { boolean wasModified = isModified(); - setValue(newValue, true); + // Don't update the local change flag + super.setValue(newValue, true); // If the modified status changes, or if we have a // formatter, repaint is needed after all. @@ -237,6 +249,26 @@ public abstract class AbstractTextField extends AbstractField implements } + @Override + protected void setValue(Object newValue, boolean repaintIsNotNeeded) + throws ReadOnlyException, ConversionException { + if (isChanged(newValue, getValue()) + || isChanged(newValue, lastKnownTextContent)) { + // The client should use the new value + localValueChanged = true; + if (!repaintIsNotNeeded) { + // Repaint even if super.setValue doesn't detect any change + requestRepaint(); + } + } + super.setValue(newValue, repaintIsNotNeeded); + } + + private static boolean isChanged(Object newValue, Object oldValue) { + return oldValue != newValue + && (newValue == null || !newValue.equals(oldValue)); + } + @Override public Class getType() { return String.class; diff --git a/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.html b/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.html new file mode 100644 index 0000000000..ecd0467fb2 --- /dev/null +++ b/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.html @@ -0,0 +1,81 @@ + + + + + + +New Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
New Test
open/run/com.vaadin.tests.components.textfield.TextFieldEagerRepaint?restartApplication
mouseClickvaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTextField[0]78,8
enterCharactervaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTextField[0]abCDef
pause100
assertValuevaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VTextField[0]abef
mouseClickvaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]73,15
enterCharactervaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]a
pause100
assertElementWidthvaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]150
enterCharactervaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]aB
pause100
assertElementWidthvaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]100
assertValuevaadin=runcomvaadintestscomponentstextfieldTextFieldEagerRepaint::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTextField[0]aB
+ + diff --git a/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.java b/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.java new file mode 100644 index 0000000000..08751a59fd --- /dev/null +++ b/tests/src/com/vaadin/tests/components/textfield/TextFieldEagerRepaint.java @@ -0,0 +1,53 @@ +package com.vaadin.tests.components.textfield; + +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.TextField; + +public class TextFieldEagerRepaint extends TestBase { + + @Override + protected void setup() { + + final TextField tf1 = new TextField("Updates value"); + tf1.setTextChangeEventMode(TextChangeEventMode.EAGER); + tf1.addListener(new TextChangeListener() { + public void textChange(TextChangeEvent event) { + String text = event.getText(); + if (!text.matches("[a-z]*")) { + String newValue = text.replaceAll("[^a-z]", ""); + tf1.setValue(newValue); + } + } + }); + + final TextField tf2 = new TextField("Updates width"); + tf2.setTextChangeEventMode(TextChangeEventMode.EAGER); + tf2.addListener(new TextChangeListener() { + public void textChange(TextChangeEvent event) { + String text = event.getText(); + if (!text.matches("[a-z]*")) { + tf2.setWidth("100px"); + } else { + tf2.setWidth("150px"); + } + } + }); + + addComponent(tf1); + addComponent(tf2); + } + + @Override + protected String getDescription() { + return "Updating the value in an EAGER TextChangeListener should send the new value to the client while updating something else (e.g. the width) should preserve the text in the field. Both fields react when the field contains anything else than lower case letters a-z"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(6588); + } + +} -- 2.39.5