package com.vaadin.client.ui;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
* @author Vaadin Ltd.
*
*/
-public class VRichTextArea extends Composite implements Field, ChangeHandler,
- BlurHandler, KeyPressHandler, KeyDownHandler, Focusable {
+public class VRichTextArea extends Composite implements Field, KeyPressHandler,
+ KeyDownHandler, Focusable {
/**
* The input node CSS classname.
private ShortcutActionHandlerOwner hasShortcutActionHandler;
- /** For internal use only. May be removed or replaced in the future. */
- public String currentValue = "";
-
private boolean readOnly = false;
+ private final Map<BlurHandler, HandlerRegistration> blurHandlers = new HashMap<BlurHandler, HandlerRegistration>();
+
public VRichTextArea() {
createRTAComponents();
fp.add(formatter);
private void createRTAComponents() {
rta = new RichTextArea();
rta.setWidth("100%");
- rta.addBlurHandler(this);
rta.addKeyDownHandler(this);
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());
+ }
}
public void setEnabled(boolean enabled) {
* Swaps html to rta and visa versa.
*/
private void swapEditableArea() {
+ String value = getValue();
if (html.isAttached()) {
fp.remove(html);
if (BrowserInfo.get().isWebkit()) {
createRTAComponents(); // recreate new RTA to bypass #5379
fp.add(formatter);
}
- rta.setHTML(currentValue);
fp.add(rta);
} else {
- html.setHTML(currentValue);
fp.remove(rta);
fp.add(html);
}
+ setValue(value);
}
/** For internal use only. May be removed or replaced in the future. */
return readOnly;
}
- // TODO is this really used, or does everything go via onBlur() only?
- @Override
- public void onChange(ChangeEvent event) {
- synchronizeContentToServer();
- }
-
- /**
- * Method is public to let popupview force synchronization on close.
- */
- public void synchronizeContentToServer() {
- if (client != null && id != null) {
- final String html = sanitizeRichTextAreaValue(rta.getHTML());
- if (!html.equals(currentValue)) {
- client.updateVariable(id, "text", html, immediate);
- currentValue = html;
- }
- }
- }
-
- /**
- * 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.
- *
- * @param html
- * @return cleaned html string
- */
- private String sanitizeRichTextAreaValue(String html) {
- BrowserInfo browser = BrowserInfo.get();
- String result = html;
- if (browser.isFirefox()) {
- if ("<br>".equals(html)) {
- result = "";
- }
- } else if (browser.isWebkit()) {
- if ("<div><br></div>".equals(html)) {
- result = "";
- }
- } else if (browser.isIE()) {
- if ("<P> </P>".equals(html)) {
- result = "";
- }
- } else if (browser.isOpera()) {
- if ("<br>".equals(html) || "<p><br></p>".equals(html)) {
- result = "";
- }
- }
- return result;
- }
-
- @Override
- public void onBlur(BlurEvent event) {
- synchronizeContentToServer();
- // TODO notify possible server side blur/focus listeners
- }
-
/**
* @return space used by components paddings and borders
*/
rta.setTabIndex(index);
}
+ /**
+ * Set the value of the text area
+ *
+ * @param value
+ * The text value. Can be html.
+ */
+ public void setValue(String value) {
+ if (rta.isAttached()) {
+ rta.setHTML(value);
+ } else {
+ html.setHTML(value);
+ }
+ }
+
+ /**
+ * Get the value the text area
+ */
+ 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 getSanitazedValue() {
+ BrowserInfo browser = BrowserInfo.get();
+ String result = getValue();
+ if (browser.isFirefox()) {
+ if ("<br>".equals(html)) {
+ result = "";
+ }
+ } else if (browser.isWebkit()) {
+ if ("<div><br></div>".equals(html)) {
+ result = "";
+ }
+ } else if (browser.isIE()) {
+ if ("<P> </P>".equals(html)) {
+ result = "";
+ }
+ } else if (browser.isOpera()) {
+ if ("<br>".equals(html) || "<p><br></p>".equals(html)) {
+ 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();
+ }
+ }
}
*/
package com.vaadin.client.ui.richtextarea;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.user.client.Event;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
import com.vaadin.client.ui.VRichTextArea;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.Connect.LoadStyle;
+import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.RichTextArea;
@Connect(value = RichTextArea.class, loadStyle = LoadStyle.LAZY)
public class RichTextAreaConnector extends AbstractFieldConnector implements
Paintable, BeforeShortcutActionListener {
+ /*
+ * Last value received from the server
+ */
+ private String cachedValue = "";
+
+ @Override
+ protected void init() {
+ getWidget().addBlurHandler(new BlurHandler() {
+
+ @Override
+ public void onBlur(BlurEvent event) {
+ flush();
+ }
+ });
+ }
+
@Override
public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
getWidget().client = client;
getWidget().id = uidl.getId();
if (uidl.hasVariable("text")) {
- getWidget().currentValue = uidl.getStringVariable("text");
- if (getWidget().rta.isAttached()) {
- getWidget().rta.setHTML(getWidget().currentValue);
- } else {
- getWidget().html.setHTML(getWidget().currentValue);
+ String newValue = uidl.getStringVariable("text");
+ if (!SharedUtil.equals(newValue, cachedValue)) {
+ getWidget().setValue(newValue);
+ cachedValue = newValue;
}
}
- if (isRealUpdate(uidl)) {
- getWidget().setEnabled(isEnabled());
- }
if (!isRealUpdate(uidl)) {
return;
}
+ getWidget().setEnabled(isEnabled());
getWidget().setReadOnly(isReadOnly());
getWidget().immediate = getState().immediate;
int newMaxLength = uidl.hasAttribute("maxLength") ? uidl
@Override
public void flush() {
- getWidget().synchronizeContentToServer();
+ if (getConnection() != null && getConnectorId() != null) {
+ final String html = getWidget().getSanitazedValue();
+ if (!html.equals(cachedValue)) {
+ getConnection().updateVariable(getConnectorId(), "text", html,
+ getState().immediate);
+ getWidget().setValue(html);
+ }
+ }
};
-
}
--- /dev/null
+package com.vaadin.tests.components.richtextarea;
+
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.progressindicator.ProgressIndicatorServerRpc;
+import com.vaadin.tests.components.AbstractComponentTest;
+import com.vaadin.tests.components.AbstractTestCase;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.ProgressIndicator;
+import com.vaadin.ui.RichTextArea;
+
+public class RichTextAreaUpdateWhileTyping extends AbstractTestUI {
+
+ private RichTextArea rta;
+
+ @Override
+ protected void setup(VaadinRequest request) {
+
+ // Progress indicator for changing the value of the RTA
+ ProgressIndicator pi = new ProgressIndicator() {
+ {
+ registerRpc(new ProgressIndicatorServerRpc() {
+
+ @Override
+ public void poll() {
+ rta.markAsDirty();
+ }
+ });
+ }
+ };
+ pi.setHeight("0px");
+ addComponent(pi);
+
+ rta = new RichTextArea();
+ rta.setId("rta");
+ rta.setImmediate(true);
+ addComponent(rta);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 11741;
+ }
+}