Change-Id: I6f430c77caaad6d610133f340eba960f2268897etags/8.0.0.alpha2
@@ -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> |
@@ -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> </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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
@@ -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); |
@@ -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()); | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
/** |
@@ -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()); |
@@ -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(); | |||
} |
@@ -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); |
@@ -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(""); | |||
} | |||
} |
@@ -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; |
@@ -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; | |||
} | |||
}); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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 |
@@ -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(); | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} |
@@ -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. |
@@ -113,7 +113,7 @@ textarea.v-textarea-readonly:focus { | |||
margin-right: 2px; | |||
} | |||
.v-richtextarea-readonly { | |||
.v-richtextarea.v-readonly { | |||
border: none; | |||
} | |||
@@ -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; |
@@ -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()); | |||
} | |||
} |
@@ -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; |
@@ -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; |
@@ -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 { | |||
@@ -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 { | |||
@@ -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 { | |||
@@ -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 { | |||
@@ -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 { | |||
@@ -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); | |||
} | |||
} |
@@ -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 { | |||
@@ -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,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> { | |||
@@ -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; |
@@ -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 { | |||
@@ -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; |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
@@ -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 { |
@@ -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") |
@@ -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; |
@@ -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 { |
@@ -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; | |||
@@ -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,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; |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |