Browse Source

Implement new RichTextArea

Change-Id: I6f430c77caaad6d610133f340eba960f2268897e
tags/8.0.0.alpha2
Artur Signell 7 years ago
parent
commit
78a5468279
73 changed files with 2103 additions and 404 deletions
  1. 1
    0
      all/src/main/templates/release-notes.html
  2. 415
    0
      client/src/main/java/com/vaadin/client/ui/VRichTextArea.java
  3. 139
    0
      client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
  4. 476
    0
      client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java
  5. 2
    4
      client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java
  6. 19
    52
      client/src/main/java/com/vaadin/client/ui/textfield/AbstractTextFieldConnector.java
  7. 1
    4
      client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java
  8. 129
    0
      client/src/main/java/com/vaadin/client/ui/textfield/ValueChangeHandler.java
  9. 35
    0
      client/src/main/resources/com/vaadin/client/ui/richtextarea/VRichTextToolbar$Strings.properties
  10. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/backColors.gif
  11. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/bold.gif
  12. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/createLink.gif
  13. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/fontSizes.gif
  14. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/fonts.gif
  15. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/foreColors.gif
  16. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/gwtLogo.png
  17. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/hr.gif
  18. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/indent.gif
  19. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/insertImage.gif
  20. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/italic.gif
  21. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyCenter.gif
  22. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyLeft.gif
  23. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyRight.gif
  24. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/ol.gif
  25. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/outdent.gif
  26. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/removeFormat.gif
  27. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/removeLink.gif
  28. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/strikeThrough.gif
  29. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/subscript.gif
  30. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/superscript.gif
  31. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/ul.gif
  32. BIN
      client/src/main/resources/com/vaadin/client/ui/richtextarea/underline.gif
  33. 10
    5
      server/src/main/java/com/vaadin/ui/AbstractField.java
  34. 10
    39
      server/src/main/java/com/vaadin/ui/AbstractTextField.java
  35. 69
    0
      server/src/main/java/com/vaadin/ui/HasValueChangeMode.java
  36. 0
    12
      server/src/main/java/com/vaadin/ui/PasswordField.java
  37. 170
    0
      server/src/main/java/com/vaadin/ui/RichTextArea.java
  38. 1
    1
      server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java
  39. 98
    0
      server/src/test/java/com/vaadin/ui/ComponentTest.java
  40. 107
    0
      server/src/test/java/com/vaadin/ui/RichTextAreaTest.java
  41. 1
    1
      shared/src/main/java/com/vaadin/shared/ui/ValueChangeMode.java
  42. 29
    0
      shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaClientRpc.java
  43. 33
    0
      shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaServerRpc.java
  44. 52
    0
      shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaState.java
  45. 1
    0
      shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldState.java
  46. 1
    1
      themes/src/main/themes/VAADIN/themes/base/textfield/textfield.scss
  47. 1
    1
      uitest/src/main/java/com/vaadin/tests/TestCaptionWrapper.java
  48. 0
    75
      uitest/src/main/java/com/vaadin/tests/TestForRichTextEditor.java
  49. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/AbstractComponentContainerTest.java
  50. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/notification/NotificationsWaiAria.java
  51. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/popupview/PopupViewWithRTE.java
  52. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java
  53. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java
  54. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaScrolling.java
  55. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaSize.java
  56. 6
    41
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaTest.java
  57. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaUpdateWhileTyping.java
  58. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaWithKeyboardShortcuts.java
  59. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreas.java
  60. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/splitpanel/GridLayoutWithCheckbox.java
  61. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/splitpanel/SplitPanelWithRichTextArea.java
  62. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/textfield/MultipleTextChangeEvents.java
  63. 152
    0
      uitest/src/main/java/com/vaadin/tests/components/textfield/TextFieldsValueChangeMode.java
  64. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/tree/TreeScrolling.java
  65. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/uitest/components/TextFieldsCssTest.java
  66. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/window/WindowCloseShortcuts.java
  67. 1
    2
      uitest/src/main/java/com/vaadin/tests/fields/TabIndexes.java
  68. 1
    1
      uitest/src/main/java/com/vaadin/tests/navigator/NavigatorTest.java
  69. 1
    1
      uitest/src/main/java/com/vaadin/tests/themes/valo/Forms.java
  70. 1
    1
      uitest/src/main/java/com/vaadin/tests/themes/valo/TextFields.java
  71. 1
    1
      uitest/src/main/java/com/vaadin/v7/tests/components/textarea/TextAreaCursorPosition.java
  72. 0
    146
      uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java
  73. 124
    0
      uitest/src/test/java/com/vaadin/tests/components/textfield/TextFieldsValueChangeModeTest.java

+ 1
- 0
all/src/main/templates/release-notes.html View File

@@ -128,6 +128,7 @@
<li>Old input prompts have been replaced with placeholders utilizing the related browser functionality</li>
<li>The old liferay theme (Liferay 6.0 look) has been removed</li>
<li>Components in the compatibility packages now use the prefix "vaadin7-" in declarative design files</li>
<li>RichTextArea no longer receives a special "v-richtextarea-readonly" class when readonly, only the standard "v-readonly" class</li>
</ul>
<h3 id="knownissues">Known Issues and Limitations</h3>
<ul>

+ 415
- 0
client/src/main/java/com/vaadin/client/ui/VRichTextArea.java View File

@@ -0,0 +1,415 @@
/*
* 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.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.richtextarea.VRichTextToolbar;

/**
* This class implements a basic client side rich text editor component.
*
* @author Vaadin Ltd.
*
*/
public class VRichTextArea extends Composite implements Field, KeyPressHandler,
KeyDownHandler, Focusable, HasEnabled {

/**
* The input node CSS classname.
*/
public static final String CLASSNAME = "v-richtextarea";

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

/** 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 boolean immediate = false;

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

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

/** For internal use only. May be removed or replaced in the future. */
public HTML html = new HTML();

private final FlowPanel fp = new FlowPanel();

private boolean enabled = true;

/** For internal use only. May be removed or replaced in the future. */
public int maxLength = -1;

private int toolbarNaturalWidth = 500;

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

private ShortcutActionHandlerOwner hasShortcutActionHandler;

private boolean readOnly = false;

private final Map<BlurHandler, HandlerRegistration> blurHandlers = new HashMap<BlurHandler, HandlerRegistration>();

private List<Command> inputHandlers = new ArrayList<>();

public VRichTextArea() {
createRTAComponents();
fp.add(formatter);
fp.add(rta);

initWidget(fp);
setStyleName(CLASSNAME);

TouchScrollDelegate.enableTouchScrolling(html, html.getElement());
}

private void createRTAComponents() {
rta = new RichTextArea();
rta.setWidth("100%");
rta.addKeyDownHandler(this);
rta.addInitializeHandler(e -> {
// Must wait until iframe is attached to be able to access body
BodyElement rtaBody = IFrameElement.as(rta.getElement())
.getContentDocument().getBody();
addInputListener(rtaBody, event -> {
inputHandlers.forEach(handler -> handler.execute());
});
});

formatter = new VRichTextToolbar(rta);

// Add blur handlers
for (Entry<BlurHandler, HandlerRegistration> handler : blurHandlers
.entrySet()) {

// Remove old registration
handler.getValue().removeHandler();

// Add blur handlers
addBlurHandler(handler.getKey());
}
}

private native void addInputListener(Element element,
Consumer<NativeEvent> listener)
/*-{
element.addEventListener("input", $entry(function(event) {
listener.@java.util.function.Consumer::accept(Ljava/lang/Object;)(event);
}));
}-*/;

public void setMaxLength(int maxLength) {
if (maxLength >= 0) {
if (this.maxLength == -1) {
keyPressHandler = rta.addKeyPressHandler(this);
}
this.maxLength = maxLength;
} else if (this.maxLength != -1) {
this.maxLength = -1;
keyPressHandler.removeHandler();
}

}

@Override
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
// rta.setEnabled(enabled);
swapEditableArea();
this.enabled = enabled;
}
}

@Override
public boolean isEnabled() {
return enabled;
}

/**
* Swaps html to rta and visa versa.
*/
private void swapEditableArea() {
String value = getValue();
if (html.isAttached()) {
fp.remove(html);
if (BrowserInfo.get().isWebkit()) {
fp.remove(formatter);
createRTAComponents(); // recreate new RTA to bypass #5379
fp.add(formatter);
}
fp.add(rta);
} else {
fp.remove(rta);
fp.add(html);
}
setValue(value);
}

/** For internal use only. May be removed or replaced in the future. */
public void selectAll() {
/*
* There is a timing issue if trying to select all immediately on first
* render. Simple deferred command is not enough. Using Timer with
* moderated timeout. If this appears to fail on many (most likely slow)
* environments, consider increasing the timeout.
*
* FF seems to require the most time to stabilize its RTA. On Vaadin
* tiergarden test machines, 200ms was not enough always (about 50%
* success rate) - 300 ms was 100% successful. This however was not
* enough on a sluggish old non-virtualized XP test machine. A bullet
* proof solution would be nice, GWT 2.1 might however solve these. At
* least setFocus has a workaround for this kind of issue.
*/
new Timer() {
@Override
public void run() {
rta.getFormatter().selectAll();
}
}.schedule(320);
}

