Listen to MouseUp event on the <textarea> and notify the state with the width and height if changed. Add com.vaadin.client.Util.Size to manipulates the css width/height. Change-Id: I96a308658d2877f1f6c05feaa7840a268bb06709tags/7.2.6
@@ -1,12 +1,12 @@ | |||
/* | |||
* Copyright 2000-2014 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 | |||
@@ -16,11 +16,14 @@ | |||
package com.vaadin.client; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
@@ -36,6 +39,8 @@ import com.google.gwt.dom.client.Style.Display; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
import com.google.gwt.dom.client.Touch; | |||
import com.google.gwt.event.dom.client.KeyEvent; | |||
import com.google.gwt.regexp.shared.MatchResult; | |||
import com.google.gwt.regexp.shared.RegExp; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Event; | |||
@@ -56,9 +61,9 @@ public class Util { | |||
/** | |||
* Helper method for debugging purposes. | |||
* | |||
* | |||
* Stops execution on firefox browsers on a breakpoint. | |||
* | |||
* | |||
*/ | |||
public static native void browserDebugger() | |||
/*-{ | |||
@@ -86,10 +91,10 @@ public class Util { | |||
/** | |||
* | |||
* Returns the topmost element of from given coordinates. | |||
* | |||
* | |||
* TODO fix crossplat issues clientX vs pageX. See quircksmode. Not critical | |||
* for vaadin as we scroll div istead of page. | |||
* | |||
* | |||
* @param x | |||
* @param y | |||
* @return the element at given coordinates | |||
@@ -98,7 +103,7 @@ public class Util { | |||
int clientX, int clientY) | |||
/*-{ | |||
var el = $wnd.document.elementFromPoint(clientX, clientY); | |||
// Call elementFromPoint two times to make sure IE8 also returns something sensible if the application is running in an iframe | |||
// Call elementFromPoint two times to make sure IE8 also returns something sensible if the application is running in an iframe | |||
el = $wnd.document.elementFromPoint(clientX, clientY); | |||
if(el != null && el.nodeType == 3) { | |||
el = el.parentNode; | |||
@@ -110,18 +115,18 @@ public class Util { | |||
* This helper method can be called if components size have been changed | |||
* outside rendering phase. It notifies components parent about the size | |||
* change so it can react. | |||
* | |||
* | |||
* When using this method, developer should consider if size changes could | |||
* be notified lazily. If lazy flag is true, method will save widget and | |||
* wait for a moment until it notifies parents in chunks. This may vastly | |||
* optimize layout in various situation. Example: if component have a lot of | |||
* images their onload events may fire "layout phase" many times in a short | |||
* period. | |||
* | |||
* | |||
* @param widget | |||
* @param lazy | |||
* run componentSizeUpdated lazyly | |||
* | |||
* | |||
* @deprecated As of 7.0, use | |||
* {@link LayoutManager#setNeedsMeasure(ComponentConnector)} | |||
* instead | |||
@@ -171,7 +176,7 @@ public class Util { | |||
/** | |||
* Converts html entities to text. | |||
* | |||
* | |||
* @param html | |||
* @return escaped string presentation of given html | |||
*/ | |||
@@ -189,7 +194,7 @@ public class Util { | |||
/** | |||
* Escapes the string so it is safe to write inside an HTML attribute. | |||
* | |||
* | |||
* @param attribute | |||
* The string to escape | |||
* @return An escaped version of <literal>attribute</literal>. | |||
@@ -208,9 +213,9 @@ public class Util { | |||
/** | |||
* Clones given element as in JavaScript. | |||
* | |||
* | |||
* Deprecate this if there appears similar method into GWT someday. | |||
* | |||
* | |||
* @param element | |||
* @param deep | |||
* clone child tree also | |||
@@ -488,9 +493,9 @@ public class Util { | |||
/** | |||
* Run workaround for webkits overflow auto issue. | |||
* | |||
* | |||
* See: our bug #2138 and https://bugs.webkit.org/show_bug.cgi?id=21462 | |||
* | |||
* | |||
* @param elem | |||
* with overflow auto | |||
*/ | |||
@@ -561,7 +566,7 @@ public class Util { | |||
* dimension is not specified as relative it will return -1. If the shared | |||
* state does not contain width or height specifications this will return | |||
* null. | |||
* | |||
* | |||
* @param state | |||
* @return | |||
*/ | |||
@@ -594,7 +599,7 @@ public class Util { | |||
* Checks if a and b are equals using {@link #equals(Object)}. Handles null | |||
* values as well. Does not ensure that objects are of the same type. | |||
* Assumes that the first object's equals method handle equals properly. | |||
* | |||
* | |||
* @param a | |||
* The first value to compare | |||
* @param b | |||
@@ -615,7 +620,7 @@ public class Util { | |||
/** | |||
* Gets the border-box width for the given element, i.e. element width + | |||
* border + padding. Always rounds up to nearest integer. | |||
* | |||
* | |||
* @param element | |||
* The element to check | |||
* @return The border-box width for the element | |||
@@ -640,7 +645,7 @@ public class Util { | |||
/** | |||
* Gets the border-box height for the given element, i.e. element height + | |||
* border + padding. Always rounds up to nearest integer. | |||
* | |||
* | |||
* @param element | |||
* The element to check | |||
* @return The border-box height for the element | |||
@@ -687,7 +692,7 @@ public class Util { | |||
var borderBottomPx = cs.borderBottom; | |||
var paddingTopPx = cs.paddingTop; | |||
var paddingBottomPx = cs.paddingBottom; | |||
var height = heightPx.substring(0,heightPx.length-2); | |||
var border = borderTopPx.substring(0,borderTopPx.length-2)+borderBottomPx.substring(0,borderBottomPx.length-2); | |||
var padding = paddingTopPx.substring(0,paddingTopPx.length-2)+paddingBottomPx.substring(0,paddingBottomPx.length-2); | |||
@@ -707,7 +712,7 @@ public class Util { | |||
var borderRightPx = cs.borderRight; | |||
var paddingLeftPx = cs.paddingLeft; | |||
var paddingRightPx = cs.paddingRight; | |||
var width = widthPx.substring(0,widthPx.length-2); | |||
var border = borderLeftPx.substring(0,borderLeftPx.length-2)+borderRightPx.substring(0,borderRightPx.length-2); | |||
var padding = paddingLeftPx.substring(0,paddingLeftPx.length-2)+paddingRightPx.substring(0,paddingRightPx.length-2); | |||
@@ -737,7 +742,7 @@ public class Util { | |||
/** | |||
* Detects what is currently the overflow style attribute in given element. | |||
* | |||
* | |||
* @param pe | |||
* the element to detect | |||
* @return true if auto or scroll | |||
@@ -759,7 +764,7 @@ public class Util { | |||
* A simple helper method to detect "computed style" (aka style sheets + | |||
* element styles). Values returned differ a lot depending on browsers. | |||
* Always be very careful when using this. | |||
* | |||
* | |||
* @param el | |||
* the element from which the style property is detected | |||
* @param p | |||
@@ -770,7 +775,7 @@ public class Util { | |||
com.google.gwt.dom.client.Element el, String p) | |||
/*-{ | |||
try { | |||
if (el.currentStyle) { | |||
// IE | |||
return el.currentStyle[p]; | |||
@@ -794,9 +799,9 @@ public class Util { | |||
* also returned if "element" is part of its caption. If | |||
* <literal>element</literal> is not part of any child component, null is | |||
* returned. | |||
* | |||
* | |||
* This method returns the deepest nested VPaintableWidget. | |||
* | |||
* | |||
* @param client | |||
* A reference to ApplicationConnection | |||
* @param parent | |||
@@ -854,7 +859,7 @@ public class Util { | |||
/** | |||
* Will (attempt) to focus the given DOM Element. | |||
* | |||
* | |||
* @param el | |||
* the element to focus | |||
*/ | |||
@@ -870,7 +875,7 @@ public class Util { | |||
/** | |||
* Helper method to find the nearest parent paintable instance by traversing | |||
* the DOM upwards from given element. | |||
* | |||
* | |||
* @param element | |||
* the element to start from | |||
*/ | |||
@@ -888,7 +893,7 @@ public class Util { | |||
/** | |||
* Helper method to find first instance of given Widget type found by | |||
* traversing DOM upwards from given element. | |||
* | |||
* | |||
* @param element | |||
* the element where to start seeking of Widget | |||
* @param class1 | |||
@@ -925,7 +930,7 @@ public class Util { | |||
/** | |||
* Force webkit to redraw an element | |||
* | |||
* | |||
* @param element | |||
* The element that should be redrawn | |||
*/ | |||
@@ -956,9 +961,9 @@ public class Util { | |||
/** | |||
* Detaches and re-attaches the element from its parent. The element is | |||
* reattached at the same position in the DOM as it was before. | |||
* | |||
* | |||
* Does nothing if the element is not attached to the DOM. | |||
* | |||
* | |||
* @param element | |||
* The element to detach and re-attach | |||
*/ | |||
@@ -993,7 +998,7 @@ public class Util { | |||
/** | |||
* Returns the index of the childElement within its parent. | |||
* | |||
* | |||
* @param subElement | |||
* @return | |||
*/ | |||
@@ -1069,7 +1074,7 @@ public class Util { | |||
* Temporarily sets the {@code styleProperty} to {@code tempValue} and then | |||
* resets it to its current value. Used mainly to work around rendering | |||
* issues in IE (and possibly in other browsers) | |||
* | |||
* | |||
* @param element | |||
* The target element | |||
* @param styleProperty | |||
@@ -1092,7 +1097,7 @@ public class Util { | |||
* A helper method to return the client position from an event. Returns | |||
* position from either first changed touch (if touch event) or from the | |||
* event itself. | |||
* | |||
* | |||
* @param event | |||
* @return | |||
*/ | |||
@@ -1108,7 +1113,7 @@ public class Util { | |||
* Find the element corresponding to the coordinates in the passed mouse | |||
* event. Please note that this is not always the same as the target of the | |||
* event e.g. if event capture is used. | |||
* | |||
* | |||
* @param event | |||
* the mouse event to get coordinates from | |||
* @return the element at the coordinates of the event | |||
@@ -1125,7 +1130,7 @@ public class Util { | |||
* A helper method to return the client position from an event. Returns | |||
* position from either first changed touch (if touch event) or from the | |||
* event itself. | |||
* | |||
* | |||
* @param event | |||
* @return | |||
*/ | |||
@@ -1138,7 +1143,7 @@ public class Util { | |||
} | |||
/** | |||
* | |||
* | |||
* @see #getTouchOrMouseClientY(Event) | |||
* @param currentGwtEvent | |||
* @return | |||
@@ -1149,7 +1154,7 @@ public class Util { | |||
/** | |||
* @see #getTouchOrMouseClientX(Event) | |||
* | |||
* | |||
* @param event | |||
* @return | |||
*/ | |||
@@ -1218,7 +1223,7 @@ public class Util { | |||
/** | |||
* Gets the currently focused element. | |||
* | |||
* | |||
* @return The active element or null if no active element could be found. | |||
*/ | |||
public native static com.google.gwt.user.client.Element getFocusedElement() | |||
@@ -1226,13 +1231,13 @@ public class Util { | |||
if ($wnd.document.activeElement) { | |||
return $wnd.document.activeElement; | |||
} | |||
return null; | |||
}-*/; | |||
/** | |||
* Gets the currently focused element for Internet Explorer. | |||
* | |||
* | |||
* @return The currently focused element | |||
* @deprecated Use #getFocusedElement instead | |||
*/ | |||
@@ -1246,7 +1251,7 @@ public class Util { | |||
* this method checks that this widget nor any of its parents is hidden. Can | |||
* be e.g used to check whether component should react to some events or | |||
* not. | |||
* | |||
* | |||
* @param widget | |||
* @return true if attached and displayed | |||
*/ | |||
@@ -1279,7 +1284,7 @@ public class Util { | |||
/** | |||
* Scrolls an element into view vertically only. Modified version of | |||
* Element.scrollIntoView. | |||
* | |||
* | |||
* @param elem | |||
* The element to scroll into view | |||
*/ | |||
@@ -1287,11 +1292,11 @@ public class Util { | |||
/*-{ | |||
var top = elem.offsetTop; | |||
var height = elem.offsetHeight; | |||
if (elem.parentNode != elem.offsetParent) { | |||
top -= elem.parentNode.offsetTop; | |||
} | |||
var cur = elem.parentNode; | |||
while (cur && (cur.nodeType == 1)) { | |||
if (top < cur.scrollTop) { | |||
@@ -1300,12 +1305,12 @@ public class Util { | |||
if (top + height > cur.scrollTop + cur.clientHeight) { | |||
cur.scrollTop = (top + height) - cur.clientHeight; | |||
} | |||
var offsetTop = cur.offsetTop; | |||
if (cur.parentNode != cur.offsetParent) { | |||
offsetTop -= cur.parentNode.offsetTop; | |||
} | |||
top += offsetTop - cur.scrollTop; | |||
cur = cur.parentNode; | |||
} | |||
@@ -1314,7 +1319,7 @@ public class Util { | |||
/** | |||
* Checks if the given event is either a touch event or caused by the left | |||
* mouse button | |||
* | |||
* | |||
* @param event | |||
* @return true if the event is a touch event or caused by the left mouse | |||
* button, false otherwise | |||
@@ -1326,7 +1331,7 @@ public class Util { | |||
/** | |||
* Performs a shallow comparison of the collections. | |||
* | |||
* | |||
* @param collection1 | |||
* The first collection | |||
* @param collection2 | |||
@@ -1372,7 +1377,7 @@ public class Util { | |||
/** | |||
* Resolve a relative URL to an absolute URL based on the current document's | |||
* location. | |||
* | |||
* | |||
* @param url | |||
* a string with the relative URL to resolve | |||
* @return the corresponding absolute URL as a string | |||
@@ -1399,4 +1404,161 @@ public class Util { | |||
} | |||
} | |||
/** | |||
* Wrap a css size value and its unit and translate back and forth to the | |||
* string representation.<br/> | |||
* Eg. 50%, 123px, ... | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
@SuppressWarnings("serial") | |||
public static class CssSize implements Serializable { | |||
/* | |||
* Map the size units with their type. | |||
*/ | |||
private static Map<String, Unit> type2Unit = new HashMap<String, Style.Unit>(); | |||
static { | |||
for (Unit unit : Unit.values()) { | |||
type2Unit.put(unit.getType(), unit); | |||
} | |||
} | |||
/** | |||
* Gets the unit value by its type. | |||
* | |||
* @since | |||
* @param type | |||
* the type of the unit as found in the style. | |||
* @return the unit value. | |||
*/ | |||
public static Unit unitByType(String type) { | |||
return type2Unit.get(type); | |||
} | |||
/* | |||
* Regex to parse the size. | |||
*/ | |||
private static final RegExp sizePattern = RegExp | |||
.compile(SharedUtil.SIZE_PATTERN); | |||
/** | |||
* Parse the size from string format to {@link CssSize}. | |||
* | |||
* @param s | |||
* the size as string. | |||
* @return a {@link CssSize} object. | |||
*/ | |||
public static CssSize fromString(String s) { | |||
if (s == null) { | |||
return null; | |||
} | |||
s = s.trim(); | |||
if ("".equals(s)) { | |||
return null; | |||
} | |||
float size = 0; | |||
Unit unit = null; | |||
MatchResult matcher = sizePattern.exec(s); | |||
if (matcher.getGroupCount() > 1) { | |||
size = Float.parseFloat(matcher.getGroup(1)); | |||
if (size < 0) { | |||
size = -1; | |||
unit = Unit.PX; | |||
} else { | |||
String symbol = matcher.getGroup(2); | |||
unit = unitByType(symbol); | |||
} | |||
} else { | |||
throw new IllegalArgumentException("Invalid size argument: \"" | |||
+ s + "\" (should match " + sizePattern.getSource() | |||
+ ")"); | |||
} | |||
return new CssSize(size, unit); | |||
} | |||
/** | |||
* Creates a {@link CssSize} using a value and its measurement unit. | |||
* | |||
* @since | |||
* @param value | |||
* the value. | |||
* @param unit | |||
* the unit. | |||
* @return the {@link CssSize} object. | |||
*/ | |||
public static CssSize fromValueUnit(float value, Unit unit) { | |||
return new CssSize(value, unit); | |||
} | |||
/* | |||
* The value. | |||
*/ | |||
private final float value; | |||
/* | |||
* The measure unit. | |||
*/ | |||
private final Unit unit; | |||
private CssSize(float value, Unit unit) { | |||
this.value = value; | |||
this.unit = unit; | |||
} | |||
/** | |||
* Gets the value for this css size. | |||
* | |||
* @return the value. | |||
*/ | |||
public float getValue() { | |||
return value; | |||
} | |||
/** | |||
* Gets the measurement unit for this css size. | |||
* | |||
* @return the unit. | |||
*/ | |||
public Unit getUnit() { | |||
return unit; | |||
} | |||
@Override | |||
public String toString() { | |||
return value + unit.getType(); | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (obj instanceof CssSize) { | |||
CssSize size = (CssSize) obj; | |||
return size.value == value && size.unit == unit; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Check whether the two sizes are equals. | |||
* | |||
* @since | |||
* @param cssSize1 | |||
* the first size to compare. | |||
* @param cssSize2 | |||
* the other size to compare with the first one. | |||
* @return true if the two sizes are equals, otherwise false. | |||
*/ | |||
public static boolean equals(String cssSize1, String cssSize2) { | |||
return CssSize.fromString(cssSize1).equals(CssSize.fromString(cssSize2)); | |||
} | |||
} | |||
} |
@@ -1,12 +1,12 @@ | |||
/* | |||
* Copyright 2000-2014 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 | |||
@@ -16,6 +16,10 @@ | |||
package com.vaadin.client.ui.textarea; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.event.dom.client.MouseUpEvent; | |||
import com.google.gwt.event.dom.client.MouseUpHandler; | |||
import com.vaadin.client.Util.CssSize; | |||
import com.vaadin.client.ui.VTextArea; | |||
import com.vaadin.client.ui.textfield.TextFieldConnector; | |||
import com.vaadin.shared.ui.Connect; | |||
@@ -34,4 +38,54 @@ public class TextAreaConnector extends TextFieldConnector { | |||
public VTextArea getWidget() { | |||
return (VTextArea) super.getWidget(); | |||
} | |||
@Override | |||
protected void init() { | |||
super.init(); | |||
getWidget().addMouseUpHandler(new ResizeMouseUpHandler()); | |||
} | |||
/* | |||
* Workaround to handle the resize on the mouse up. | |||
*/ | |||
private class ResizeMouseUpHandler implements MouseUpHandler { | |||
@Override | |||
public void onMouseUp(MouseUpEvent event) { | |||
Element element = getWidget().getElement(); | |||
updateSize(element.getStyle().getHeight(), getState().height, | |||
"height"); | |||
updateSize(element.getStyle().getWidth(), getState().width, "width"); | |||
} | |||
/* | |||
* Update the specified size on the server. | |||
*/ | |||
private void updateSize(String sizeText, String stateSizeText, | |||
String sizeType) { | |||
CssSize stateSize = CssSize.fromString(stateSizeText); | |||
CssSize newSize = CssSize.fromString(sizeText); | |||
if (stateSize == null && newSize == null) { | |||
return; | |||
} else if (newSize == null) { | |||
sizeText = ""; | |||
// Else, if the current stateSize is null, just go ahead and set | |||
// the newSize, so no check on stateSize is needed. | |||
} else if (stateSize != null && stateSize.equals(newSize)) { | |||
return; | |||
} | |||
getConnection().updateVariable(getConnectorId(), sizeType, | |||
sizeText, false); | |||
} | |||
} | |||
} |
@@ -1,12 +1,12 @@ | |||
/* | |||
* Copyright 2000-2014 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 | |||
@@ -37,6 +37,7 @@ import com.vaadin.server.VaadinSession; | |||
import com.vaadin.shared.AbstractComponentState; | |||
import com.vaadin.shared.ComponentConstants; | |||
import com.vaadin.shared.ui.ComponentStateUtil; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.Field.ValueChangeEvent; | |||
import com.vaadin.util.ReflectTools; | |||
@@ -45,7 +46,7 @@ import com.vaadin.util.ReflectTools; | |||
* {@link Component} interface. Basic UI components that are not derived from an | |||
* external component can inherit this class to easily qualify as Vaadin | |||
* components. Most components in Vaadin do just that. | |||
* | |||
* | |||
* @author Vaadin Ltd. | |||
* @since 3.0 | |||
*/ | |||
@@ -83,7 +84,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
private Unit widthUnit = Unit.PIXELS; | |||
private Unit heightUnit = Unit.PIXELS; | |||
private static final Pattern sizePattern = Pattern | |||
.compile("^(-?\\d+(\\.\\d+)?)(%|px|em|rem|ex|in|cm|mm|pt|pc)?$"); | |||
.compile(SharedUtil.SIZE_PATTERN); | |||
/** | |||
* Keeps track of the Actions added to this component; the actual | |||
@@ -240,7 +241,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
* Sets the component's caption <code>String</code>. Caption is the visible | |||
* name of the component. This method will trigger a | |||
* {@link RepaintRequestEvent}. | |||
* | |||
* | |||
* @param caption | |||
* the new caption <code>String</code> for the component. | |||
*/ | |||
@@ -271,7 +272,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Sets the locale of this component. | |||
* | |||
* | |||
* <pre> | |||
* // Component for which the locale is meaningful | |||
* InlineDateField date = new InlineDateField("Datum"); | |||
@@ -283,8 +284,8 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
* date.setResolution(DateField.RESOLUTION_DAY); | |||
* layout.addComponent(date); | |||
* </pre> | |||
* | |||
* | |||
* | |||
* | |||
* @param locale | |||
* the locale to become this component's locale. | |||
*/ | |||
@@ -310,7 +311,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Sets the component's icon. This method will trigger a | |||
* {@link RepaintRequestEvent}. | |||
* | |||
* | |||
* @param icon | |||
* the icon to be shown with the component's caption. | |||
*/ | |||
@@ -376,7 +377,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Sets the component's immediate mode to the specified status. | |||
* | |||
* | |||
* @param immediate | |||
* the boolean value specifying if the component should be in the | |||
* immediate mode after the call. | |||
@@ -437,11 +438,11 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
* Sets the component's description. See {@link #getDescription()} for more | |||
* information on what the description is. This method will trigger a | |||
* {@link RepaintRequestEvent}. | |||
* | |||
* | |||
* The description is displayed as HTML in tooltips or directly in certain | |||
* components so care should be taken to avoid creating the possibility for | |||
* HTML injection and possibly XSS vulnerabilities. | |||
* | |||
* | |||
* @param description | |||
* the new description string for the component. | |||
*/ | |||
@@ -490,7 +491,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
* To find the Window that contains the component, use {@code Window w = | |||
* getParent(Window.class);} | |||
* </p> | |||
* | |||
* | |||
* @param <T> | |||
* The type of the ancestor | |||
* @param parentType | |||
@@ -511,7 +512,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Gets the error message for this component. | |||
* | |||
* | |||
* @return ErrorMessage containing the description of the error state of the | |||
* component or null, if the component contains no errors. Extending | |||
* classes should override this method if they support other error | |||
@@ -524,9 +525,9 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Gets the component's error message. | |||
* | |||
* | |||
* @link Terminal.ErrorMessage#ErrorMessage(String, int) | |||
* | |||
* | |||
* @return the component's error message. | |||
*/ | |||
public ErrorMessage getComponentError() { | |||
@@ -536,9 +537,9 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Sets the component's error message. The message may contain certain XML | |||
* tags, for more information see | |||
* | |||
* | |||
* @link Component.ErrorMessage#ErrorMessage(String, int) | |||
* | |||
* | |||
* @param componentError | |||
* the new <code>ErrorMessage</code> of the component. | |||
*/ | |||
@@ -615,7 +616,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Build CSS compatible string representation of height. | |||
* | |||
* | |||
* @return CSS height | |||
*/ | |||
private String getCSSHeight() { | |||
@@ -624,7 +625,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Build CSS compatible string representation of width. | |||
* | |||
* | |||
* @return CSS width | |||
*/ | |||
private String getCSSWidth() { | |||
@@ -634,12 +635,12 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Returns the shared state bean with information to be sent from the server | |||
* to the client. | |||
* | |||
* | |||
* Subclasses should override this method and set any relevant fields of the | |||
* state returned by super.getState(). | |||
* | |||
* | |||
* @since 7.0 | |||
* | |||
* | |||
* @return updated component shared state | |||
*/ | |||
@Override | |||
@@ -730,7 +731,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Sets the data object, that can be used for any application specific data. | |||
* The component does not use or modify this data. | |||
* | |||
* | |||
* @param data | |||
* the Application specific data. | |||
* @since 3.1 | |||
@@ -741,7 +742,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Gets the application specific data. See {@link #setData(Object)}. | |||
* | |||
* | |||
* @return the Application specific data set with setData function. | |||
* @since 3.1 | |||
*/ | |||
@@ -896,7 +897,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
size = -1; | |||
unit = Unit.PIXELS; | |||
} else { | |||
String symbol = matcher.group(3); | |||
String symbol = matcher.group(2); | |||
unit = Unit.getUnitFromSymbol(symbol); | |||
} | |||
} else { | |||
@@ -931,7 +932,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Gets the {@link ActionManager} used to manage the | |||
* {@link ShortcutListener}s added to this {@link Field}. | |||
* | |||
* | |||
* @return the ActionManager in use | |||
*/ | |||
protected ActionManager getActionManager() { | |||
@@ -975,7 +976,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
/** | |||
* Determine whether a <code>content</code> component is equal to, or the | |||
* ancestor of this component. | |||
* | |||
* | |||
* @param content | |||
* the potential ancestor element | |||
* @return <code>true</code> if the relationship holds |
@@ -147,6 +147,18 @@ public abstract class AbstractTextField extends AbstractField<String> implements | |||
try { | |||
// Sets the height set by the user when resize the <textarea>. | |||
String newHeight = (String) variables.get("height"); | |||
if (newHeight != null) { | |||
setHeight(newHeight); | |||
} | |||
// Sets the width set by the user when resize the <textarea>. | |||
String newWidth = (String) variables.get("width"); | |||
if (newWidth != null) { | |||
setWidth(newWidth); | |||
} | |||
if (variables.containsKey(TextFieldConstants.VAR_CURSOR)) { | |||
Integer object = (Integer) variables | |||
.get(TextFieldConstants.VAR_CURSOR); |
@@ -1,12 +1,12 @@ | |||
/* | |||
* Copyright 2000-2014 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 | |||
@@ -19,17 +19,17 @@ import java.io.Serializable; | |||
/** | |||
* Misc internal utility methods used by both the server and the client package. | |||
* | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.1 | |||
* | |||
* | |||
*/ | |||
public class SharedUtil implements Serializable { | |||
/** | |||
* Checks if a and b are equals using {@link #equals(Object)}. Handles null | |||
* values as well. Does not ensure that objects are of the same type. | |||
* Assumes that the first object's equals method handle equals properly. | |||
* | |||
* | |||
* @param o1 | |||
* The first value to compare | |||
* @param o2 | |||
@@ -46,11 +46,18 @@ public class SharedUtil implements Serializable { | |||
/** | |||
* Trims trailing slashes (if any) from a string. | |||
* @param value The string value to be trimmed. Cannot be null. | |||
* | |||
* @param value | |||
* The string value to be trimmed. Cannot be null. | |||
* @return String value without trailing slashes. | |||
*/ | |||
public static String trimTrailingSlashes(String value) { | |||
return value.replaceAll("/*$", ""); | |||
} | |||
/** | |||
* RegEx pattern to extract the width/height values. | |||
*/ | |||
public static final String SIZE_PATTERN = "^(-?\\d*(?:\\.\\d+)?)(%|px|em|rem|ex|in|cm|mm|pt|pc)?$"; | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* Copyright 2000-2014 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.textarea; | |||
import com.vaadin.event.UIEvents; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Button.ClickEvent; | |||
import com.vaadin.ui.Button.ClickListener; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.CssLayout; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.TextArea; | |||
import com.vaadin.ui.TextField; | |||
/** | |||
* Ticket #14080 | |||
* | |||
* - The bug happen on push event.<br/> | |||
* - The changes in the DOM are css related.<br/> | |||
* - It seems like when the class attribute is set on push, the textarea revert | |||
* to the height defined by the rows attribute.<br/> | |||
* - The size is reseted on onStateChange where the size is set to the one from | |||
* the state. And it's because, when the user changes the text, at the next poll | |||
* the state will confirm the change of the text, but the width and height | |||
* didn't change in the state either client or server before the fix. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public class TextAreaSizeResetted extends AbstractTestUI { | |||
public static final int TEXTAREAHEIGHT = 200; | |||
public static final int TEXTAREAWIDTH = 200; | |||
CssLayout layout = new CssLayout() { | |||
@Override | |||
protected String getCss(Component c) { | |||
if (c instanceof TextArea) { | |||
return "resize:both"; | |||
} | |||
return super.getCss(c); | |||
} | |||
}; | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
setPollInterval(500); // Short polling like 100ms jams up the TestBench | |||
// waitForVaadin -functionality. | |||
final Label pollIndicator = new Label(); | |||
pollIndicator.setId("pollIndicator"); | |||
final TextField textField = new TextField("height"); | |||
final TextArea textArea = new TextArea(); | |||
textArea.setHeight(TEXTAREAHEIGHT + "px"); | |||
textArea.setWidth(TEXTAREAWIDTH + "px"); | |||
textArea.setValue("This is a text."); | |||
Button button = new Button("Change Height", new ClickListener() { | |||
@Override | |||
public void buttonClick(ClickEvent event) { | |||
textArea.setHeight(textField.getValue()); | |||
} | |||
}); | |||
addComponent(layout); | |||
layout.addComponent(textArea); | |||
layout.addComponent(textField); | |||
layout.addComponent(button); | |||
layout.addComponent(pollIndicator); | |||
addPollListener(new UIEvents.PollListener() { | |||
@Override | |||
public void poll(UIEvents.PollEvent event) { | |||
pollIndicator.setValue(String.valueOf(System | |||
.currentTimeMillis())); | |||
} | |||
}); | |||
} | |||
@Override | |||
protected String getTestDescription() { | |||
return "TextArea width/height change when user resize it, change the text then a poll come."; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 14080; | |||
} | |||
} |
@@ -0,0 +1,127 @@ | |||
package com.vaadin.tests.components.textarea; | |||
import static com.vaadin.tests.components.textarea.TextAreaSizeResetted.TEXTAREAHEIGHT; | |||
import static com.vaadin.tests.components.textarea.TextAreaSizeResetted.TEXTAREAWIDTH; | |||
import static org.hamcrest.CoreMatchers.is; | |||
import static org.junit.Assert.assertThat; | |||
import java.util.List; | |||
import org.junit.Test; | |||
import org.openqa.selenium.Dimension; | |||
import org.openqa.selenium.WebDriver; | |||
import org.openqa.selenium.interactions.Actions; | |||
import org.openqa.selenium.remote.DesiredCapabilities; | |||
import org.openqa.selenium.support.ui.ExpectedCondition; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.testbench.elements.TextAreaElement; | |||
import com.vaadin.testbench.elements.TextFieldElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public class TextAreaSizeResettedTest extends MultiBrowserTest { | |||
private final int OFFSET = 100; | |||
@Override | |||
public void setup() throws Exception { | |||
super.setup(); | |||
openTestURL(); | |||
} | |||
@Override | |||
public List<DesiredCapabilities> getBrowsersToTest() { | |||
return getBrowsersExcludingIE(); // IE8-11 don't support CSS resize. | |||
} | |||
@Test | |||
public void textAreaIsNotResizedOnBlur() { | |||
resizeAndAssertTextAreaTo(TEXTAREAHEIGHT, OFFSET); | |||
getTextArea().sendKeys("foo"); | |||
moveFocusOutsideTextArea(); | |||
// We can't use a waitUntil to check the text area size here, because it | |||
// won't release the focus from | |||
// the text area, so we need to do use something else. This workaround | |||
// uses a label which is updated to indicate | |||
// polling, which should trigger a resize. | |||
waitUntilPollingOccurs(); | |||
assertThat(getTextAreaHeight(), is(TEXTAREAHEIGHT + OFFSET)); | |||
assertThat(getTextAreaWidth(), is(TEXTAREAWIDTH + OFFSET)); | |||
waitUntilPollingOccurs(); | |||
} | |||
private void moveFocusOutsideTextArea() { | |||
$(TextFieldElement.class).first().focus(); | |||
} | |||
private void resizeAndAssertTextAreaTo(int size, int offset) { | |||
// Sanity check | |||
assertThat(getTextAreaHeight(), is(size)); | |||
resizeTextAreaBy(offset); | |||
assertThat(getTextAreaHeight(), is(size + offset)); | |||
} | |||
private void resizeTextAreaBy(int offset) { | |||
int resizeHandlerOffset = 10; | |||
new Actions(getDriver()) | |||
.moveToElement(getTextArea(), | |||
TEXTAREAWIDTH - resizeHandlerOffset, | |||
TEXTAREAHEIGHT - resizeHandlerOffset).clickAndHold() | |||
.moveByOffset(offset, offset).release().build().perform(); | |||
} | |||
@Test | |||
public void textAreaWidthIsPresevedOnHeightResize() { | |||
resizeAndAssertTextAreaTo(TEXTAREAHEIGHT, OFFSET); | |||
changeHeightTo(TEXTAREAHEIGHT + OFFSET + OFFSET); | |||
assertThat(getTextAreaWidth(), is(TEXTAREAWIDTH + OFFSET)); | |||
assertThat(getTextAreaHeight(), is(TEXTAREAHEIGHT + OFFSET + OFFSET)); | |||
} | |||
private void changeHeightTo(int offset) { | |||
$(TextFieldElement.class).first().sendKeys(String.valueOf(offset)); | |||
$(ButtonElement.class).first().click(); | |||
} | |||
private void waitUntilPollingOccurs() { | |||
final String timestamp = getPollTimestamp(); | |||
waitUntil(new ExpectedCondition<Boolean>() { | |||
@Override | |||
public Boolean apply(WebDriver input) { | |||
return !timestamp.equals(getPollTimestamp()); | |||
} | |||
}); | |||
} | |||
private String getPollTimestamp() { | |||
return $(LabelElement.class).id("pollIndicator").getText(); | |||
} | |||
private int getTextAreaHeight() { | |||
return getTextAreaSize().getHeight(); | |||
} | |||
private int getTextAreaWidth() { | |||
return getTextAreaSize().getWidth(); | |||
} | |||
private Dimension getTextAreaSize() { | |||
return getTextArea().getSize(); | |||
} | |||
private TextAreaElement getTextArea() { | |||
return $(TextAreaElement.class).first(); | |||
} | |||
} |