summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-24 23:10:54 +0300
committerArtur Signell <artur@vaadin.com>2016-08-25 11:03:06 +0300
commit983a4e3417a0b3ad63ee3dc921b4b46305e30a8d (patch)
treef1e6585f06fbc98618a0c233289d31bf796a3aed
parent848b877d479b704280814509335b1bd0814af2df (diff)
downloadvaadin-framework-983a4e3417a0b3ad63ee3dc921b4b46305e30a8d.tar.gz
vaadin-framework-983a4e3417a0b3ad63ee3dc921b4b46305e30a8d.zip
Use RPC for TextField cursor position and selection range updates
Change-Id: I48595a1d1a9a1620739d00a499d996026bd51000
-rw-r--r--client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java62
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractTextField.java28
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldClientRpc.java39
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/textfield/TextFieldState.java9
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/textfield/SelectionAndCursorPosition.java98
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/textfield/Selection.java54
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/textfield/SelectionAndCursorPositionTest.java91
7 files changed, 287 insertions, 94 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java
index aec7b0f3d5..2c1c64b633 100644
--- a/client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java
+++ b/client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java
@@ -17,7 +17,6 @@
package com.vaadin.client.ui.textfield;
import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.event.InputEvent;
@@ -26,6 +25,7 @@ import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.client.ui.VTextField;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.Connect.LoadStyle;
+import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
import com.vaadin.shared.ui.textfield.TextFieldState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
@@ -37,6 +37,35 @@ import com.vaadin.ui.TextField;
@Connect(value = TextField.class, loadStyle = LoadStyle.EAGER)
public class TextFieldConnector extends AbstractComponentConnector {
+ private class AbstractTextFieldClientRpcImpl
+ implements AbstractTextFieldClientRpc {
+ @Override
+ public void selectRange(int start, int length) {
+ int textLength = getWidget().getText().length();
+ start = restrictTo(start, 0, textLength - 1);
+ length = restrictTo(length, 0, textLength - start);
+ getWidget().setSelectionRange(start, length);
+ }
+
+ private int restrictTo(int value, int min, int max) {
+ if (value < min) {
+ value = min;
+ }
+ if (value > max) {
+ value = max;
+ }
+
+ return value;
+ }
+
+ @Override
+ public void selectAll() {
+ getWidget().selectAll();
+ }
+ }
+
+ private int lastSentCursorPosition = -1;
+
private Timer valueChangeTrigger = new Timer() {
@Override
public void run() {
@@ -46,6 +75,8 @@ public class TextFieldConnector extends AbstractComponentConnector {
@Override
protected void init() {
+ registerRpc(AbstractTextFieldClientRpc.class,
+ new AbstractTextFieldClientRpcImpl());
ConnectorFocusAndBlurHandler.addHandlers(this);
getWidget().addChangeHandler(event -> sendValueChange());
getWidget().addDomHandler(event -> {
@@ -102,33 +133,10 @@ public class TextFieldConnector extends AbstractComponentConnector {
getWidget().setReadOnly(getState().readOnly);
}
- @OnStateChange({ "selectionStart", "selectionLength" })
- private void updateSelection() {
- if (getState().selectionStart != -1) {
- Scheduler.get().scheduleDeferred(new Command() {
- @Override
- public void execute() {
- getWidget().setSelectionRange(getState().selectionStart,
- getState().selectionLength);
- }
- });
- }
- }
-
- @OnStateChange("cursorPosition")
- private void updateCursorPosition() {
- Scheduler.get().scheduleDeferred(new Command() {
- @Override
- public void execute() {
- getWidget().setCursorPos(getState().cursorPosition);
- }
- });
- }
-
private boolean hasStateChanged() {
boolean textChanged = !getWidget().getValue().equals(getState().text);
boolean cursorPosChanged = getWidget()
- .getCursorPos() != getState().cursorPosition;
+ .getCursorPos() != lastSentCursorPosition;
return textChanged || cursorPosChanged;
}
@@ -136,9 +144,9 @@ public class TextFieldConnector extends AbstractComponentConnector {
if (!hasStateChanged()) {
return;
}
+ lastSentCursorPosition = getWidget().getCursorPos();
getRpcProxy(AbstractTextFieldServerRpc.class)
- .setText(getWidget().getValue(), getWidget().getCursorPos());
+ .setText(getWidget().getValue(), lastSentCursorPosition);
getState().text = getWidget().getValue();
- getState().cursorPosition = getWidget().getCursorPos();
}
}
diff --git a/server/src/main/java/com/vaadin/ui/AbstractTextField.java b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
index f6e31d6da3..8043766a11 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractTextField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
@@ -26,6 +26,7 @@ import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.Registration;
+import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
import com.vaadin.shared.ui.textfield.TextFieldState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
@@ -56,13 +57,16 @@ public abstract class AbstractTextField extends AbstractField<String> {
public void setText(String text, int cursorPosition) {
getUI().getConnectorTracker().getDiffState(AbstractTextField.this)
.put("text", text);
- getUI().getConnectorTracker().getDiffState(AbstractTextField.this)
- .put("cursorPosition", cursorPosition);
- getState(false).cursorPosition = cursorPosition;
+ lastKnownCursorPosition = cursorPosition;
setValue(text, true);
}
}
+ private int lastKnownCursorPosition = -1;
+
+ /**
+ * Creates a new instance.
+ */
protected AbstractTextField() {
registerRpc(new TextFieldServerRpcImpl());
}
@@ -125,24 +129,27 @@ public abstract class AbstractTextField extends AbstractField<String> {
/**
* Selects all text in the field.
+ * <p>
+ * As a side effect the field will become focused.
*/
public void selectAll() {
- setSelection(0, getValue().length());
+ getRpcProxy(AbstractTextFieldClientRpc.class).selectAll();
+ focus();
}
/**
* Sets the range of text to be selected.
- *
+ * <p>
* As a side effect the field will become focused.
*
- * @param pos
+ * @param start
* the position of the first character to be selected
* @param length
* the number of characters to be selected
*/
public void setSelection(int start, int length) {
- getState().selectionStart = start;
- getState().selectionLength = length;
+ getRpcProxy(AbstractTextFieldClientRpc.class).selectRange(start,
+ length);
focus();
}
@@ -154,8 +161,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
* the position for the cursor
*/
public void setCursorPosition(int pos) {
- getState().cursorPosition = pos;
- focus();
+ setSelection(pos, 0);
}
/**
@@ -163,7 +169,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
*
*/
public int getCursorPosition() {
- return getState(false).cursorPosition;
+ return lastKnownCursorPosition;
}
/**
diff --git a/shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldClientRpc.java b/shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldClientRpc.java
new file mode 100644
index 0000000000..171707d848
--- /dev/null
+++ b/shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldClientRpc.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2016 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.shared.ui.textfield;
+
+import com.vaadin.shared.communication.ClientRpc;
+
+/**
+ * Server to client RPC interface for AbstractTextField.
+ *
+ */
+public interface AbstractTextFieldClientRpc extends ClientRpc {
+ /**
+ * Selects the given range in the field.
+ *
+ * @param start
+ * the start of the range
+ * @param length
+ * the length to select
+ */
+ void selectRange(int start, int length);
+
+ /**
+ * Selects everything in the field.
+ */
+ void selectAll();
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/textfield/TextFieldState.java b/shared/src/main/java/com/vaadin/shared/ui/textfield/TextFieldState.java
index a61a2897f7..f30af9ec78 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/textfield/TextFieldState.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/textfield/TextFieldState.java
@@ -51,15 +51,6 @@ public class TextFieldState extends AbstractFieldState {
public String text = "";
@NoLayout
- public int selectionStart = -1;
-
- @NoLayout
- public int selectionLength = 0;
-
- @NoLayout
- public int cursorPosition = 0;
-
- @NoLayout
public ValueChangeMode valueChangeMode = ValueChangeMode.LAZY;
@NoLayout
diff --git a/uitest/src/main/java/com/vaadin/tests/components/textfield/SelectionAndCursorPosition.java b/uitest/src/main/java/com/vaadin/tests/components/textfield/SelectionAndCursorPosition.java
index 42f648cfc2..61ae5e535d 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/textfield/SelectionAndCursorPosition.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/textfield/SelectionAndCursorPosition.java
@@ -1,10 +1,9 @@
package com.vaadin.tests.components.textfield;
-import com.vaadin.tests.components.TestBase;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.AbstractTextField;
import com.vaadin.ui.Button;
-import com.vaadin.ui.Button.ClickEvent;
-import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
@@ -12,14 +11,24 @@ import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import com.vaadin.v7.ui.TextArea;
-public class SelectionAndCursorPosition extends TestBase {
+public class SelectionAndCursorPosition extends AbstractTestUI {
+
+ static final String DEFAULT_TEXT = "So we have some text to select";
+ static final String TEXTFIELD_ID = "tf";
+ static final String TEXTAREA_ID = "ta";
+ static final String SELECT_ALL_ID = "selectAll";
+ static final String RANGE_START_ID = "rS";
+ static final String RANGE_LENGTH_ID = "rL";
+ static final String CURSOR_POS_ID = "cp";
+ static final String RANGE_SET_BUTTON_ID = "setSelection";
+ static final String CURSOR_POS_SET_ID = "cps";
TextField textField = createTextField();
TextArea textArea = createTextArea();
AbstractTextField activeComponent = textField;
@Override
- protected void setup() {
+ protected void setup(VaadinRequest request) {
FormLayout fl = new FormLayout();
Panel panel = new Panel(fl);
panel.setCaption("Hackers panel");
@@ -42,49 +51,44 @@ public class SelectionAndCursorPosition extends TestBase {
// });
fl.addComponent(ml);
- Button b = new Button("Select all ( selectAll() )");
- b.addListener(new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- activeComponent.selectAll();
- }
- });
- fl.addComponent(b);
+ Button selectAll = new Button("Select all ( selectAll() )");
+ selectAll.setId(SELECT_ALL_ID);
+ selectAll.addClickListener(event -> activeComponent.selectAll());
+ fl.addComponent(selectAll);
HorizontalLayout selectRange = new HorizontalLayout();
selectRange.setCaption(
"Select range of text ( setSelectionRange(int start, int lengt) )");
final TextField start = new TextField("From:");
+ start.setId(RANGE_START_ID);
final TextField length = new TextField("Selection length:");
- b = new Button("select");
- b.addListener(new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- int startPos = Integer.parseInt(start.getValue());
- int lenght = Integer.parseInt(length.getValue());
-
- activeComponent.setSelection(startPos, lenght);
- }
+ length.setId(RANGE_LENGTH_ID);
+ Button select = new Button("select");
+ select.setId(RANGE_SET_BUTTON_ID);
+ select.addClickListener(event -> {
+ int startPos = Integer.parseInt(start.getValue());
+ int lenght = Integer.parseInt(length.getValue());
+
+ activeComponent.setSelection(startPos, lenght);
});
selectRange.addComponent(start);
selectRange.addComponent(length);
- selectRange.addComponent(b);
+ selectRange.addComponent(select);
fl.addComponent(selectRange);
HorizontalLayout setCursorPosition = new HorizontalLayout();
final TextField pos = new TextField("Position:");
- b = new Button("set");
- b.addListener(new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- int startPos = Integer.parseInt(pos.getValue());
- activeComponent.setCursorPosition(startPos);
- }
+ pos.setId(CURSOR_POS_ID);
+ Button setCursorButton = new Button("set");
+ setCursorButton.setId(CURSOR_POS_SET_ID);
+ setCursorButton.addClickListener(event -> {
+ int startPos = Integer.parseInt(pos.getValue());
+ activeComponent.setCursorPosition(startPos);
});
setCursorPosition.addComponent(pos);
- setCursorPosition.addComponent(b);
+ setCursorPosition.addComponent(setCursorButton);
setCursorPosition.setCaption(
"Set cursor position ( setCursorPosition(int pos) )");
fl.addComponent(setCursorPosition);
@@ -95,29 +99,29 @@ public class SelectionAndCursorPosition extends TestBase {
}
private static TextField createTextField() {
- TextField tf = new TextField();
- tf.setCaption("Text field");
- tf.setValue("So we have some text to select");
- tf.setWidth("400px");
+ TextField textField = new TextField();
+ textField.setId(TEXTFIELD_ID);
+ textField.setCaption("Text field");
+ textField.setValue(DEFAULT_TEXT);
+ textField.setWidth("400px");
- return tf;
+ return textField;
}
private static TextArea createTextArea() {
- TextArea ta = new TextArea();
- ta.setCaption("Text area");
- ta.setValue("So we have some text to select");
- ta.setWidth("400px");
- ta.setHeight("50px");
-
- return ta;
+ TextArea textArea = new TextArea();
+ textArea.setId(TEXTAREA_ID);
+ textArea.setCaption("Text area");
+ textArea.setValue(DEFAULT_TEXT);
+ textArea.setWidth("400px");
+ textArea.setHeight("50px");
+
+ return textArea;
}
@Override
- protected String getDescription() {
- return "For usability reasons it is often essential that developer "
- + "can hint how to select the text in the "
- + "field or where to set the cursor position.";
+ protected String getTestDescription() {
+ return "Tests that setSelectionRange and setCursorPosition works for a TextField";
}
@Override
diff --git a/uitest/src/test/java/com/vaadin/tests/components/textfield/Selection.java b/uitest/src/test/java/com/vaadin/tests/components/textfield/Selection.java
new file mode 100644
index 0000000000..7988339344
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/textfield/Selection.java
@@ -0,0 +1,54 @@
+package com.vaadin.tests.components.textfield;
+
+import java.util.List;
+
+public class Selection {
+ private int start, length;
+
+ public Selection(List<Long> range) {
+ start = range.get(0).intValue();
+ length = range.get(1).intValue() - start;
+ }
+
+ public Selection(int start, int length) {
+ super();
+ this.start = start;
+ this.length = length;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + length;
+ result = prime * result + start;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Selection other = (Selection) obj;
+ if (length != other.length) {
+ return false;
+ }
+ if (start != other.start) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Selection [start=" + start + ", length=" + length + "]";
+ }
+
+} \ No newline at end of file
diff --git a/uitest/src/test/java/com/vaadin/tests/components/textfield/SelectionAndCursorPositionTest.java b/uitest/src/test/java/com/vaadin/tests/components/textfield/SelectionAndCursorPositionTest.java
new file mode 100644
index 0000000000..8b540b1095
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/textfield/SelectionAndCursorPositionTest.java
@@ -0,0 +1,91 @@
+package com.vaadin.tests.components.textfield;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.TextFieldElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class SelectionAndCursorPositionTest extends SingleBrowserTest {
+
+ private static final int DEFAULT_TEXT_LENGTH = SelectionAndCursorPosition.DEFAULT_TEXT
+ .length();
+ private WebElement textField;
+
+ @Test
+ public void testSelection() {
+ openTestURL();
+ textField = findElement(By.id(SelectionAndCursorPosition.TEXTFIELD_ID));
+
+ // Select all
+ getSelectAll().click();
+ assertSelection(0, DEFAULT_TEXT_LENGTH);
+
+ // Select range
+ setSelectionRange(10, 5);
+ assertSelection(10, 5);
+
+ // Test for index out of bounds
+ setSelectionRange(0, DEFAULT_TEXT_LENGTH);
+ assertSelection(0, DEFAULT_TEXT_LENGTH);
+ setSelectionRange(0, DEFAULT_TEXT_LENGTH + 1);
+ assertSelection(0, DEFAULT_TEXT_LENGTH);
+ setSelectionRange(1, DEFAULT_TEXT_LENGTH);
+ assertSelection(1, DEFAULT_TEXT_LENGTH - 1);
+ setSelectionRange(DEFAULT_TEXT_LENGTH - 1, 2);
+ assertSelection(DEFAULT_TEXT_LENGTH - 1, 1);
+
+ // Cursor position
+ setCursorPosition(0);
+ assertCursorPosition(0);
+
+ }
+
+ private void assertCursorPosition(int i) {
+ assertSelection(i, 0);
+ }
+
+ private void setCursorPosition(int i) {
+ $(TextFieldElement.class).id(SelectionAndCursorPosition.CURSOR_POS_ID)
+ .setValue(String.valueOf(i));
+ $(ButtonElement.class).id(SelectionAndCursorPosition.CURSOR_POS_SET_ID)
+ .click();
+
+ }
+
+ private void setSelectionRange(int start, int length) {
+ $(TextFieldElement.class).id(SelectionAndCursorPosition.RANGE_START_ID)
+ .setValue(String.valueOf(start));
+ $(TextFieldElement.class).id(SelectionAndCursorPosition.RANGE_LENGTH_ID)
+ .setValue(String.valueOf(length));
+ $(ButtonElement.class)
+ .id(SelectionAndCursorPosition.RANGE_SET_BUTTON_ID).click();
+ }
+
+ private void assertSelection(int start, int length) {
+ Assert.assertEquals(new Selection(start, length),
+ getSelection(textField));
+ }
+
+ private void clearSelection() {
+ setSelectionRange(0, 0);
+
+ }
+
+ private WebElement getSelectAll() {
+ return findElement(By.id(SelectionAndCursorPosition.SELECT_ALL_ID));
+ }
+
+ private Selection getSelection(WebElement textField) {
+ @SuppressWarnings("unchecked")
+ List<Long> range = (List<Long>) executeScript(
+ "return [arguments[0].selectionStart,arguments[0].selectionEnd]",
+ textField);
+ return new Selection(range);
+ }
+}