public void setReadOnly(boolean b) {
if (isReadOnly() != b) {
swapEditableArea();
readOnly = b;
}
// reset visibility in case enabled state changed and the formatter was
// recreated
formatter.setVisible(!readOnly);
}

private boolean isReadOnly() {
return readOnly;
}

@Override
public void setHeight(String height) {
super.setHeight(height);
if (height == null || height.equals("")) {
rta.setHeight("");
}
}

@Override
public void setWidth(String width) {
if (width.equals("")) {
/*
* IE cannot calculate the width of the 100% iframe correctly if
* there is no width specified for the parent. In this case we would
* use the toolbar but IE cannot calculate the width of that one
* correctly either in all cases. So we end up using a default width
* for a RichTextArea with no width definition in all browsers (for
* compatibility).
*/

super.setWidth(toolbarNaturalWidth + "px");
} else {
super.setWidth(width);
}
}

@Override
public void onKeyPress(KeyPressEvent event) {
if (maxLength >= 0) {
Scheduler.get().scheduleDeferred(new Command() {
@Override
public void execute() {
if (rta.getHTML().length() > maxLength) {
rta.setHTML(rta.getHTML().substring(0, maxLength));
}
}
});
}
}

@Override
public void onKeyDown(KeyDownEvent event) {
// delegate to closest shortcut action handler
// throw event from the iframe forward to the shortcuthandler
ShortcutActionHandler shortcutHandler = getShortcutHandlerOwner()
.getShortcutActionHandler();
if (shortcutHandler != null) {
shortcutHandler.handleKeyboardEvent(
com.google.gwt.user.client.Event.as(event.getNativeEvent()),
ConnectorMap.get(client).getConnector(this));
}
}

private ShortcutActionHandlerOwner getShortcutHandlerOwner() {
if (hasShortcutActionHandler == null) {
Widget parent = getParent();
while (parent != null) {
if (parent instanceof ShortcutActionHandlerOwner) {
break;
}
parent = parent.getParent();
}
hasShortcutActionHandler = (ShortcutActionHandlerOwner) parent;
}
return hasShortcutActionHandler;
}

@Override
public int getTabIndex() {
return rta.getTabIndex();
}

@Override
public void setAccessKey(char key) {
rta.setAccessKey(key);
}

@Override
public void setFocus(boolean focused) {
/*
* Similar issue as with selectAll. Focusing must happen before possible
* selectall, so keep the timeout here lower.
*/
new Timer() {

@Override
public void run() {
rta.setFocus(true);
}
}.schedule(300);
}

@Override
public void setTabIndex(int index) {
rta.setTabIndex(index);
}

/**
* Sets the value of the text area.
*
* @param value
* The text value, as HTML
*/
public void setValue(String value) {
if (rta.isAttached()) {
rta.setHTML(value);
} else {
html.setHTML(value);
}
}

/**
* Gets the value of the text area.
*
* @return the value as HTML
*/
public String getValue() {
if (rta.isAttached()) {
return rta.getHTML();
} else {
return html.getHTML();
}
}

/**
* Browsers differ in what they return as the content of a visually empty
* rich text area. This method is used to normalize these to an empty
* string. See #8004.
*
* @return cleaned html string
*/
public String getSanitizedValue() {
BrowserInfo browser = BrowserInfo.get();
String result = getValue();
if (browser.isFirefox()) {
if ("<br>".equals(result)) {
result = "";
}
} else if (browser.isWebkit() || browser.isEdge()) {
if ("<br>".equals(result) || "<div><br></div>".equals(result)) {
result = "";
}
} else if (browser.isIE()) {
if ("<P>&nbsp;</P>".equals(result)) {
result = "";
}
} else if (browser.isOpera()) {
if ("<br>".equals(result) || "<p><br></p>".equals(result)) {
result = "";
}
}
return result;
}

/**
* Adds a blur handler to the component.
*
* @param blurHandler
* the blur handler to add
*/
public void addBlurHandler(BlurHandler blurHandler) {
blurHandlers.put(blurHandler, rta.addBlurHandler(blurHandler));
}

/**
* Removes a blur handler.
*
* @param blurHandler
* the handler to remove
*/
public void removeBlurHandler(BlurHandler blurHandler) {
HandlerRegistration registration = blurHandlers.remove(blurHandler);
if (registration != null) {
registration.removeHandler();
}
}

public HandlerRegistration addInputHandler(Command inputHandler) {
inputHandlers.add(inputHandler);
return () -> inputHandlers.remove(inputHandler);
}
}

+ 139
- 0
client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java View File

@@ -0,0 +1,139 @@
/*
* 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.client.ui.richtextarea;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.VRichTextArea;
import com.vaadin.client.ui.textfield.ValueChangeHandler;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.Connect.LoadStyle;
import com.vaadin.shared.ui.richtextarea.RichTextAreaClientRpc;
import com.vaadin.shared.ui.richtextarea.RichTextAreaServerRpc;
import com.vaadin.shared.ui.richtextarea.RichTextAreaState;
import com.vaadin.ui.RichTextArea;

/**
* Connector for RichTextArea.
*/
@Connect(value = RichTextArea.class, loadStyle = LoadStyle.EAGER)
public class RichTextAreaConnector extends AbstractFieldConnector
implements SimpleManagedLayout, ValueChangeHandler.Owner {

private class RichTextAreaClientRpcImpl implements RichTextAreaClientRpc {
@Override
public void selectAll() {
getWidget().selectAll();
}
}

private ValueChangeHandler valueChangeHandler;

@Override
protected void init() {
getWidget().addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
flush();
}
});
getWidget().addInputHandler(() -> {
valueChangeHandler.scheduleValueChange();
});

registerRpc(RichTextAreaClientRpc.class,
new RichTextAreaClientRpcImpl());
ConnectorFocusAndBlurHandler.addHandlers(this);

valueChangeHandler = new ValueChangeHandler(this);

getLayoutManager().registerDependency(this,
getWidget().formatter.getElement());
}

@OnStateChange("valueChangeMode")
private void updateValueChangeMode() {
valueChangeHandler.setValueChangeMode(getState().valueChangeMode);
}

@OnStateChange("valueChangeTimeout")
private void updateValueChangeTimeout() {
valueChangeHandler.setValueChangeTimeout(getState().valueChangeTimeout);
}

@OnStateChange("readOnly")
private void updateReadOnly() {
getWidget().setReadOnly(getState().readOnly);
}

@Override
public void onUnregister() {
super.onUnregister();
getLayoutManager().unregisterDependency(this,
getWidget().formatter.getElement());
}

@Override
public VRichTextArea getWidget() {
return (VRichTextArea) super.getWidget();
}

@Override
public void layout() {
if (!isUndefinedHeight()) {
int rootElementInnerHeight = getLayoutManager()
.getInnerHeight(getWidget().getElement());
int formatterHeight = getLayoutManager()
.getOuterHeight(getWidget().formatter.getElement());
int editorHeight = rootElementInnerHeight - formatterHeight;
if (editorHeight < 0) {
editorHeight = 0;
}
getWidget().rta.setHeight(editorHeight + "px");
}
}

@Override
public RichTextAreaState getState() {
return (RichTextAreaState) super.getState();
}

private boolean hasStateChanged(String widgetValue) {
return !widgetValue.equals(getState().value);
}

@Override
public void sendValueChange() {
String widgetValue = getWidget().getSanitizedValue();
if (!hasStateChanged(widgetValue)) {
return;
}

getRpcProxy(RichTextAreaServerRpc.class).setText(widgetValue);
getState().value = widgetValue;

}

@Override
public void flush() {
super.flush();
sendValueChange();
}
}

+ 476
- 0
client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java View File

