<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>
--- /dev/null
+/*
+ * 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> </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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+ }
+}
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)
super.init();
getWidget().addChangeHandler(event -> sendValueChange());
getWidget().addDomHandler(event -> {
- if (getState().valueChangeMode != ValueChangeMode.BLUR) {
- scheduleValueChange();
- }
+ getValueChangeHandler().scheduleValueChange();
}, InputEvent.getType());
+
getWidget().addMouseUpHandler(new ResizeMouseUpHandler());
}
*/
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;
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 {
}
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;
}
/**
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")
* 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);
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;
/**
super.init();
getWidget().addChangeHandler(event -> sendValueChange());
getWidget().addDomHandler(event -> {
- if (getState().valueChangeMode != ValueChangeMode.BLUR) {
- scheduleValueChange();
- }
+ getValueChangeHandler().scheduleValueChange();
}, InputEvent.getType());
}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+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
\ No newline at end of file
*
* @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;
}
/**
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;
* @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 {
/**
* Returns the last known cursor position of the field.
*
+ * @return the last known cursor position
*/
public int getCursorPosition() {
return lastKnownCursorPosition;
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(
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;
}
/**
* 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());
--- /dev/null
+/*
+ * 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();
+
+}
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);
}
}
- /*
- * (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);
--- /dev/null
+/*
+ * 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("");
+ }
+}
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;
--- /dev/null
+/*
+ * 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;
+ }
+ });
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Different modes for when and how often field value changes are transmitted
+ * from the client to the server.
+ */
+public enum ValueChangeMode {
+
+ /**
+ * Fires a server-side event when the field loses focus.
+ */
+ BLUR,
+
+ /**
+ * Fires a server-side event every time the client-side value changes. This
+ * gives the least latency but may cause unnecessary traffic.
+ */
+ EAGER,
+
+ /**
+ * Fires a server-side event at defined intervals as long as the value
+ * changes from one event to the next. For instance, you can use this mode
+ * to transmit a snapshot of the contents of a text area every second as
+ * long as the user keeps typing.
+ */
+ TIMEOUT,
+
+ /**
+ * On every user event, schedule a server-side event after a defined
+ * interval, cancelling the currently-scheduled event if any. This is a good
+ * choice if you want to, for instance, wait for a small break in the user's
+ * typing before sending the event.
+ */
+ LAZY
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+
+}
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.
+++ /dev/null
-/*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.vaadin.shared.ui.textfield;
-
-/**
- * Different modes for when and how often field value changes are transmitted
- * from the client to the server.
- */
-public enum ValueChangeMode {
-
- /**
- * Fires a server-side event when the field loses focus.
- */
- BLUR,
-
- /**
- * Fires a server-side event every time the client-side value changes. This
- * gives the least latency but may cause unnecessary traffic.
- */
- EAGER,
-
- /**
- * Fires a server-side event at defined intervals as long as the value
- * changes from one event to the next. For instance, you can use this mode
- * to transmit a snapshot of the contents of a text area every second as
- * long as the user keeps typing.
- */
- TIMEOUT,
-
- /**
- * On every user event, schedule a server-side event after a defined
- * interval, cancelling the currently-scheduled event if any. This is a good
- * choice if you want to, for instance, wait for a small break in the user's
- * typing before sending the event.
- */
- LAZY
-}
margin-right: 2px;
}
-.v-richtextarea-readonly {
+.v-richtextarea.v-readonly {
border: none;
}
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;
+++ /dev/null
-/*
- * 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());
- }
-
-}
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;
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;
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 {
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 {
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 {
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 {
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 {
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);
}
}
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 {
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 {
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> {
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;
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 {
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;
--- /dev/null
+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;
+ }
+ }
+
+}
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;
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 {
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")
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;
import com.vaadin.v7.ui.TreeTable;
import com.vaadin.v7.ui.TwinColSelect;
-@SuppressWarnings("rawtypes")
public class TabIndexes extends AbstractTestUIWithLog {
private List<Focusable> fields;
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 {
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;
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);
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;
+++ /dev/null
-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;
- }
- }
-
-}
--- /dev/null
+/*
+ * 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);
+ }
+
+ }
+}