@@ -0,0 +1,476 @@
/*
* 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.
*/
/*
* Copyright 2007 Google Inc.
*
* 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.richtextarea;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.ToggleButton;

/**
* A modified version of sample toolbar for use with {@link RichTextArea}. It
* provides a simple UI for all rich text formatting, dynamically displayed only
* for the available functionality.
*/
public class VRichTextToolbar extends Composite {

/**
* This {@link ClientBundle} is used for all the button icons. Using a
* bundle allows all of these images to be packed into a single image, which
* saves a lot of HTTP requests, drastically improving startup time.
*/
public interface Images extends ClientBundle {

ImageResource bold();

ImageResource createLink();

ImageResource hr();

ImageResource indent();

ImageResource insertImage();

ImageResource italic();

ImageResource justifyCenter();

ImageResource justifyLeft();

ImageResource justifyRight();

ImageResource ol();

ImageResource outdent();

ImageResource removeFormat();

ImageResource removeLink();

ImageResource strikeThrough();

ImageResource subscript();

ImageResource superscript();

ImageResource ul();

ImageResource underline();
}

/**
* This {@link Constants} interface is used to make the toolbar's strings
* internationalizable.
*/
public interface Strings extends Constants {

String black();

String blue();

String bold();

String color();

String createLink();

String font();

String green();

String hr();

String indent();

String insertImage();

String italic();

String justifyCenter();

String justifyLeft();

String justifyRight();

String large();

String medium();

String normal();

String ol();

String outdent();

String red();

String removeFormat();

String removeLink();

String size();

String small();

String strikeThrough();

String subscript();

String superscript();

String ul();

String underline();

String white();

String xlarge();

String xsmall();

String xxlarge();

String xxsmall();

String yellow();
}

/**
* We use an inner EventHandler class to avoid exposing event methods on the
* RichTextToolbar itself.
*/
private class EventHandler
implements ClickHandler, ChangeHandler, KeyUpHandler {

@Override
@SuppressWarnings("deprecation")
public void onChange(ChangeEvent event) {
Object sender = event.getSource();
if (sender == backColors) {
basic.setBackColor(
backColors.getValue(backColors.getSelectedIndex()));
backColors.setSelectedIndex(0);
} else if (sender == foreColors) {
basic.setForeColor(
foreColors.getValue(foreColors.getSelectedIndex()));
foreColors.setSelectedIndex(0);
} else if (sender == fonts) {
basic.setFontName(fonts.getValue(fonts.getSelectedIndex()));
fonts.setSelectedIndex(0);
} else if (sender == fontSizes) {
basic.setFontSize(
fontSizesConstants[fontSizes.getSelectedIndex() - 1]);
fontSizes.setSelectedIndex(0);
}
}

@Override
@SuppressWarnings("deprecation")
public void onClick(ClickEvent event) {
Object sender = event.getSource();
if (sender == bold) {
basic.toggleBold();
} else if (sender == italic) {
basic.toggleItalic();
} else if (sender == underline) {
basic.toggleUnderline();
} else if (sender == subscript) {
basic.toggleSubscript();
} else if (sender == superscript) {
basic.toggleSuperscript();
} else if (sender == strikethrough) {
extended.toggleStrikethrough();
} else if (sender == indent) {
extended.rightIndent();
} else if (sender == outdent) {
extended.leftIndent();
} else if (sender == justifyLeft) {
basic.setJustification(RichTextArea.Justification.LEFT);
} else if (sender == justifyCenter) {
basic.setJustification(RichTextArea.Justification.CENTER);
} else if (sender == justifyRight) {
basic.setJustification(RichTextArea.Justification.RIGHT);
} else if (sender == insertImage) {
final String url = Window.prompt("Enter an image URL:",
"http://");
if (url != null) {
extended.insertImage(url);
}
} else if (sender == createLink) {
final String url = Window.prompt("Enter a link URL:",
"http://");
if (url != null) {
extended.createLink(url);
}
} else if (sender == removeLink) {
extended.removeLink();
} else if (sender == hr) {
extended.insertHorizontalRule();
} else if (sender == ol) {
extended.insertOrderedList();
} else if (sender == ul) {
extended.insertUnorderedList();
} else if (sender == removeFormat) {
extended.removeFormat();
} else if (sender == richText) {
// We use the RichTextArea's onKeyUp event to update the toolbar
// status. This will catch any cases where the user moves the
// cursur using the keyboard, or uses one of the browser's
// built-in keyboard shortcuts.
updateStatus();
}
}

@Override
public void onKeyUp(KeyUpEvent event) {
if (event.getSource() == richText) {
// We use the RichTextArea's onKeyUp event to update the toolbar
// status. This will catch any cases where the user moves the
// cursor using the keyboard, or uses one of the browser's
// built-in keyboard shortcuts.
updateStatus();
}
}
}

private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] {
RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL,
RichTextArea.FontSize.SMALL, RichTextArea.FontSize.MEDIUM,
RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE,
RichTextArea.FontSize.XX_LARGE };

private final Images images = (Images) GWT.create(Images.class);
private final Strings strings = (Strings) GWT.create(Strings.class);
private final EventHandler handler = new EventHandler();

private final RichTextArea richText;
@SuppressWarnings("deprecation")
private final RichTextArea.BasicFormatter basic;
@SuppressWarnings("deprecation")
private final RichTextArea.ExtendedFormatter extended;

private final FlowPanel outer = new FlowPanel();
private final FlowPanel topPanel = new FlowPanel();
private final FlowPanel bottomPanel = new FlowPanel();
private ToggleButton bold;
private ToggleButton italic;
private ToggleButton underline;
private ToggleButton subscript;
private ToggleButton superscript;
private ToggleButton strikethrough;
private PushButton indent;
private PushButton outdent;
private PushButton justifyLeft;
private PushButton justifyCenter;
private PushButton justifyRight;
private PushButton hr;
private PushButton ol;
private PushButton ul;
private PushButton insertImage;
private PushButton createLink;
private PushButton removeLink;
private PushButton removeFormat;

private ListBox backColors;
private ListBox foreColors;
private ListBox fonts;
private ListBox fontSizes;

/**
* Creates a new toolbar that drives the given rich text area.
*
* @param richText
* the rich text area to be controlled
*/
@SuppressWarnings("deprecation")
public VRichTextToolbar(RichTextArea richText) {
this.richText = richText;
basic = richText.getBasicFormatter();
extended = richText.getExtendedFormatter();

outer.add(topPanel);
outer.add(bottomPanel);
topPanel.setStyleName("gwt-RichTextToolbar-top");
bottomPanel.setStyleName("gwt-RichTextToolbar-bottom");

initWidget(outer);
setStyleName("gwt-RichTextToolbar");

if (basic != null) {
topPanel.add(
bold = createToggleButton(images.bold(), strings.bold()));
topPanel.add(italic = createToggleButton(images.italic(),
strings.italic()));
topPanel.add(underline = createToggleButton(images.underline(),
strings.underline()));
topPanel.add(subscript = createToggleButton(images.subscript(),
strings.subscript()));
topPanel.add(superscript = createToggleButton(images.superscript(),
strings.superscript()));
topPanel.add(justifyLeft = createPushButton(images.justifyLeft(),
strings.justifyLeft()));
topPanel.add(justifyCenter = createPushButton(
images.justifyCenter(), strings.justifyCenter()));
topPanel.add(justifyRight = createPushButton(images.justifyRight(),
strings.justifyRight()));
}

if (extended != null) {
topPanel.add(strikethrough = createToggleButton(
images.strikeThrough(), strings.strikeThrough()));
topPanel.add(indent = createPushButton(images.indent(),
strings.indent()));
topPanel.add(outdent = createPushButton(images.outdent(),
strings.outdent()));
topPanel.add(hr = createPushButton(images.hr(), strings.hr()));
topPanel.add(ol = createPushButton(images.ol(), strings.ol()));
topPanel.add(ul = createPushButton(images.ul(), strings.ul()));
topPanel.add(insertImage = createPushButton(images.insertImage(),
strings.insertImage()));
topPanel.add(createLink = createPushButton(images.createLink(),
strings.createLink()));
topPanel.add(removeLink = createPushButton(images.removeLink(),
strings.removeLink()));
topPanel.add(removeFormat = createPushButton(images.removeFormat(),
strings.removeFormat()));
}

if (basic != null) {
bottomPanel.add(backColors = createColorList("Background"));
bottomPanel.add(foreColors = createColorList("Foreground"));
bottomPanel.add(fonts = createFontList());
bottomPanel.add(fontSizes = createFontSizes());

// We only use these handlers for updating status, so don't hook
// them up unless at least basic editing is supported.
richText.addKeyUpHandler(handler);
richText.addClickHandler(handler);
}
}

private ListBox createColorList(String caption) {
final ListBox lb = new ListBox();
lb.addChangeHandler(handler);
lb.setVisibleItemCount(1);

lb.addItem(caption);
lb.addItem(strings.white(), "white");
lb.addItem(strings.black(), "black");
lb.addItem(strings.red(), "red");
lb.addItem(strings.green(), "green");
lb.addItem(strings.yellow(), "yellow");
lb.addItem(strings.blue(), "blue");
lb.setTabIndex(-1);
return lb;
}

private ListBox createFontList() {
final ListBox lb = new ListBox();
lb.addChangeHandler(handler);
lb.setVisibleItemCount(1);

lb.addItem(strings.font(), "");
lb.addItem(strings.normal(), "inherit");
lb.addItem("Times New Roman", "Times New Roman");
lb.addItem("Arial", "Arial");
lb.addItem("Courier New", "Courier New");
lb.addItem("Georgia", "Georgia");
lb.addItem("Trebuchet", "Trebuchet");
lb.addItem("Verdana", "Verdana");
lb.setTabIndex(-1);
return lb;
}

private ListBox createFontSizes() {
final ListBox lb = new ListBox();
lb.addChangeHandler(handler);
lb.setVisibleItemCount(1);

lb.addItem(strings.size());
lb.addItem(strings.xxsmall());
lb.addItem(strings.xsmall());
lb.addItem(strings.small());
lb.addItem(strings.medium());
lb.addItem(strings.large());
lb.addItem(strings.xlarge());
lb.addItem(strings.xxlarge());
lb.setTabIndex(-1);
return lb;
}

private PushButton createPushButton(ImageResource img, String tip) {
final PushButton pb = new PushButton(new Image(img));
pb.addClickHandler(handler);
pb.setTitle(tip);
pb.setTabIndex(-1);
return pb;
}

private ToggleButton createToggleButton(ImageResource img, String tip) {
final ToggleButton tb = new ToggleButton(new Image(img));
tb.addClickHandler(handler);
tb.setTitle(tip);
tb.setTabIndex(-1);
return tb;
}

/**
* Updates the status of all the stateful buttons.
*/
@SuppressWarnings("deprecation")
private void updateStatus() {
if (basic != null) {
bold.setDown(basic.isBold());
italic.setDown(basic.isItalic());
underline.setDown(basic.isUnderlined());
subscript.setDown(basic.isSubscript());
superscript.setDown(basic.isSuperscript());
}

if (extended != null) {
strikethrough.setDown(extended.isStrikethrough());
}
}
}

+ 2
- 4
client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java View File

@@ -25,7 +25,6 @@ import com.vaadin.client.ui.textfield.AbstractTextFieldConnector;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.textarea.TextAreaServerRpc;
import com.vaadin.shared.ui.textarea.TextAreaState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.ui.TextArea;

@Connect(TextArea.class)
@@ -46,10 +45,9 @@ public class TextAreaConnector extends AbstractTextFieldConnector {
super.init();
getWidget().addChangeHandler(event -> sendValueChange());
getWidget().addDomHandler(event -> {
if (getState().valueChangeMode != ValueChangeMode.BLUR) {
scheduleValueChange();
}
getValueChangeHandler().scheduleValueChange();
}, InputEvent.getType());

getWidget().addMouseUpHandler(new ResizeMouseUpHandler());
}


+ 19
- 52
client/src/main/java/com/vaadin/client/ui/textfield/AbstractTextFieldConnector.java View File

@@ -15,8 +15,6 @@
*/
package com.vaadin.client.ui.textfield;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.ui.AbstractComponentConnector;
@@ -25,14 +23,13 @@ import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.ui.AbstractTextField;

/**
* Connector class for AbstractTextField.
*/
public abstract class AbstractTextFieldConnector
extends AbstractComponentConnector {
extends AbstractComponentConnector implements ValueChangeHandler.Owner {

private class AbstractTextFieldClientRpcImpl
implements AbstractTextFieldClientRpc {
@@ -62,19 +59,18 @@ public abstract class AbstractTextFieldConnector
}

private int lastSentCursorPosition = -1;

private Timer valueChangeTrigger = new Timer() {
@Override
public void run() {
Scheduler.get().scheduleDeferred(() -> sendValueChange());
}
};
private ValueChangeHandler valueChangeHandler;

@Override
protected void init() {
registerRpc(AbstractTextFieldClientRpc.class,
new AbstractTextFieldClientRpcImpl());
ConnectorFocusAndBlurHandler.addHandlers(this);
valueChangeHandler = new ValueChangeHandler(this);
}

protected ValueChangeHandler getValueChangeHandler() {
return valueChangeHandler;
}

/**
@@ -88,50 +84,19 @@ public abstract class AbstractTextFieldConnector
return (AbstractTextFieldWidget) getWidget();
}

/**
* Called whenever a change in the value has been detected. Schedules a
* value change to be sent to the server, depending on the current value
* change mode.
* <p>
* Note that this method does not consider the {@link ValueChangeMode#BLUR}
* mode but assumes that {@link #sendValueChange()} is called directly for
* this mode.
*/
protected void scheduleValueChange() {
switch (getState().valueChangeMode) {
case LAZY:
lazyTextChange();
break;
case TIMEOUT:
timeoutTextChange();
break;
case EAGER:
eagerTextChange();
break;
case BLUR:
// Nothing to schedule for this mode
break;
}
}

private void lazyTextChange() {
valueChangeTrigger.schedule(getState().valueChangeTimeout);
}

private void timeoutTextChange() {
if (valueChangeTrigger.isRunning()) {
return;
}
valueChangeTrigger.schedule(getState().valueChangeTimeout);
@Override
public AbstractTextFieldState getState() {
return (AbstractTextFieldState) super.getState();
}

private void eagerTextChange() {
valueChangeTrigger.run();
@OnStateChange("valueChangeMode")
private void updateValueChangeMode() {
valueChangeHandler.setValueChangeMode(getState().valueChangeMode);
}

@Override
public AbstractTextFieldState getState() {
return (AbstractTextFieldState) super.getState();
@OnStateChange("valueChangeTimeout")
private void updateValueChangeTimeout() {
valueChangeHandler.setValueChangeTimeout(getState().valueChangeTimeout);
}

@OnStateChange("readOnly")
@@ -151,10 +116,12 @@ public abstract class AbstractTextFieldConnector
* Sends the updated value and cursor position to the server, if either one
* has changed.
*/
protected void sendValueChange() {
@Override
public void sendValueChange() {
if (!hasStateChanged()) {
return;
}

lastSentCursorPosition = getAbstractTextField().getCursorPos();
getRpcProxy(AbstractTextFieldServerRpc.class).setText(
getAbstractTextField().getValue(), lastSentCursorPosition);

+ 1
- 4
client/src/main/java/com/vaadin/client/ui/textfield/TextFieldConnector.java View File

@@ -20,7 +20,6 @@ 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.TextFieldState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.ui.TextField;

/**
@@ -34,9 +33,7 @@ public class TextFieldConnector extends AbstractTextFieldConnector {
super.init();
getWidget().addChangeHandler(event -> sendValueChange());
getWidget().addDomHandler(event -> {
if (getState().valueChangeMode != ValueChangeMode.BLUR) {
scheduleValueChange();
}
getValueChangeHandler().scheduleValueChange();
}, InputEvent.getType());
}


+ 129
- 0
client/src/main/java/com/vaadin/client/ui/textfield/ValueChangeHandler.java View File

@@ -0,0 +1,129 @@
/*
* 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.client.ui.textfield;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.Timer;
import com.vaadin.client.ComponentConnector;
import com.vaadin.shared.ui.ValueChangeMode;

/**
* Helper for dealing with scheduling value change events based on a given mode
* and possibly timeout.
*/
public class ValueChangeHandler {

/**
* Must be implemented by any user of a ValueChangeHandler.
*/
public interface Owner extends ComponentConnector {
/**
* Sends the current value to the server, if it has changed.
*/
void sendValueChange();
}

private Owner owner;

private Timer valueChangeTrigger = new Timer() {
@Override
public void run() {
Scheduler.get().scheduleDeferred(() -> owner.sendValueChange());
}
};

private int valueChangeTimeout = -1;

private ValueChangeMode valueChangeMode;

/**
* Creates a value change handler for the given owner.
*
* @param owner
* the owner connector
*/
public ValueChangeHandler(Owner owner) {
this.owner = owner;
}

/**
* Called whenever a change in the value has been detected. Schedules a
* value change to be sent to the server, depending on the current value
* change mode.
* <p>
* Note that this method does not consider the {@link ValueChangeMode#BLUR}
* mode but assumes that {@link #sendValueChange()} is called directly for
* this mode.
*/
public void scheduleValueChange() {
switch (valueChangeMode) {
case LAZY:
lazyTextChange();
break;
case TIMEOUT:
timeoutTextChange();
break;
case EAGER:
eagerTextChange();
break;
case BLUR:
// Nothing to schedule for this mode
break;
default:
throw new IllegalStateException("Unknown mode: " + valueChangeMode);
}
}

private void lazyTextChange() {
valueChangeTrigger.schedule(valueChangeTimeout);
}

private void timeoutTextChange() {
if (valueChangeTrigger.isRunning()) {
return;
}
valueChangeTrigger.schedule(valueChangeTimeout);
}

private void eagerTextChange() {
valueChangeTrigger.run();
}

/**
* Sets the value change mode to use.
*
* @see ValueChangeMode
*
* @param valueChangeMode
* the value change mode to use
*/
public void setValueChangeMode(ValueChangeMode valueChangeMode) {
this.valueChangeMode = valueChangeMode;
}

/**
* Sets the value change timeout to use.
*
* @see ValueChangeMode
*
* @param valueChangeTimeout
* the value change timeout
*/
public void setValueChangeTimeout(int valueChangeTimeout) {
this.valueChangeTimeout = valueChangeTimeout;
}

}

+ 35
- 0
client/src/main/resources/com/vaadin/client/ui/richtextarea/VRichTextToolbar$Strings.properties View File

@@ -0,0 +1,35 @@
bold = Toggle Bold
createLink = Create Link
hr = Insert Horizontal Rule
indent = Indent Right
insertImage = Insert Image
italic = Toggle Italic
justifyCenter = Center
justifyLeft = Left Justify
justifyRight = Right Justify
ol = Insert Ordered List
outdent = Indent Left
removeFormat = Remove Formatting
removeLink = Remove Link
strikeThrough = Toggle Strikethrough
subscript = Toggle Subscript
superscript = Toggle Superscript
ul = Insert Unordered List
underline = Toggle Underline
color = Color
black = Black
white = White
red = Red
green = Green
yellow = Yellow
blue = Blue
font = Font
normal = Normal
size = Size
xxsmall = XX-Small
xsmall = X-Small
small = Small
medium = Medium
large = Large
xlarge = X-Large
xxlarge = XX-Large

BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/backColors.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/bold.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/createLink.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/fontSizes.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/fonts.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/foreColors.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/gwtLogo.png View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/hr.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/indent.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/insertImage.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/italic.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyCenter.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyLeft.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/justifyRight.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/ol.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/outdent.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/removeFormat.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/removeLink.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/strikeThrough.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/subscript.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/superscript.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/ul.gif View File


BIN
client/src/main/resources/com/vaadin/client/ui/richtextarea/underline.gif View File


+ 10
- 5
server/src/main/java/com/vaadin/ui/AbstractField.java View File

@@ -142,21 +142,26 @@ public abstract class AbstractField<T> extends AbstractComponent
*
* @param value
* the new value to set
* @return {@code true} if this event originates from the client,
* {@code false} otherwise.
* @param userOriginated
* {@code true} if this event originates from the client,
* {@code false} otherwise.
* @return <code>true</code> if the value was updated, <code>false</code>
* otherwise
*/
protected void setValue(T value, boolean userOriginated) {
protected boolean setValue(T value, boolean userOriginated) {
if (userOriginated && isReadOnly()) {
return;
return false;
}
if (Objects.equals(value, getValue())) {
return;
return false;
}
doSetValue(value);
if (!userOriginated) {
markAsDirty();
}
fireEvent(createValueChange(userOriginated));

return true;
}

/**

+ 10
- 39
server/src/main/java/com/vaadin/ui/AbstractTextField.java View File

@@ -27,10 +27,10 @@ import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.Registration;
import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldState;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;

@@ -40,7 +40,8 @@ import com.vaadin.ui.declarative.DesignContext;
* @author Vaadin Ltd.
* @since 8.0
*/
public abstract class AbstractTextField extends AbstractField<String> {
public abstract class AbstractTextField extends AbstractField<String>
implements HasValueChangeMode {

private final class AbstractTextFieldServerRpcImpl
implements AbstractTextFieldServerRpc {
@@ -173,6 +174,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
/**
* Returns the last known cursor position of the field.
*
* @return the last known cursor position
*/
public int getCursorPosition() {
return lastKnownCursorPosition;
@@ -212,41 +214,17 @@ public abstract class AbstractTextField extends AbstractField<String> {
listener);
}

/**
* Sets the mode how the TextField triggers {@link ValueChange}s.
*
* @param mode
* the new mode
*
* @see ValueChangeMode
*/
@Override
public void setValueChangeMode(ValueChangeMode mode) {
getState().valueChangeMode = mode;
}

/**
* Returns the currently set {@link ValueChangeMode}.
*
* @return the mode used to trigger {@link ValueChange}s.
*
* @see ValueChangeMode
*/
@Override
public ValueChangeMode getValueChangeMode() {
return getState(false).valueChangeMode;
}

/**
* Sets how often {@link ValueChange}s are triggered when the
* {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or
* {@link ValueChangeMode#TIMEOUT}.
*
* @param timeout
* timeout in milliseconds, must be greater or equal to 0
* @throws IllegalArgumentException
* if given timeout is smaller than 0
*
* @see ValueChangeMode
*/
@Override
public void setValueChangeTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException(
@@ -255,15 +233,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
getState().valueChangeTimeout = timeout;
}

/**
* Returns the currently set timeout, in milliseconds, for how often
* {@link ValueChange}s are triggered if the current {@link ValueChangeMode}
* is set to either {@link ValueChangeMode#LAZY} or
* {@link ValueChangeMode#TIMEOUT}.
*
* @return the timeout in milliseconds of how often {@link ValueChange}s are
* triggered.
*/
@Override
public int getValueChangeTimeout() {
return getState(false).valueChangeTimeout;
}
@@ -303,7 +273,8 @@ public abstract class AbstractTextField extends AbstractField<String> {
/**
* Checks if the field is empty.
*
* @return true if the field value is an empty string, false otherwise
* @return <code>true</code> if the field value is an empty string,
* <code>false</code> otherwise
*/
public boolean isEmpty() {
return "".equals(getValue());

+ 69
- 0
server/src/main/java/com/vaadin/ui/HasValueChangeMode.java View File

@@ -0,0 +1,69 @@
/*
* 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.ui;

import com.vaadin.data.HasValue.ValueChange;
import com.vaadin.shared.ui.ValueChangeMode;

/**
* Implemented by components which support value change modes.
*/
public interface HasValueChangeMode extends Component {
/**
* Sets the mode how the TextField triggers {@link ValueChange}s.
*
* @param valueChangeMode
* the new mode
*
* @see ValueChangeMode
*/
public void setValueChangeMode(ValueChangeMode valueChangeMode);

/**
* Returns the currently set {@link ValueChangeMode}.
*
* @return the mode used to trigger {@link ValueChange}s.
*
* @see ValueChangeMode
*/
public ValueChangeMode getValueChangeMode();

/**
* Sets how often {@link ValueChange}s are triggered when the
* {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or
* {@link ValueChangeMode#TIMEOUT}.
*
* @param valueChangeTimeout
* timeout in milliseconds, must be greater or equal to 0
* @throws IllegalArgumentException
* if given timeout is smaller than 0
*
* @see ValueChangeMode
*/
public void setValueChangeTimeout(int valueChangeTimeout);

/**
* Returns the currently set timeout, in milliseconds, for how often
* {@link ValueChange}s are triggered if the current {@link ValueChangeMode}
* is set to either {@link ValueChangeMode#LAZY} or
* {@link ValueChangeMode#TIMEOUT}.
*
* @return the timeout in milliseconds of how often {@link ValueChange}s are
* triggered.
*/
public int getValueChangeTimeout();

}

+ 0
- 12
server/src/main/java/com/vaadin/ui/PasswordField.java View File

@@ -59,12 +59,6 @@ public class PasswordField extends TextField {
setCaption(caption);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element ,
* com.vaadin.ui.declarative.DesignContext)
*/
@Override
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
@@ -75,12 +69,6 @@ public class PasswordField extends TextField {
}
}

/*
* (non-Javadoc)
*
* @see com.vaadin.ui.AbstractTextField#writeDesign(org.jsoup.nodes.Element
* , com.vaadin.ui.declarative.DesignContext)
*/
@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);

+ 170
- 0
server/src/main/java/com/vaadin/ui/RichTextArea.java View File

@@ -0,0 +1,170 @@
/*
* 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.ui;

import org.jsoup.nodes.Element;

import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.shared.ui.richtextarea.RichTextAreaClientRpc;
import com.vaadin.shared.ui.richtextarea.RichTextAreaServerRpc;
import com.vaadin.shared.ui.richtextarea.RichTextAreaState;
import com.vaadin.ui.declarative.DesignContext;

/**
* A simple RichTextArea to edit HTML format text.
*/
public class RichTextArea extends AbstractField<String>
implements HasValueChangeMode {

private class RichTextAreaServerRpcImpl implements RichTextAreaServerRpc {
@Override
public void setText(String text) {
getUI().getConnectorTracker().getDiffState(RichTextArea.this)
.put("value", text);
if (!setValue(text, true)) {
// The value was not updated, this could happen if the field has
// been set to readonly on the server and the client does not
// know about it yet. Must re-send the correct state back.
markAsDirty();
}
}
}

/**
* Constructs an empty <code>RichTextArea</code> with no caption.
*/
public RichTextArea() {
super();
registerRpc(new RichTextAreaServerRpcImpl());
setValue("");
}

/**
* Constructs an empty <code>RichTextArea</code> with the given caption.
*
* @param caption
* the caption for the editor.
*/
public RichTextArea(String caption) {
this();
setCaption(caption);
}

/**
* Constructs a new <code>RichTextArea</code> with the given caption and
* initial text contents.
*
* @param caption
* the caption for the editor.
* @param value
* the initial text content of the editor.
*/
public RichTextArea(String caption, String value) {
this(caption);
setValue(value);
}

@Override
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
setValue(design.html());
}

@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);
design.html(getValue());
}

@Override
protected RichTextAreaState getState() {
return (RichTextAreaState) super.getState();
}

@Override
protected RichTextAreaState getState(boolean markAsDirty) {
return (RichTextAreaState) super.getState(markAsDirty);
}

@Override
public void setValue(String value) {
if (value == null) {
setValue("", false);
} else {
setValue(value, false);
}
}

@Override
public String getValue() {
return getState(false).value;
}

@Override
protected void doSetValue(String value) {
getState().value = value;
}

/**
* Selects all text in the rich text area. As a side effect, focuses the
* rich text area.
*
* @since 6.5
*/
public void selectAll() {
getRpcProxy(RichTextAreaClientRpc.class).selectAll();
focus();
}

@Override
public void setValueChangeMode(ValueChangeMode mode) {
getState().valueChangeMode = mode;
}

@Override
public ValueChangeMode getValueChangeMode() {
return getState(false).valueChangeMode;
}

@Override
public void setValueChangeTimeout(int timeout) {
getState().valueChangeTimeout = timeout;

}

@Override
public int getValueChangeTimeout() {
return getState(false).valueChangeTimeout;
}

/**
* Checks if the field is empty.
*
* @return <code>true</code> if the field value is an empty string,
* <code>false</code> otherwise
*/
public boolean isEmpty() {
return getValue().length() == 0;
}

/**
* Clears the value of this field.
*/
public void clear() {
setValue("");
}
}

+ 1
- 1
server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java View File

@@ -17,7 +17,7 @@ package com.vaadin.tests.server.component.abstracttextfield;

import org.junit.Test;

import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractTextField;
import com.vaadin.ui.TextField;

+ 98
- 0
server/src/test/java/com/vaadin/ui/ComponentTest.java View File

@@ -0,0 +1,98 @@
/*
* 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.ui;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.vaadin.server.ClientConnector;
import com.vaadin.server.ServerRpcMethodInvocation;
import com.vaadin.shared.communication.ServerRpc;

/**
* Base class for component unit tests, providing helper methods for e.g.
* invoking RPC and updating diff state.
*/
public class ComponentTest {

/**
* Perform operations on the component similar to what would be done when
* the component state is communicated to the client, e.g. update diff state
* and mark as clean.
*
* @param component
* the component to update
*/
public static void syncToClient(AbstractComponent component) {
updateDiffState(component);
component.getUI().getConnectorTracker().markClean(component);
}

/**
* Checks if the connector has been marked dirty.
*
* @param connector
* the connector to check
* @return <code>true</code> if the connector has been marked dirty,
* <code>false</code> otherwise
*/
public static boolean isDirty(ClientConnector connector) {
return connector.getUI().getConnectorTracker().isDirty(connector);
}

/**
* Updates the stored diff state from the current component state.
*
* @param rta
* the component to update
*/
public static void updateDiffState(AbstractComponent component) {
component.getUI().getSession().getCommunicationManager()
.encodeState(component, component.getState());

}

/**
* Gets a proxy object which invokes ServerRpc methods.
*
* @param component
* the component which listens to the RPC
* @param serverRpcClass
* the server RPC class
* @return a proxy which can be used to invoke RPC methods
*/
@SuppressWarnings("unchecked")
public static <T extends ServerRpc> T getRpcProxy(Component component,
Class<T> serverRpcClass) {
return (T) Proxy.newProxyInstance(component.getClass().getClassLoader(),
new Class[] { serverRpcClass }, new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
component.getConnectorId(), serverRpcClass,
method.getName(), args.length);
invocation.setParameters(args);
component.getRpcManager(serverRpcClass.getName())
.applyInvocation(invocation);
return null;
}
});
}

}

+ 107
- 0
server/src/test/java/com/vaadin/ui/RichTextAreaTest.java View File

@@ -0,0 +1,107 @@
/*
* 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.ui;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.server.ClientConnector;
import com.vaadin.server.ServerRpcManager.RpcInvocationException;
import com.vaadin.shared.ui.richtextarea.RichTextAreaServerRpc;
import com.vaadin.tests.util.MockUI;

public class RichTextAreaTest extends ComponentTest {

@Test
public void initiallyEmpty() {
RichTextArea tf = new RichTextArea();
Assert.assertTrue(tf.isEmpty());
}

@Test
public void setValueServerWhenReadOnly() {
RichTextArea tf = new RichTextArea();
tf.setReadOnly(true);
tf.setValue("foo");
Assert.assertEquals("foo", tf.getValue());
}

@Test
public void diffStateAfterClientSetValueWhenReadOnly() {
UI ui = new MockUI();

// If the client has a non-readonly text field which is set to read-only
// on the server, then any update from the client must cause both the
// readonly state and the old value to be sent
RichTextArea rta = new RichTextArea();
ui.setContent(rta);
rta.setValue("bar");
rta.setReadOnly(true);
syncToClient(rta);

// Client thinks the field says "foo" but it won't be updated because
// the field is readonly
getRpcProxy(rta, RichTextAreaServerRpc.class).setText("foo");

// The real value will be sent back as long as the field is marked as
// dirty and diffstate contains what the client has
Assert.assertEquals("foo", getDiffStateString(rta, "value"));
Assert.assertTrue("Component should be marked dirty", isDirty(rta));
}

@Test
public void setValueClientNotSentBack() throws RpcInvocationException {
UI ui = new MockUI();
RichTextArea rta = new RichTextArea();
ui.setContent(rta);
rta.setValue("bar");

updateDiffState(rta);
getRpcProxy(rta, RichTextAreaServerRpc.class).setText("foo");
Assert.assertEquals("foo", getDiffStateString(rta, "value"));
}

private String getDiffStateString(ClientConnector connector, String key) {
return connector.getUI().getConnectorTracker().getDiffState(connector)
.get(key).asString();
}

@Test
public void setValueClientRefusedWhenReadOnly() {
RichTextArea tf = new RichTextArea();
tf.setValue("bar");
tf.setReadOnly(true);
tf.setValue("foo", true);
Assert.assertEquals("bar", tf.getValue());
}

@Test
public void setValueNullBecomesEmptyString() {
RichTextArea tf = new RichTextArea();
tf.setValue(null);
Assert.assertEquals("", tf.getValue());
}

@Test
public void emptyAfterClear() {
RichTextArea tf = new RichTextArea();
tf.setValue("foobar");
Assert.assertFalse(tf.isEmpty());
tf.clear();
Assert.assertTrue(tf.isEmpty());
}

}

shared/src/main/java/com/vaadin/shared/ui/textfield/ValueChangeMode.java → shared/src/main/java/com/vaadin/shared/ui/ValueChangeMode.java View File

@@ -14,7 +14,7 @@
* the License.
*/

package com.vaadin.shared.ui.textfield;
package com.vaadin.shared.ui;

/**
* Different modes for when and how often field value changes are transmitted

+ 29
- 0
shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaClientRpc.java View File

@@ -0,0 +1,29 @@
/*
* 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.richtextarea;

import com.vaadin.shared.communication.ClientRpc;

/**
* Server to client RPC interface for RichTextArea.
*/
public interface RichTextAreaClientRpc extends ClientRpc {

/**
* Selects everything in the field.
*/
void selectAll();
}

+ 33
- 0
shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaServerRpc.java View File

@@ -0,0 +1,33 @@
/*
* 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.richtextarea;

import com.vaadin.shared.communication.ServerRpc;

/**
* Client to server RPC interface for RichTextArea.
*
*/
public interface RichTextAreaServerRpc extends ServerRpc {

/**
* Sends the updated text to the server.
*
* @param text
* the text in the field
*/
void setText(String text);
}

+ 52
- 0
shared/src/main/java/com/vaadin/shared/ui/richtextarea/RichTextAreaState.java View File

@@ -0,0 +1,52 @@
/*
* 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.richtextarea;

import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.annotations.DelegateToWidget;
import com.vaadin.shared.annotations.NoLayout;
import com.vaadin.shared.ui.ValueChangeMode;

/**
* State for RichTextArea.
*
*/
public class RichTextAreaState extends AbstractFieldState {
{
primaryStyleName = "v-richtextarea";
}

/**
* Maximum character count in text field.
*/
@DelegateToWidget
@NoLayout
public int maxLength = -1;

/**
* The text in the field.
*/
@DelegateToWidget
@NoLayout
public String value = "";

@NoLayout
public ValueChangeMode valueChangeMode = ValueChangeMode.LAZY;

@NoLayout
public int valueChangeTimeout = 400;

}

+ 1
- 0
shared/src/main/java/com/vaadin/shared/ui/textfield/AbstractTextFieldState.java View File

@@ -18,6 +18,7 @@ package com.vaadin.shared.ui.textfield;
import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.annotations.DelegateToWidget;
import com.vaadin.shared.annotations.NoLayout;
import com.vaadin.shared.ui.ValueChangeMode;

/**
* State class for AbstractTextField.

+ 1
- 1
themes/src/main/themes/VAADIN/themes/base/textfield/textfield.scss View File

@@ -113,7 +113,7 @@ textarea.v-textarea-readonly:focus {
margin-right: 2px;
}

.v-richtextarea-readonly {
.v-richtextarea.v-readonly {
border: none;
}


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/TestCaptionWrapper.java View File

@@ -41,7 +41,7 @@ import com.vaadin.ui.Window;
import com.vaadin.v7.ui.NativeSelect;
import com.vaadin.v7.ui.OptionGroup;
import com.vaadin.v7.ui.ProgressIndicator;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.Select;
import com.vaadin.v7.ui.Table;
import com.vaadin.v7.ui.TextField;

+ 0
- 75
uitest/src/main/java/com/vaadin/tests/TestForRichTextEditor.java View File

@@ -1,75 +0,0 @@
/*
* 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.tests;

import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.data.Property.ValueChangeEvent;
import com.vaadin.v7.data.Property.ValueChangeListener;
import com.vaadin.v7.ui.RichTextArea;

/**
*
* @author Vaadin Ltd.
*/
public class TestForRichTextEditor extends CustomComponent
implements ValueChangeListener {

private final VerticalLayout main = new VerticalLayout();

private Label l;

private RichTextArea rte;

public TestForRichTextEditor() {

setCompositionRoot(main);
createNewView();
}

public void createNewView() {
main.removeAllComponents();
main.addComponent(new Label(
"RTE uses google richtextArea and their examples toolbar."));

rte = new RichTextArea();
rte.addListener(this);

main.addComponent(rte);

main.addComponent(new Button("commit content to label below"));

l = new Label("", ContentMode.HTML);
main.addComponent(l);

CheckBox b = new CheckBox("enabled");
b.setImmediate(true);
b.addValueChangeListener(event -> rte.setEnabled(!rte.isEnabled()));
main.addComponent(b);

}

@Override
public void valueChange(ValueChangeEvent event) {
l.setValue(rte.getValue());
}

}

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/AbstractComponentContainerTest.java View File

@@ -17,7 +17,7 @@ import com.vaadin.ui.NativeButton;
import com.vaadin.ui.PopupDateField;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.VerticalSplitPanel;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.Table;
import com.vaadin.v7.ui.TextArea;
import com.vaadin.v7.ui.TextField;

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/notification/NotificationsWaiAria.java View File

@@ -2,7 +2,7 @@ package com.vaadin.tests.components.notification;

import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.shared.ui.ui.NotificationRole;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/popupview/PopupViewWithRTE.java View File

@@ -5,7 +5,7 @@ import com.vaadin.ui.Component;
import com.vaadin.ui.PopupView;
import com.vaadin.ui.PopupView.Content;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class PopupViewWithRTE extends TestBase {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java View File

@@ -6,7 +6,7 @@ import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Label;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreaEmptyString extends TestBase {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaRelativeHeightResize.java View File

@@ -20,7 +20,7 @@ import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreaRelativeHeightResize extends AbstractTestUI {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaScrolling.java View File

@@ -3,7 +3,7 @@ package com.vaadin.tests.components.richtextarea;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreaScrolling extends TestBase {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaSize.java View File

@@ -3,7 +3,7 @@ package com.vaadin.tests.components.richtextarea;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreaSize extends TestBase {


+ 6
- 41
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaTest.java View File

@@ -1,56 +1,21 @@
package com.vaadin.tests.components.richtextarea;

import java.util.LinkedHashMap;
import com.vaadin.tests.components.abstractfield.AbstractFieldTest;
import com.vaadin.ui.RichTextArea;

import com.vaadin.tests.components.abstractfield.LegacyAbstractFieldTest;
import com.vaadin.v7.ui.RichTextArea;

public class RichTextAreaTest extends LegacyAbstractFieldTest<RichTextArea> {
public class RichTextAreaTest extends AbstractFieldTest<RichTextArea, String> {

@Override
protected Class<RichTextArea> getTestClass() {
return RichTextArea.class;
}

private Command<RichTextArea, Boolean> nullSelectionAllowedCommand = new Command<RichTextArea, Boolean>() {

@Override
public void execute(RichTextArea c, Boolean value, Object data) {
c.setNullSettingAllowed(value);

}
};
private Command<RichTextArea, String> nullRepresentationCommand = new Command<RichTextArea, String>() {

@Override
public void execute(RichTextArea c, String value, Object data) {
c.setNullRepresentation(value);
}
};

@Override
protected void createActions() {
super.createActions();

createSetTextValueAction(CATEGORY_ACTIONS);

createNullSettingAllowedAction(CATEGORY_FEATURES);
createNullRepresentationAction(CATEGORY_FEATURES);
}

private void createNullSettingAllowedAction(String category) {
createBooleanAction("Null selection allowed", category, true,
nullSelectionAllowedCommand);
}

private void createNullRepresentationAction(String category) {
LinkedHashMap<String, String> options = new LinkedHashMap<>();
options.put("-", null);
options.put("null", "null");
options.put("This is empty", "This is empty");
options.put("- Nothing -", "- Nothing -");
createSelectAction("Null representation", category, options, "null",
nullRepresentationCommand);
createClickAction("Select all", CATEGORY_FEATURES, (rta, a, b) -> {
rta.selectAll();
}, null);
}

}

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaUpdateWhileTyping.java View File

@@ -4,7 +4,7 @@ import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.v7.shared.ui.progressindicator.ProgressIndicatorServerRpc;
import com.vaadin.v7.ui.ProgressIndicator;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreaUpdateWhileTyping extends AbstractTestUI {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreaWithKeyboardShortcuts.java View File

@@ -11,7 +11,7 @@ import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.v7.ui.AbstractField;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

@SuppressWarnings("serial")
public class RichTextAreaWithKeyboardShortcuts extends TestBase {

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/richtextarea/RichTextAreas.java View File

@@ -1,7 +1,7 @@
package com.vaadin.tests.components.richtextarea;

import com.vaadin.tests.components.ComponentTestCase;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class RichTextAreas extends ComponentTestCase<RichTextArea> {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/splitpanel/GridLayoutWithCheckbox.java View File

@@ -2,7 +2,7 @@ package com.vaadin.tests.components.splitpanel;

import com.vaadin.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/splitpanel/SplitPanelWithRichTextArea.java View File

@@ -3,7 +3,7 @@ package com.vaadin.tests.components.splitpanel;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalSplitPanel;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class SplitPanelWithRichTextArea extends TestBase {


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/textfield/MultipleTextChangeEvents.java View File

@@ -2,8 +2,8 @@ package com.vaadin.tests.components.textfield;

import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.event.ShortcutAction;
import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.tests.components.TestBase;
import com.vaadin.tests.util.Log;
import com.vaadin.ui.TextField;

+ 152
- 0
uitest/src/main/java/com/vaadin/tests/components/textfield/TextFieldsValueChangeMode.java View File

@@ -0,0 +1,152 @@
package com.vaadin.tests.components.textfield;

import com.vaadin.data.HasValue.ValueChange;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.util.TestUtils;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.AbstractTextField;
import com.vaadin.ui.Component;
import com.vaadin.ui.HasValueChangeMode;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.RichTextArea;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

public class TextFieldsValueChangeMode extends AbstractTestUIWithLog {

@Override
protected void setup(VaadinRequest request) {
log.setNumberLogRows(false);
HorizontalLayout hl = new HorizontalLayout();
hl.addComponent(createFields(TextField.class));
hl.addComponent(createFields(TextArea.class));
hl.addComponent(createFields(RichTextArea.class));
addComponent(hl);
}

private Component createFields(Class<?> fieldClass) {
VerticalLayout vl = new VerticalLayout();
String id = fieldClass.getSimpleName().toLowerCase();
try {
AbstractField<String> f = (AbstractField<String>) fieldClass
.newInstance();
f.setId(id + "-default");
f.setCaption(f.getId());
f.addValueChangeListener(this::logValueChange);

vl.addComponent(f);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}

try {
AbstractField<String> eager = (AbstractField<String>) fieldClass
.newInstance();
eager.setId(id + "-eager");
eager.setCaption(eager.getId());

eager.addValueChangeListener(this::logValueChange);
((HasValueChangeMode) eager)
.setValueChangeMode(ValueChangeMode.EAGER);
vl.addComponent(eager);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
try {
AbstractField<String> timeout = (AbstractField<String>) fieldClass
.newInstance();
timeout.setId(id + "-timeout");
timeout.setCaption(timeout.getId());
timeout.addValueChangeListener(this::logValueChange);
((HasValueChangeMode) timeout)
.setValueChangeMode(ValueChangeMode.TIMEOUT);
((HasValueChangeMode) timeout).setValueChangeTimeout(1000);
vl.addComponent(timeout);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}

return vl;
}

private void logValueChange(ValueChange<String> listener) {
AbstractField<String> field = (AbstractField<String>) listener
.getConnector();
String msg = "Value change event for " + field.getCaption()
+ ", new value: '" + listener.getValue() + "'";
if (field instanceof AbstractTextField) {
msg += " Cursor at index:"
+ ((AbstractTextField) field).getCursorPosition();
}
log(msg);

}

@Override
protected String getTestDescription() {
return "Simple TextChangeEvent test cases.";
}

/**
* "Autosuggest"
*
* Known issue is timing if suggestion comes while typing more content. IMO
* we will not support this kind of features in default TextField, but
* hopefully make it easily extendable to perfect suggest feature. MT
* 2010-10
*
*/
private class VaadinDeveloperNameField extends TextField {
private String[] names = new String[] { "Matti Tahvonen",
"Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita",
"Marko Grönroos", "Artur Signell" };

public VaadinDeveloperNameField() {
setCaption("Start typing 'old' Vaadin developers.");
addValueChangeListener(listener -> {
boolean atTheEndOfText = listener.getValue()
.length() == getCursorPosition();
String match = findMatch(listener.getValue());
if (match != null) {
setStyleName("match");
String curText = listener.getValue();
int matchlenght = curText.length();
// autocomplete if caret is at the end of the text
if (atTheEndOfText) {
suggest(match, matchlenght);
}
} else {
setStyleName("nomatch");
}
});
setStyleName("nomatch");
}

@Override
public void attach() {
super.attach();
TestUtils.injectCSS(getUI(), ".match { background:green ;} "
+ ".nomatch {background:red;}");
}

private void suggest(String match, int matchlenght) {
setValue(match);
setSelection(matchlenght, match.length() - matchlenght);
}

private String findMatch(String currentTextContent) {
if (currentTextContent.length() > 0) {
for (int i = 0; i < names.length; i++) {
if (names[i].startsWith(currentTextContent)) {
return names[i];
}
}
}
return null;
}
}

}

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/tree/TreeScrolling.java View File

@@ -3,7 +3,7 @@ package com.vaadin.tests.components.tree;
import com.vaadin.tests.components.AbstractTestCase;
import com.vaadin.ui.LegacyWindow;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.TextField;
import com.vaadin.v7.ui.Tree;


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/uitest/components/TextFieldsCssTest.java View File

@@ -7,7 +7,7 @@ import com.vaadin.ui.PasswordField;
import com.vaadin.ui.TextField;
import com.vaadin.ui.themes.ChameleonTheme;
import com.vaadin.ui.themes.Reindeer;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.TextArea;

public class TextFieldsCssTest extends GridLayout {

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/window/WindowCloseShortcuts.java View File

@@ -39,7 +39,7 @@ import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.declarative.Design;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

@Theme("valo")
@SuppressWarnings("serial")

+ 1
- 2
uitest/src/main/java/com/vaadin/tests/fields/TabIndexes.java View File

@@ -14,13 +14,13 @@ import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.InlineDateField;
import com.vaadin.ui.PopupDateField;
import com.vaadin.ui.RichTextArea;
import com.vaadin.ui.Slider;
import com.vaadin.v7.ui.ComboBox;
import com.vaadin.v7.ui.ListSelect;
import com.vaadin.v7.ui.NativeSelect;
import com.vaadin.v7.ui.OptionGroup;
import com.vaadin.v7.ui.PasswordField;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.v7.ui.Table;
import com.vaadin.v7.ui.TextArea;
import com.vaadin.v7.ui.TextField;
@@ -28,7 +28,6 @@ import com.vaadin.v7.ui.Tree;
import com.vaadin.v7.ui.TreeTable;
import com.vaadin.v7.ui.TwinColSelect;

@SuppressWarnings("rawtypes")
public class TabIndexes extends AbstractTestUIWithLog {

private List<Focusable> fields;

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/navigator/NavigatorTest.java View File

@@ -16,7 +16,7 @@ import com.vaadin.ui.Layout;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.Table;

public class NavigatorTest extends UI {

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/themes/valo/Forms.java View File

@@ -34,7 +34,7 @@ import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
import com.vaadin.v7.ui.ComboBox;
import com.vaadin.v7.ui.OptionGroup;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;
import com.vaadin.v7.ui.TextArea;
import com.vaadin.v7.ui.TextField;


+ 1
- 1
uitest/src/main/java/com/vaadin/tests/themes/valo/TextFields.java View File

@@ -28,7 +28,7 @@ import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
import com.vaadin.v7.ui.RichTextArea;
import com.vaadin.ui.RichTextArea;

public class TextFields extends VerticalLayout implements View {
private TestIcon testIcon = new TestIcon(140);

+ 1
- 1
uitest/src/main/java/com/vaadin/v7/tests/components/textarea/TextAreaCursorPosition.java View File

@@ -1,6 +1,6 @@
package com.vaadin.v7.tests.components.textarea;

import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.AbstractTextField;

+ 0
- 146
uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java View File

@@ -1,146 +0,0 @@
package com.vaadin.v7.tests.components.textfield;

import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.tests.components.TestBase;
import com.vaadin.tests.util.Log;
import com.vaadin.tests.util.TestUtils;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
import com.vaadin.v7.event.FieldEvents.TextChangeListener;

public class TextChangeEvents extends TestBase {
Log l = new Log(10);

@Override
protected void setup() {

TextField tf = new TextField("Default");

TextChangeListener inputEventListener = new TextChangeListener() {

@Override
public void textChange(TextChangeEvent event) {
l.log("Text change event for "
+ event.getComponent().getCaption()
+ ", text content currently:'" + event.getText()
+ "' Cursor at index:" + event.getCursorPosition());
}
};

tf.addValueChangeListener(listener -> {
l.log("Text change event for " + tf.getCaption()
+ ", text content currently:'" + listener.getValue()
+ "' Cursor at index:" + tf.getCursorPosition());
});

getLayout().addComponent(tf);

TextField eager = new TextField("Eager");
eager.addValueChangeListener(listener -> {
l.log("Text change event for " + eager.getCaption()
+ ", text content currently:'" + listener.getValue()
+ "' Cursor at index:" + eager.getCursorPosition());
});
eager.setValueChangeMode(ValueChangeMode.EAGER);
getLayout().addComponent(eager);

TextField to = new TextField("Timeout 3s");
to.addValueChangeListener(listener -> {
l.log("Text change event for " + to.getCaption()
+ ", text content currently:'" + listener.getValue()
+ "' Cursor at index:" + to.getCursorPosition());
});
to.setValueChangeMode(ValueChangeMode.TIMEOUT);
to.setValueChangeTimeout(3000);
getLayout().addComponent(to);

TextArea ta = new TextArea("Default text area");
ta.addValueChangeListener(listener -> {
l.log("Text change event for " + ta.getCaption()
+ ", text content currently:'" + listener.getValue()
+ "' Cursor at index:" + ta.getCursorPosition());
});
getLayout().addComponent(ta);

VaadinDeveloperNameField vd = new VaadinDeveloperNameField();
vd.addValueChangeListener(listener -> {
l.log("Text change event for " + vd.getCaption()
+ ", text content currently:'" + listener.getValue()
+ "' Cursor at index:" + vd.getCursorPosition());
});
getLayout().addComponent(vd);

getLayout().addComponent(l);
}

@Override
protected String getDescription() {
return "Simple TextChangeEvent test cases.";
}

@Override
protected Integer getTicketNumber() {
return null;
}

/**
* "Autosuggest"
*
* Known issue is timing if suggestion comes while typing more content. IMO
* we will not support this kind of features in default TextField, but
* hopefully make it easily extendable to perfect suggest feature. MT
* 2010-10
*
*/
private class VaadinDeveloperNameField extends TextField {
private String[] names = new String[] { "Matti Tahvonen",
"Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita",
"Marko Grönroos", "Artur Signell" };

public VaadinDeveloperNameField() {
setCaption("Start typing 'old' Vaadin developers.");
addValueChangeListener(listener -> {
boolean atTheEndOfText = listener.getValue()
.length() == getCursorPosition();
String match = findMatch(listener.getValue());
if (match != null) {
setStyleName("match");
String curText = listener.getValue();
int matchlenght = curText.length();
// autocomplete if caret is at the end of the text
if (atTheEndOfText) {
suggest(match, matchlenght);
}
} else {
setStyleName("nomatch");
}
});
setStyleName("nomatch");
}

@Override
public void attach() {
super.attach();
TestUtils.injectCSS(getUI(), ".match { background:green ;} "
+ ".nomatch {background:red;}");
}

private void suggest(String match, int matchlenght) {
setValue(match);
setSelection(matchlenght, match.length() - matchlenght);
}

private String findMatch(String currentTextContent) {
if (currentTextContent.length() > 0) {
for (int i = 0; i < names.length; i++) {
if (names[i].startsWith(currentTextContent)) {
return names[i];
}
}
}
return null;
}
}

}

+ 124
- 0
uitest/src/test/java/com/vaadin/tests/components/textfield/TextFieldsValueChangeModeTest.java View File

@@ -0,0 +1,124 @@
/*
* 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.tests.components.textfield;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import com.vaadin.tests.tb3.MultiBrowserTest;

public class TextFieldsValueChangeModeTest extends MultiBrowserTest {

@Test
public void textFieldEager() {
testEager("textfield-eager");
}

@Test
public void textAreaEager() {
testEager("textarea-eager");
}

@Test
@Ignore("No support for typing in a RichTextArea in TestBench")
public void richTextAreaEager() {
testEager("richtextarea-eager");
}

@Test
public void textFieldDefault() {
testDefault("textfield-default");
}

@Test
public void textAreaDefault() {
testDefault("textarea-default");
}

@Test
@Ignore("No support for typing in a RichTextArea in TestBench")
public void richTextAreaDefault() {
testEager("richtextarea-default");
}

@Test
public void textFieldTimeout() {
testTimeout("textfield-timeout");
}

@Test
public void textAreaTimeout() {
testTimeout("textarea-timeout");
}

@Test
@Ignore("No support for typing in a RichTextArea in TestBench")
public void richTextAreaTimeout() {
testEager("richtextarea-timeout");
}

private void testEager(String id) {
openTestURL();
WebElement eagerTextField = findElement(By.id(id));
eagerTextField.sendKeys("f");
eagerTextField.sendKeys("o");
eagerTextField.sendKeys("o");
assertLog(id, "f", "fo", "foo");
}

private void testDefault(String id) {
openTestURL();
WebElement eagerTextField = findElement(By.id(id));
eagerTextField.sendKeys("f");
eagerTextField.sendKeys("o");
eagerTextField.sendKeys("o");
sleep(400); // Default timeout is 400ms
assertLog(id, "foo");

}

private void testTimeout(String id) {
openTestURL();
WebElement eagerTextField = findElement(By.id(id));
eagerTextField.sendKeys("f");
eagerTextField.sendKeys("o");
eagerTextField.sendKeys("o");
sleep(1000); // Timer set to 1000ms
eagerTextField.sendKeys("b");
eagerTextField.sendKeys("a");
eagerTextField.sendKeys("a");
sleep(1000); // Timer set to 1000ms
assertLog(id, "foo", "foobaa");
}

private void assertLog(String id, String... messages) {
for (int i = 0; i < messages.length; i++) {
String expected = "Value change event for " + id + ", new value: '"
+ messages[i] + "'";

String log = getLogRow(messages.length - 1 - i);
int tail = log.indexOf(" Cursor at");
if (tail != -1) {
log = log.substring(0, tail);
}
Assert.assertEquals(expected, log);
}

}
}

Loading…
Cancel
Save