--- /dev/null
+ /*
+ * Copyright 2011 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;
+
+ import java.util.ArrayList;
+ import java.util.Iterator;
+ import java.util.List;
+
+ import com.google.gwt.user.client.DOM;
+ import com.google.gwt.user.client.Element;
+ import com.google.gwt.user.client.ui.HasWidgets;
+ import com.google.gwt.user.client.ui.RootPanel;
+ import com.google.gwt.user.client.ui.Widget;
+ import com.vaadin.client.ui.SubPartAware;
+ import com.vaadin.client.ui.UI.VUI;
+ import com.vaadin.client.ui.gridlayout.VGridLayout;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout;
+ import com.vaadin.client.ui.orderedlayout.VMeasuringOrderedLayout;
+ import com.vaadin.client.ui.tabsheet.VTabsheetPanel;
+ import com.vaadin.client.ui.window.VWindow;
+ import com.vaadin.client.ui.window.WindowConnector;
+ import com.vaadin.shared.ComponentState;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.communication.SharedState;
+
+ /**
+ * ComponentLocator provides methods for generating a String locator for a given
+ * DOM element and for locating a DOM element using a String locator.
+ */
+ public class ComponentLocator {
+
+ /**
+ * Separator used in the String locator between a parent and a child widget.
+ */
+ private static final String PARENTCHILD_SEPARATOR = "/";
+
+ /**
+ * Separator used in the String locator between the part identifying the
+ * containing widget and the part identifying the target element within the
+ * widget.
+ */
+ private static final String SUBPART_SEPARATOR = "#";
+
+ /**
+ * String that identifies the root panel when appearing first in the String
+ * locator.
+ */
+ private static final String ROOT_ID = "Root";
+
+ /**
+ * Reference to ApplicationConnection instance.
+ */
+ private ApplicationConnection client;
+
+ /**
+ * Construct a ComponentLocator for the given ApplicationConnection.
+ *
+ * @param client
+ * ApplicationConnection instance for the application.
+ */
+ public ComponentLocator(ApplicationConnection client) {
+ this.client = client;
+ }
+
+ /**
+ * Generates a String locator which uniquely identifies the target element.
+ * The {@link #getElementByPath(String)} method can be used for the inverse
+ * operation, i.e. locating an element based on the return value from this
+ * method.
+ * <p>
+ * Note that getElementByPath(getPathForElement(element)) == element is not
+ * always true as {@link #getPathForElement(Element)} can return a path to
+ * another element if the widget determines an action on the other element
+ * will give the same result as the action on the target element.
+ * </p>
+ *
+ * @since 5.4
+ * @param targetElement
+ * The element to generate a path for.
+ * @return A String locator that identifies the target element or null if a
+ * String locator could not be created.
+ */
+ public String getPathForElement(Element targetElement) {
+ String pid = null;
+
+ Element e = targetElement;
+
+ while (true) {
+ pid = ConnectorMap.get(client).getConnectorId(e);
+ if (pid != null) {
+ break;
+ }
+
+ e = DOM.getParent(e);
+ if (e == null) {
+ break;
+ }
+ }
+
+ Widget w = null;
+ if (pid != null) {
+ // If we found a Paintable then we use that as reference. We should
+ // find the Paintable for all but very special cases (like
+ // overlays).
+ w = ((ComponentConnector) ConnectorMap.get(client)
+ .getConnector(pid)).getWidget();
+
+ /*
+ * Still if the Paintable contains a widget that implements
+ * SubPartAware, we want to use that as a reference
+ */
+ Widget targetParent = findParentWidget(targetElement, w);
+ while (targetParent != w && targetParent != null) {
+ if (targetParent instanceof SubPartAware) {
+ /*
+ * The targetParent widget is a child of the Paintable and
+ * the first parent (of the targetElement) that implements
+ * SubPartAware
+ */
+ w = targetParent;
+ break;
+ }
+ targetParent = targetParent.getParent();
+ }
+ }
+ if (w == null) {
+ // Check if the element is part of a widget that is attached
+ // directly to the root panel
+ RootPanel rootPanel = RootPanel.get();
+ int rootWidgetCount = rootPanel.getWidgetCount();
+ for (int i = 0; i < rootWidgetCount; i++) {
+ Widget rootWidget = rootPanel.getWidget(i);
+ if (rootWidget.getElement().isOrHasChild(targetElement)) {
+ // The target element is contained by this root widget
+ w = findParentWidget(targetElement, rootWidget);
+ break;
+ }
+ }
+ if (w != null) {
+ // We found a widget but we should still see if we find a
+ // SubPartAware implementor (we cannot find the Paintable as
+ // there is no link from VOverlay to its paintable/owner).
+ Widget subPartAwareWidget = findSubPartAwareParentWidget(w);
+ if (subPartAwareWidget != null) {
+ w = subPartAwareWidget;
+ }
+ }
+ }
+
+ if (w == null) {
+ // Containing widget not found
+ return null;
+ }
+
+ // Determine the path for the target widget
+ String path = getPathForWidget(w);
+ if (path == null) {
+ /*
+ * No path could be determined for the target widget. Cannot create
+ * a locator string.
+ */
+ return null;
+ }
+
+ if (w.getElement() == targetElement) {
+ /*
+ * We are done if the target element is the root of the target
+ * widget.
+ */
+ return path;
+ } else if (w instanceof SubPartAware) {
+ /*
+ * If the widget can provide an identifier for the targetElement we
+ * let it do that
+ */
+ String elementLocator = ((SubPartAware) w)
+ .getSubPartName(targetElement);
+ if (elementLocator != null) {
+ return path + SUBPART_SEPARATOR + elementLocator;
+ }
+ }
+ /*
+ * If everything else fails we use the DOM path to identify the target
+ * element
+ */
+ return path + getDOMPathForElement(targetElement, w.getElement());
+ }
+
+ /**
+ * Finds the first widget in the hierarchy (moving upwards) that implements
+ * SubPartAware. Returns the SubPartAware implementor or null if none is
+ * found.
+ *
+ * @param w
+ * The widget to start from. This is returned if it implements
+ * SubPartAware.
+ * @return The first widget (upwards in hierarchy) that implements
+ * SubPartAware or null
+ */
+ private Widget findSubPartAwareParentWidget(Widget w) {
+
+ while (w != null) {
+ if (w instanceof SubPartAware) {
+ return w;
+ }
+ w = w.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the first widget found when going from {@code targetElement}
+ * upwards in the DOM hierarchy, assuming that {@code ancestorWidget} is a
+ * parent of {@code targetElement}.
+ *
+ * @param targetElement
+ * @param ancestorWidget
+ * @return The widget whose root element is a parent of
+ * {@code targetElement}.
+ */
+ private Widget findParentWidget(Element targetElement, Widget ancestorWidget) {
+ /*
+ * As we cannot resolve Widgets from the element we start from the
+ * widget and move downwards to the correct child widget, as long as we
+ * find one.
+ */
+ if (ancestorWidget instanceof HasWidgets) {
+ for (Widget w : ((HasWidgets) ancestorWidget)) {
+ if (w.getElement().isOrHasChild(targetElement)) {
+ return findParentWidget(targetElement, w);
+ }
+ }
+ }
+
+ // No children found, this is it
+ return ancestorWidget;
+ }
+
+ /**
+ * Locates an element based on a DOM path and a base element.
+ *
+ * @param baseElement
+ * The base element which the path is relative to
+ * @param path
+ * String locator (consisting of domChild[x] parts) that
+ * identifies the element
+ * @return The element identified by path, relative to baseElement or null
+ * if the element could not be found.
+ */
+ private Element getElementByDOMPath(Element baseElement, String path) {
+ String parts[] = path.split(PARENTCHILD_SEPARATOR);
+ Element element = baseElement;
+
+ for (String part : parts) {
+ if (part.startsWith("domChild[")) {
+ String childIndexString = part.substring("domChild[".length(),
+ part.length() - 1);
+ try {
+ int childIndex = Integer.parseInt(childIndexString);
+ element = DOM.getChild(element, childIndex);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
+ return element;
+ }
+
+ /**
+ * Generates a String locator using domChild[x] parts for the element
+ * relative to the baseElement.
+ *
+ * @param element
+ * The target element
+ * @param baseElement
+ * The starting point for the locator. The generated path is
+ * relative to this element.
+ * @return A String locator that can be used to locate the target element
+ * using {@link #getElementByDOMPath(Element, String)} or null if
+ * the locator String cannot be created.
+ */
+ private String getDOMPathForElement(Element element, Element baseElement) {
+ Element e = element;
+ String path = "";
+ while (true) {
+ Element parent = DOM.getParent(e);
+ if (parent == null) {
+ return null;
+ }
+
+ int childIndex = -1;
+
+ int childCount = DOM.getChildCount(parent);
+ for (int i = 0; i < childCount; i++) {
+ if (e == DOM.getChild(parent, i)) {
+ childIndex = i;
+ break;
+ }
+ }
+ if (childIndex == -1) {
+ return null;
+ }
+
+ path = PARENTCHILD_SEPARATOR + "domChild[" + childIndex + "]"
+ + path;
+
+ if (parent == baseElement) {
+ break;
+ }
+
+ e = parent;
+ }
+
+ return path;
+ }
+
+ /**
+ * Locates an element using a String locator (path) which identifies a DOM
+ * element. The {@link #getPathForElement(Element)} method can be used for
+ * the inverse operation, i.e. generating a string expression for a DOM
+ * element.
+ *
+ * @since 5.4
+ * @param path
+ * The String locater which identifies the target element.
+ * @return The DOM element identified by {@code path} or null if the element
+ * could not be located.
+ */
+ public Element getElementByPath(String path) {
+ /*
+ * Path is of type "targetWidgetPath#componentPart" or
+ * "targetWidgetPath".
+ */
+ String parts[] = path.split(SUBPART_SEPARATOR, 2);
+ String widgetPath = parts[0];
+ Widget w = getWidgetFromPath(widgetPath);
+ if (w == null || !Util.isAttachedAndDisplayed(w)) {
+ return null;
+ }
+
+ if (parts.length == 1) {
+ int pos = widgetPath.indexOf("domChild");
+ if (pos == -1) {
+ return w.getElement();
+ }
+
+ // Contains dom reference to a sub element of the widget
+ String subPath = widgetPath.substring(pos);
+ return getElementByDOMPath(w.getElement(), subPath);
+ } else if (parts.length == 2) {
+ if (w instanceof SubPartAware) {
+ return ((SubPartAware) w).getSubPartElement(parts[1]);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a locator String for the given widget. The path can be used to
+ * locate the widget using {@link #getWidgetFromPath(String)}.
+ *
+ * Returns null if no path can be determined for the widget or if the widget
+ * is null.
+ *
+ * @param w
+ * The target widget
+ * @return A String locator for the widget
+ */
+ private String getPathForWidget(Widget w) {
+ if (w == null) {
+ return null;
+ }
+
+ if (w instanceof VUI) {
+ return "";
+ } else if (w instanceof VWindow) {
+ Connector windowConnector = ConnectorMap.get(client)
+ .getConnector(w);
+ List<WindowConnector> subWindowList = client.getRootConnector()
+ .getSubWindows();
+ int indexOfSubWindow = subWindowList.indexOf(windowConnector);
+ return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]";
+ } else if (w instanceof RootPanel) {
+ return ROOT_ID;
+ }
+
+ Widget parent = w.getParent();
+
+ String basePath = getPathForWidget(parent);
+ if (basePath == null) {
+ return null;
+ }
+ String simpleName = Util.getSimpleName(w);
+
+ /*
+ * Check if the parent implements Iterable. At least VPopupView does not
+ * implement HasWdgets so we cannot check for that.
+ */
+ if (!(parent instanceof Iterable<?>)) {
+ // Parent does not implement Iterable so we cannot find out which
+ // child this is
+ return null;
+ }
+
+ Iterator<?> i = ((Iterable<?>) parent).iterator();
+ int pos = 0;
+ while (i.hasNext()) {
+ Object child = i.next();
+ if (child == w) {
+ return basePath + PARENTCHILD_SEPARATOR + simpleName + "["
+ + pos + "]";
+ }
+ String simpleName2 = Util.getSimpleName(child);
+ if (simpleName.equals(simpleName2)) {
+ pos++;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Locates the widget based on a String locator.
+ *
+ * @param path
+ * The String locator that identifies the widget.
+ * @return The Widget identified by the String locator or null if the widget
+ * could not be identified.
+ */
+ private Widget getWidgetFromPath(String path) {
+ Widget w = null;
+ String parts[] = path.split(PARENTCHILD_SEPARATOR);
+
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+
+ if (part.equals(ROOT_ID)) {
+ w = RootPanel.get();
+ } else if (part.equals("")) {
+ w = client.getRootConnector().getWidget();
+ } else if (w == null) {
+ String id = part;
+ // Must be old static pid (PID_S*)
+ ServerConnector connector = ConnectorMap.get(client)
+ .getConnector(id);
+ if (connector == null) {
+ // Lookup by component id
+ // TODO Optimize this
+ connector = findConnectorById(client.getRootConnector(),
+ id.substring(5));
+ }
+
+ if (connector instanceof ComponentConnector) {
+ w = ((ComponentConnector) connector).getWidget();
+ } else {
+ // Not found
+ return null;
+ }
+ } else if (part.startsWith("domChild[")) {
+ // The target widget has been found and the rest identifies the
+ // element
+ break;
+ } else if (w instanceof Iterable) {
+ // W identifies a widget that contains other widgets, as it
+ // should. Try to locate the child
+ Iterable<?> parent = (Iterable<?>) w;
+
+ // Part is of type "VVerticalLayout[0]", split this into
+ // VVerticalLayout and 0
+ String[] split = part.split("\\[", 2);
+ String widgetClassName = split[0];
+ String indexString = split[1];
+ int widgetPosition = Integer.parseInt(indexString.substring(0,
+ indexString.length() - 1));
+
+ // AbsolutePanel in GridLayout has been removed -> skip it
+ if (w instanceof VGridLayout
+ && "AbsolutePanel".equals(widgetClassName)) {
+ continue;
+ }
+
+ if (w instanceof VTabsheetPanel && widgetPosition != 0) {
+ // TabSheetPanel now only contains 1 connector => the index
+ // is always 0 which indicates the widget in the active tab
+ widgetPosition = 0;
+ }
++ if ("VVerticalLayout".equals(widgetClassName)
++ || "VHorizontalLayout".equals(widgetClassName)) {
++ widgetClassName = "VBoxLayout";
++ }
++ if (w instanceof VBoxLayout
++ && "ChildComponentContainer".equals(widgetClassName)) {
++ widgetClassName = "VBoxLayout$Slot";
++ }
+
+ /*
+ * The new grid and ordered layotus do not contain
+ * ChildComponentContainer widgets. This is instead simulated by
+ * constructing a path step that would find the desired widget
+ * from the layout and injecting it as the next search step
+ * (which would originally have found the widget inside the
+ * ChildComponentContainer)
+ */
+ if ((w instanceof VMeasuringOrderedLayout || w instanceof VGridLayout)
+ && "ChildComponentContainer".equals(widgetClassName)
+ && i + 1 < parts.length) {
+
+ HasWidgets layout = (HasWidgets) w;
+
+ String nextPart = parts[i + 1];
+ String[] nextSplit = nextPart.split("\\[", 2);
+ String nextWidgetClassName = nextSplit[0];
+
+ // Find the n:th child and count the number of children with
+ // the same type before it
+ int nextIndex = 0;
+ for (Widget child : layout) {
+ boolean matchingType = nextWidgetClassName.equals(Util
+ .getSimpleName(child));
+ if (matchingType && widgetPosition == 0) {
+ // This is the n:th child that we looked for
+ break;
+ } else if (widgetPosition < 0) {
+ // Error if we're past the desired position without
+ // a match
+ return null;
+ } else if (matchingType) {
+ // If this was another child of the expected type,
+ // increase the count for the next step
+ nextIndex++;
+ }
+
+ // Don't count captions
+ if (!(child instanceof VCaption)) {
+ widgetPosition--;
+ }
+ }
+
+ // Advance to the next step, this time checking for the
+ // actual child widget
+ parts[i + 1] = nextWidgetClassName + '[' + nextIndex + ']';
+ continue;
+ }
+
+ // Locate the child
+ Iterator<? extends Widget> iterator;
+
+ /*
+ * VWindow and VContextMenu workarounds for backwards
+ * compatibility
+ */
+ if (widgetClassName.equals("VWindow")) {
+ List<WindowConnector> windows = client.getRootConnector()
+ .getSubWindows();
+ List<VWindow> windowWidgets = new ArrayList<VWindow>(
+ windows.size());
+ for (WindowConnector wc : windows) {
+ windowWidgets.add(wc.getWidget());
+ }
+ iterator = windowWidgets.iterator();
+ } else if (widgetClassName.equals("VContextMenu")) {
+ return client.getContextMenu();
+ } else {
+ iterator = (Iterator<? extends Widget>) parent.iterator();
+ }
+
+ boolean ok = false;
+
+ // Find the widgetPosition:th child of type "widgetClassName"
+ while (iterator.hasNext()) {
+
+ Widget child = iterator.next();
+ String simpleName2 = Util.getSimpleName(child);
+
+ if (widgetClassName.equals(simpleName2)) {
+ if (widgetPosition == 0) {
+ w = child;
+ ok = true;
+ break;
+ }
+ widgetPosition--;
+ }
+ }
+
+ if (!ok) {
+ // Did not find the child
+ return null;
+ }
+ } else {
+ // W identifies something that is not a "HasWidgets". This
+ // should not happen as all widget containers should implement
+ // HasWidgets.
+ return null;
+ }
+ }
+
+ return w;
+ }
+
+ private ServerConnector findConnectorById(ServerConnector root, String id) {
+ SharedState state = root.getState();
+ if (state instanceof ComponentState
+ && id.equals(((ComponentState) state).getId())) {
+ return root;
+ }
+ for (ServerConnector child : root.getChildren()) {
+ ServerConnector found = findConnectorById(child, id);
+ if (found != null) {
+ return found;
+ }
+ }
+
+ return null;
+ }
+
+ }
--- /dev/null
- if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
- // Remember the original values
- var left = style.left, rsLeft = elem.runtimeStyle.left;
-
- // Put in the new values to get a computed value out
- elem.runtimeStyle.left = cs.left;
- style.left = ret || 0;
- ret = style.pixelLeft + "px";
-
- // Revert the changed values
- style.left = left;
- elem.runtimeStyle.left = rsLeft;
- }
+ /*
+ * Copyright 2011 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;
+
+ import com.google.gwt.core.client.JavaScriptObject;
+ import com.google.gwt.dom.client.Element;
+
+ public class ComputedStyle {
+
+ protected final JavaScriptObject computedStyle;
+ private final Element elem;
+
+ /**
+ * Gets this element's computed style object which can be used to gather
+ * information about the current state of the rendered node.
+ * <p>
+ * Note that this method is expensive. Wherever possible, reuse the returned
+ * object.
+ *
+ * @param elem
+ * the element
+ * @return the computed style
+ */
+ public ComputedStyle(Element elem) {
+ computedStyle = getComputedStyle(elem);
+ this.elem = elem;
+ }
+
+ private static native JavaScriptObject getComputedStyle(Element elem)
+ /*-{
+ if(elem.nodeType != 1) {
+ return {};
+ }
+
+ if($wnd.document.defaultView && $wnd.document.defaultView.getComputedStyle) {
+ return $wnd.document.defaultView.getComputedStyle(elem, null);
+ }
+
+ if(elem.currentStyle) {
+ return elem.currentStyle;
+ }
+ }-*/;
+
+ /**
+ *
+ * @param name
+ * name of the CSS property in camelCase
+ * @return the value of the property, normalized for across browsers (each
+ * browser returns pixel values whenever possible).
+ */
+ public final native String getProperty(String name)
+ /*-{
+ var cs = this.@com.vaadin.client.ComputedStyle::computedStyle;
+ var elem = this.@com.vaadin.client.ComputedStyle::elem;
+
+ // Border values need to be checked separately. The width might have a
+ // meaningful value even if the border style is "none". In that case the
+ // value should be 0.
+ if(name.indexOf("border") > -1 && name.indexOf("Width") > -1) {
+ var borderStyleProp = name.substring(0,name.length-5) + "Style";
+ if(cs.getPropertyValue)
+ var borderStyle = cs.getPropertyValue(borderStyleProp);
+ else // IE
+ var borderStyle = cs[borderStyleProp];
+ if(borderStyle == "none")
+ return "0px";
+ }
+
+ if(cs.getPropertyValue) {
+
+ // Convert name to dashed format
+ name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
+ var ret = cs.getPropertyValue(name);
+
+ } else {
+
+ var ret = cs[name];
+ var style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
++ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
++ // Remember the original values
++ var left = style.left, rsLeft = elem.runtimeStyle.left;
++
++ // Put in the new values to get a computed value out
++ elem.runtimeStyle.left = cs.left;
++ style.left = ret || 0;
++ ret = style.pixelLeft + "px";
++
++ // Revert the changed values
++ style.left = left;
++ elem.runtimeStyle.left = rsLeft;
++ }
+
+ }
+
+ // Normalize margin values. This is not totally valid, but in most cases
+ // it is what the user wants to know.
+ if(name.indexOf("margin") > -1 && ret == "auto") {
+ return "0px";
+ }
+
+ // Some browsers return undefined width and height values as "auto", so
+ // we need to retrieve those ourselves.
+ if (name == "width" && ret == "auto") {
+ ret = elem.clientWidth + "px";
+ } else if (name == "height" && ret == "auto") {
+ ret = elem.clientHeight + "px";
+ }
+
+ return ret;
+
+ }-*/;
+
+ public final int getIntProperty(String name) {
+ Integer parsed = parseInt(getProperty(name));
+ if (parsed != null) {
+ return parsed.intValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Get current margin values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getMargin() {
+ int[] margin = { 0, 0, 0, 0 };
+ margin[0] = getIntProperty("marginTop");
+ margin[1] = getIntProperty("marginRight");
+ margin[2] = getIntProperty("marginBottom");
+ margin[3] = getIntProperty("marginLeft");
+ return margin;
+ }
+
+ /**
+ * Get current padding values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getPadding() {
+ int[] padding = { 0, 0, 0, 0 };
+ padding[0] = getIntProperty("paddingTop");
+ padding[1] = getIntProperty("paddingRight");
+ padding[2] = getIntProperty("paddingBottom");
+ padding[3] = getIntProperty("paddingLeft");
+ return padding;
+ }
+
+ /**
+ * Get current border values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getBorder() {
+ int[] border = { 0, 0, 0, 0 };
+ border[0] = getIntProperty("borderTopWidth");
+ border[1] = getIntProperty("borderRightWidth");
+ border[2] = getIntProperty("borderBottomWidth");
+ border[3] = getIntProperty("borderLeftWidth");
+ return border;
+ }
+
+ /**
+ * Takes a String value e.g. "12px" and parses that to int 12.
+ *
+ * @param String
+ * a value starting with a number
+ * @return int the value from the string before any non-numeric characters.
+ * If the value cannot be parsed to a number, returns
+ * <code>null</code>.
+ */
+ public static native Integer parseInt(final String value)
+ /*-{
+ var number = parseInt(value, 10);
+ if (isNaN(number))
+ return null;
+ else
+ // $entry not needed as function is not exported
+ return @java.lang.Integer::valueOf(I)(number);
+ }-*/;
+
+ }
--- /dev/null
- private void layoutLater() {
+ /*
+ * Copyright 2011 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;
+
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.Map;
+ import java.util.Set;
+
+ import com.google.gwt.core.client.Duration;
+ import com.google.gwt.core.client.JsArrayString;
+ import com.google.gwt.dom.client.Element;
+ import com.google.gwt.dom.client.Style;
+ import com.google.gwt.dom.client.Style.Overflow;
+ import com.google.gwt.user.client.Timer;
+ import com.vaadin.client.MeasuredSize.MeasureResult;
+ import com.vaadin.client.ui.ManagedLayout;
+ import com.vaadin.client.ui.PostLayoutListener;
+ import com.vaadin.client.ui.SimpleManagedLayout;
+ import com.vaadin.client.ui.layout.ElementResizeEvent;
+ import com.vaadin.client.ui.layout.ElementResizeListener;
+ import com.vaadin.client.ui.layout.LayoutDependencyTree;
+ import com.vaadin.client.ui.notification.VNotification;
+
+ public class LayoutManager {
+ private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
+
+ private static final boolean debugLogging = false;
+
+ private ApplicationConnection connection;
+ private final Set<Element> measuredNonConnectorElements = new HashSet<Element>();
+ private final MeasuredSize nullSize = new MeasuredSize();
+
+ private LayoutDependencyTree currentDependencyTree;
+
+ private final Collection<ManagedLayout> needsHorizontalLayout = new HashSet<ManagedLayout>();
+ private final Collection<ManagedLayout> needsVerticalLayout = new HashSet<ManagedLayout>();
+
+ private final Collection<ComponentConnector> needsMeasure = new HashSet<ComponentConnector>();
+
+ private Collection<ComponentConnector> pendingOverflowFixes = new HashSet<ComponentConnector>();
+
+ private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<Element, Collection<ElementResizeListener>>();
+ private final Set<Element> listenersToFire = new HashSet<Element>();
+
+ private boolean layoutPending = false;
+ private Timer layoutTimer = new Timer() {
+ @Override
+ public void run() {
+ cancel();
+ layoutNow();
+ }
+ };
+ private boolean everythingNeedsMeasure = false;
+
+ public void setConnection(ApplicationConnection connection) {
+ if (this.connection != null) {
+ throw new RuntimeException(
+ "LayoutManager connection can never be changed");
+ }
+ this.connection = connection;
+ }
+
+ /**
+ * Gets the layout manager associated with the given
+ * {@link ApplicationConnection}.
+ *
+ * @param connection
+ * the application connection to get a layout manager for
+ * @return the layout manager associated with the provided application
+ * connection
+ */
+ public static LayoutManager get(ApplicationConnection connection) {
+ return connection.getLayoutManager();
+ }
+
+ /**
+ * Registers that a ManagedLayout is depending on the size of an Element.
+ * This causes this layout manager to measure the element in the beginning
+ * of every layout phase and call the appropriate layout method of the
+ * managed layout if the size of the element has changed.
+ *
+ * @param owner
+ * the ManagedLayout that depends on an element
+ * @param element
+ * the Element that should be measured
+ */
+ public void registerDependency(ManagedLayout owner, Element element) {
+ MeasuredSize measuredSize = ensureMeasured(element);
+ setNeedsLayout(owner);
+ measuredSize.addDependent(owner.getConnectorId());
+ }
+
+ private MeasuredSize ensureMeasured(Element element) {
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ measuredSize = new MeasuredSize();
+
+ if (ConnectorMap.get(connection).getConnector(element) == null) {
+ measuredNonConnectorElements.add(element);
+ }
+ setMeasuredSize(element, measuredSize);
+ }
+ return measuredSize;
+ }
+
+ private boolean needsMeasure(Element e) {
+ if (connection.getConnectorMap().getConnectorId(e) != null) {
+ return true;
+ } else if (elementResizeListeners.containsKey(e)) {
+ return true;
+ } else if (getMeasuredSize(e, nullSize).hasDependents()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Assigns a measured size to an element. Method defined as protected to
+ * allow separate implementation for IE8.
+ *
+ * @param element
+ * the dom element to attach the measured size to
+ * @param measuredSize
+ * the measured size to attach to the element. If
+ * <code>null</code>, any previous measured size is removed.
+ */
+ protected native void setMeasuredSize(Element element,
+ MeasuredSize measuredSize)
+ /*-{
+ if (measuredSize) {
+ element.vMeasuredSize = measuredSize;
+ } else {
+ delete element.vMeasuredSize;
+ }
+ }-*/;
+
+ /**
+ * Gets the measured size for an element. Method defined as protected to
+ * allow separate implementation for IE8.
+ *
+ * @param element
+ * The element to get measured size for
+ * @param defaultSize
+ * The size to return if no measured size could be found
+ * @return The measured size for the element or {@literal defaultSize}
+ */
+ protected native MeasuredSize getMeasuredSize(Element element,
+ MeasuredSize defaultSize)
+ /*-{
+ return element.vMeasuredSize || defaultSize;
+ }-*/;
+
+ private final MeasuredSize getMeasuredSize(ComponentConnector connector) {
+ Element element = connector.getWidget().getElement();
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ measuredSize = new MeasuredSize();
+ setMeasuredSize(element, measuredSize);
+ }
+ return measuredSize;
+ }
+
+ /**
+ * Registers that a ManagedLayout is no longer depending on the size of an
+ * Element.
+ *
+ * @see #registerDependency(ManagedLayout, Element)
+ *
+ * @param owner
+ * the ManagedLayout no longer depends on an element
+ * @param element
+ * the Element that that no longer needs to be measured
+ */
+ public void unregisterDependency(ManagedLayout owner, Element element) {
+ MeasuredSize measuredSize = getMeasuredSize(element, null);
+ if (measuredSize == null) {
+ return;
+ }
+ measuredSize.removeDependent(owner.getConnectorId());
+ stopMeasuringIfUnecessary(element);
+ }
+
+ public boolean isLayoutRunning() {
+ return currentDependencyTree != null;
+ }
+
+ private void countLayout(Map<ManagedLayout, Integer> layoutCounts,
+ ManagedLayout layout) {
+ Integer count = layoutCounts.get(layout);
+ if (count == null) {
+ count = Integer.valueOf(0);
+ } else {
+ count = Integer.valueOf(count.intValue() + 1);
+ }
+ layoutCounts.put(layout, count);
+ if (count.intValue() > 2) {
+ VConsole.error(Util.getConnectorString(layout)
+ + " has been layouted " + count.intValue() + " times");
+ }
+ }
+
++ public void layoutLater() {
+ if (!layoutPending) {
+ layoutPending = true;
+ layoutTimer.schedule(100);
+ }
+ }
+
+ public void layoutNow() {
+ if (isLayoutRunning()) {
+ throw new IllegalStateException(
+ "Can't start a new layout phase before the previous layout phase ends.");
+ }
+ layoutPending = false;
+ try {
+ currentDependencyTree = new LayoutDependencyTree();
+ doLayout();
+ } finally {
+ currentDependencyTree = null;
+ }
+ }
+
+ private void doLayout() {
+ VConsole.log("Starting layout phase");
+
+ Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>();
+
+ int passes = 0;
+ Duration totalDuration = new Duration();
+
+ for (ManagedLayout layout : needsHorizontalLayout) {
+ currentDependencyTree.setNeedsHorizontalLayout(layout, true);
+ }
+ for (ManagedLayout layout : needsVerticalLayout) {
+ currentDependencyTree.setNeedsVerticalLayout(layout, true);
+ }
+ needsHorizontalLayout.clear();
+ needsVerticalLayout.clear();
+
+ for (ComponentConnector connector : needsMeasure) {
+ currentDependencyTree.setNeedsMeasure(connector, true);
+ }
+ needsMeasure.clear();
+
+ measureNonConnectors();
+
+ VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms");
+
+ while (true) {
+ Duration passDuration = new Duration();
+ passes++;
+
+ int measuredConnectorCount = measureConnectors(
+ currentDependencyTree, everythingNeedsMeasure);
+ everythingNeedsMeasure = false;
+ if (measuredConnectorCount == 0) {
+ VConsole.log("No more changes in pass " + passes);
+ break;
+ }
+
+ int measureTime = passDuration.elapsedMillis();
+ VConsole.log(" Measured " + measuredConnectorCount
+ + " elements in " + measureTime + " ms");
+
+ if (!listenersToFire.isEmpty()) {
+ for (Element element : listenersToFire) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ ElementResizeListener[] array = listeners
+ .toArray(new ElementResizeListener[listeners.size()]);
+ ElementResizeEvent event = new ElementResizeEvent(this,
+ element);
+ for (ElementResizeListener listener : array) {
+ try {
+ listener.onElementResize(event);
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ }
+ }
+ int measureListenerTime = passDuration.elapsedMillis();
+ VConsole.log(" Fired resize listeners for "
+ + listenersToFire.size() + " elements in "
+ + (measureListenerTime - measureTime) + " ms");
+ measureTime = measuredConnectorCount;
+ listenersToFire.clear();
+ }
+
+ FastStringSet updatedSet = FastStringSet.create();
+
+ while (currentDependencyTree.hasHorizontalConnectorToLayout()
+ || currentDependencyTree.hasVerticaConnectorToLayout()) {
+ for (ManagedLayout layout : currentDependencyTree
+ .getHorizontalLayoutTargets()) {
+ if (layout instanceof DirectionalManagedLayout) {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+ try {
+ cl.layoutHorizontally();
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ countLayout(layoutCounts, cl);
+ } else {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+ try {
+ rr.layout();
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ countLayout(layoutCounts, rr);
+ }
+ if (debugLogging) {
+ updatedSet.add(layout.getConnectorId());
+ }
+ }
+
+ for (ManagedLayout layout : currentDependencyTree
+ .getVerticalLayoutTargets()) {
+ if (layout instanceof DirectionalManagedLayout) {
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+ try {
+ cl.layoutVertically();
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ countLayout(layoutCounts, cl);
+ } else {
+ currentDependencyTree
+ .markAsHorizontallyLayouted(layout);
+ currentDependencyTree.markAsVerticallyLayouted(layout);
+ SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+ try {
+ rr.layout();
+ } catch (RuntimeException e) {
+ VConsole.error(e);
+ }
+ countLayout(layoutCounts, rr);
+ }
+ if (debugLogging) {
+ updatedSet.add(layout.getConnectorId());
+ }
+ }
+ }
+
+ if (debugLogging) {
+ JsArrayString changedCids = updatedSet.dump();
+
+ StringBuilder b = new StringBuilder(" ");
+ b.append(changedCids.length());
+ b.append(" requestLayout invocations in ");
+ b.append(passDuration.elapsedMillis() - measureTime);
+ b.append(" ms");
+ if (changedCids.length() < 30) {
+ for (int i = 0; i < changedCids.length(); i++) {
+ if (i != 0) {
+ b.append(", ");
+ } else {
+ b.append(": ");
+ }
+ String connectorString = changedCids.get(i);
+ if (changedCids.length() < 10) {
+ ServerConnector connector = ConnectorMap.get(
+ connection).getConnector(connectorString);
+ connectorString = Util
+ .getConnectorString(connector);
+ }
+ b.append(connectorString);
+ }
+ }
+ VConsole.log(b.toString());
+ }
+
+ VConsole.log("Pass " + passes + " completed in "
+ + passDuration.elapsedMillis() + " ms");
+
+ if (passes > 100) {
+ VConsole.log(LOOP_ABORT_MESSAGE);
+ VNotification.createNotification(VNotification.DELAY_FOREVER)
+ .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
+ "error");
+ break;
+ }
+ }
+
+ int postLayoutStart = totalDuration.elapsedMillis();
+ for (ComponentConnector connector : connection.getConnectorMap()
+ .getComponentConnectors()) {
+ if (connector instanceof PostLayoutListener) {
+ ((PostLayoutListener) connector).postLayout();
+ }
+ }
+ int postLayoutDone = (totalDuration.elapsedMillis() - postLayoutStart);
+ VConsole.log("Invoke post layout listeners in " + postLayoutDone
+ + " ms");
+
+ cleanMeasuredSizes();
+ int cleaningDone = (totalDuration.elapsedMillis() - postLayoutDone);
+ VConsole.log("Cleaned old measured sizes in " + cleaningDone + "ms");
+
+ VConsole.log("Total layout phase time: "
+ + totalDuration.elapsedMillis() + "ms");
+ }
+
+ private void logConnectorStatus(int connectorId) {
+ currentDependencyTree
+ .logDependencyStatus((ComponentConnector) ConnectorMap.get(
+ connection).getConnector(Integer.toString(connectorId)));
+ }
+
+ private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
+ boolean measureAll) {
+ if (!pendingOverflowFixes.isEmpty()) {
+ Duration duration = new Duration();
+
+ HashMap<Element, String> originalOverflows = new HashMap<Element, String>();
+
+ HashSet<ComponentConnector> delayedOverflowFixes = new HashSet<ComponentConnector>();
+
+ // First set overflow to hidden (and save previous value so it can
+ // be restored later)
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ // Delay the overflow fix if the involved connectors might still
+ // change
+ boolean connectorChangesExpected = !currentDependencyTree
+ .noMoreChangesExpected(componentConnector);
+ boolean parentChangesExcpected = componentConnector.getParent() instanceof ComponentConnector
+ && !currentDependencyTree
+ .noMoreChangesExpected((ComponentConnector) componentConnector
+ .getParent());
+ if (connectorChangesExpected || parentChangesExcpected) {
+ delayedOverflowFixes.add(componentConnector);
+ continue;
+ }
+
+ if (debugLogging) {
+ VConsole.log("Doing overflow fix for "
+ + Util.getConnectorString(componentConnector)
+ + " in "
+ + Util.getConnectorString(componentConnector
+ .getParent()));
+ }
+
+ Element parentElement = componentConnector.getWidget()
+ .getElement().getParentElement();
+ Style style = parentElement.getStyle();
+ String originalOverflow = style.getOverflow();
+
+ if (originalOverflow != null
+ && !originalOverflows.containsKey(parentElement)) {
+ // Store original value for restore, but only the first time
+ // the value is changed
+ originalOverflows.put(parentElement, originalOverflow);
+ }
+
+ style.setOverflow(Overflow.HIDDEN);
+ }
+
+ pendingOverflowFixes.removeAll(delayedOverflowFixes);
+
+ // Then ensure all scrolling elements are reflowed by measuring
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ componentConnector.getWidget().getElement().getParentElement()
+ .getOffsetHeight();
+ }
+
+ // Finally restore old overflow value and update bookkeeping
+ for (ComponentConnector componentConnector : pendingOverflowFixes) {
+ Element parentElement = componentConnector.getWidget()
+ .getElement().getParentElement();
+ parentElement.getStyle().setProperty("overflow",
+ originalOverflows.get(parentElement));
+
+ layoutDependencyTree.setNeedsMeasure(componentConnector, true);
+ }
+ if (!pendingOverflowFixes.isEmpty()) {
+ VConsole.log("Did overflow fix for "
+ + pendingOverflowFixes.size() + " elements in "
+ + duration.elapsedMillis() + " ms");
+ }
+ pendingOverflowFixes = delayedOverflowFixes;
+ }
+
+ int measureCount = 0;
+ if (measureAll) {
+ ComponentConnector[] connectors = ConnectorMap.get(connection)
+ .getComponentConnectors();
+ for (ComponentConnector connector : connectors) {
+ measureConnector(connector);
+ }
+ for (ComponentConnector connector : connectors) {
+ layoutDependencyTree.setNeedsMeasure(connector, false);
+ }
+ measureCount += connectors.length;
+ }
+
+ while (layoutDependencyTree.hasConnectorsToMeasure()) {
+ Collection<ComponentConnector> measureTargets = layoutDependencyTree
+ .getMeasureTargets();
+ for (ComponentConnector connector : measureTargets) {
+ measureConnector(connector);
+ measureCount++;
+ }
+ for (ComponentConnector connector : measureTargets) {
+ layoutDependencyTree.setNeedsMeasure(connector, false);
+ }
+ }
+ return measureCount;
+ }
+
+ private void measureConnector(ComponentConnector connector) {
+ Element element = connector.getWidget().getElement();
+ MeasuredSize measuredSize = getMeasuredSize(connector);
+ MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
+
+ if (measureResult.isChanged()) {
+ onConnectorChange(connector, measureResult.isWidthChanged(),
+ measureResult.isHeightChanged());
+ }
+ }
+
+ private void onConnectorChange(ComponentConnector connector,
+ boolean widthChanged, boolean heightChanged) {
+ setNeedsOverflowFix(connector);
+ if (heightChanged) {
+ currentDependencyTree.markHeightAsChanged(connector);
+ }
+ if (widthChanged) {
+ currentDependencyTree.markWidthAsChanged(connector);
+ }
+ }
+
+ private void setNeedsOverflowFix(ComponentConnector connector) {
+ // IE9 doesn't need the original fix, but for some reason it needs this
+ if (BrowserInfo.get().requiresOverflowAutoFix()
+ || BrowserInfo.get().isIE9()) {
+ ComponentConnector scrollingBoundary = currentDependencyTree
+ .getScrollingBoundary(connector);
+ if (scrollingBoundary != null) {
+ pendingOverflowFixes.add(scrollingBoundary);
+ }
+ }
+ }
+
+ private void measureNonConnectors() {
+ for (Element element : measuredNonConnectorElements) {
+ measuredAndUpdate(element, getMeasuredSize(element, null));
+ }
+ VConsole.log("Measured " + measuredNonConnectorElements.size()
+ + " non connector elements");
+ }
+
+ private MeasureResult measuredAndUpdate(Element element,
+ MeasuredSize measuredSize) {
+ MeasureResult measureResult = measuredSize.measure(element);
+ if (measureResult.isChanged()) {
+ notifyListenersAndDepdendents(element,
+ measureResult.isWidthChanged(),
+ measureResult.isHeightChanged());
+ }
+ return measureResult;
+ }
+
+ private void notifyListenersAndDepdendents(Element element,
+ boolean widthChanged, boolean heightChanged) {
+ assert widthChanged || heightChanged;
+
+ MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
+ JsArrayString dependents = measuredSize.getDependents();
+ for (int i = 0; i < dependents.length(); i++) {
+ String pid = dependents.get(i);
+ ManagedLayout dependent = (ManagedLayout) connection
+ .getConnectorMap().getConnector(pid);
+ if (dependent != null) {
+ if (heightChanged) {
+ currentDependencyTree.setNeedsVerticalLayout(dependent,
+ true);
+ }
+ if (widthChanged) {
+ currentDependencyTree.setNeedsHorizontalLayout(dependent,
+ true);
+ }
+ }
+ }
+ if (elementResizeListeners.containsKey(element)) {
+ listenersToFire.add(element);
+ }
+ }
+
+ private static boolean isManagedLayout(ComponentConnector connector) {
+ return connector instanceof ManagedLayout;
+ }
+
+ public void forceLayout() {
+ ConnectorMap connectorMap = connection.getConnectorMap();
+ ComponentConnector[] componentConnectors = connectorMap
+ .getComponentConnectors();
+ for (ComponentConnector connector : componentConnectors) {
+ if (connector instanceof ManagedLayout) {
+ setNeedsLayout((ManagedLayout) connector);
+ }
+ }
+ setEverythingNeedsMeasure();
+ layoutNow();
+ }
+
+ /**
+ * Marks that a ManagedLayout should be layouted in the next layout phase
+ * even if none of the elements managed by the layout have been resized.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsLayout(ManagedLayout layout) {
+ setNeedsHorizontalLayout(layout);
+ setNeedsVerticalLayout(layout);
+ }
+
+ /**
+ * Marks that a ManagedLayout should be layouted horizontally in the next
+ * layout phase even if none of the elements managed by the layout have been
+ * resized horizontally.
+ *
+ * For SimpleManagedLayout which is always layouted in both directions, this
+ * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsHorizontalLayout(ManagedLayout layout) {
+ needsHorizontalLayout.add(layout);
+ }
+
+ /**
+ * Marks that a ManagedLayout should be layouted vertically in the next
+ * layout phase even if none of the elements managed by the layout have been
+ * resized vertically.
+ *
+ * For SimpleManagedLayout which is always layouted in both directions, this
+ * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+ *
+ * @param layout
+ * the managed layout that should be layouted
+ */
+ public final void setNeedsVerticalLayout(ManagedLayout layout) {
+ needsVerticalLayout.add(layout);
+ }
+
+ /**
+ * Gets the outer height (including margins, paddings and borders) of the
+ * given element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * -1 is returned if the element has not been measured. If 0 is returned, it
+ * might indicate that the element is not attached to the DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured outer height (including margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getOuterHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getOuterHeight();
+ }
+
+ /**
+ * Gets the outer width (including margins, paddings and borders) of the
+ * given element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * -1 is returned if the element has not been measured. If 0 is returned, it
+ * might indicate that the element is not attached to the DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured outer width (including margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getOuterWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getOuterWidth();
+ }
+
+ /**
+ * Gets the inner height (excluding margins, paddings and borders) of the
+ * given element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * -1 is returned if the element has not been measured. If 0 is returned, it
+ * might indicate that the element is not attached to the DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured inner height (excluding margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getInnerHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getInnerHeight();
+ }
+
+ /**
+ * Gets the inner width (excluding margins, paddings and borders) of the
+ * given element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * -1 is returned if the element has not been measured. If 0 is returned, it
+ * might indicate that the element is not attached to the DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured inner width (excluding margins, paddings and
+ * borders) of the element in pixels.
+ */
+ public final int getInnerWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getInnerWidth();
+ }
+
+ /**
+ * Gets the border height (top border + bottom border) of the given element,
+ * provided that it has been measured. These elements are guaranteed to be
+ * measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured border height (top border + bottom border) of the
+ * element in pixels.
+ */
+ public final int getBorderHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getBorderHeight();
+ }
+
+ /**
+ * Gets the padding height (top padding + bottom padding) of the given
+ * element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured padding height (top padding + bottom padding) of the
+ * element in pixels.
+ */
+ public int getPaddingHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingHeight();
+ }
+
+ /**
+ * Gets the border width (left border + right border) of the given element,
+ * provided that it has been measured. These elements are guaranteed to be
+ * measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured border width (left border + right border) of the
+ * element in pixels.
+ */
+ public int getBorderWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getBorderWidth();
+ }
+
+ /**
+ * Gets the padding width (left padding + right padding) of the given
+ * element, provided that it has been measured. These elements are
+ * guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured padding width (left padding + right padding) of the
+ * element in pixels.
+ */
+ public int getPaddingWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingWidth();
+ }
+
+ /**
+ * Gets the top padding of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured top padding of the element in pixels.
+ */
+ public int getPaddingTop(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingTop();
+ }
+
+ /**
+ * Gets the left padding of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured left padding of the element in pixels.
+ */
+ public int getPaddingLeft(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingLeft();
+ }
+
+ /**
+ * Gets the bottom padding of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured bottom padding of the element in pixels.
+ */
+ public int getPaddingBottom(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingBottom();
+ }
+
+ /**
+ * Gets the right padding of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured right padding of the element in pixels.
+ */
+ public int getPaddingRight(Element element) {
+ return getMeasuredSize(element, nullSize).getPaddingRight();
+ }
+
+ /**
+ * Gets the top margin of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured top margin of the element in pixels.
+ */
+ public int getMarginTop(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginTop();
+ }
+
+ /**
+ * Gets the right margin of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured right margin of the element in pixels.
+ */
+ public int getMarginRight(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginRight();
+ }
+
+ /**
+ * Gets the bottom margin of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured bottom margin of the element in pixels.
+ */
+ public int getMarginBottom(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginBottom();
+ }
+
+ /**
+ * Gets the left margin of the given element, provided that it has been
+ * measured. These elements are guaranteed to be measured:
+ * <ul>
+ * <li>ManagedLayotus and their child Connectors
+ * <li>Elements for which there is at least one ElementResizeListener
+ * <li>Elements for which at least one ManagedLayout has registered a
+ * dependency
+ * </ul>
+ *
+ * A negative number is returned if the element has not been measured. If 0
+ * is returned, it might indicate that the element is not attached to the
+ * DOM.
+ *
+ * @param element
+ * the element to get the measured size for
+ * @return the measured left margin of the element in pixels.
+ */
+ public int getMarginLeft(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginLeft();
+ }
+
++ /**
++ * Gets the combined top & bottom margin of the given element, provided that
++ * they have been measured. These elements are guaranteed to be measured:
++ * <ul>
++ * <li>ManagedLayotus and their child Connectors
++ * <li>Elements for which there is at least one ElementResizeListener
++ * <li>Elements for which at least one ManagedLayout has registered a
++ * dependency
++ * </ul>
++ *
++ * A negative number is returned if the element has not been measured. If 0
++ * is returned, it might indicate that the element is not attached to the
++ * DOM.
++ *
++ * @param element
++ * the element to get the measured margin for
++ * @return the measured top+bottom margin of the element in pixels.
++ */
++ public int getMarginHeight(Element element) {
++ return getMarginTop(element) + getMarginBottom(element);
++ }
++
++ /**
++ * Gets the combined left & right margin of the given element, provided that
++ * they have been measured. These elements are guaranteed to be measured:
++ * <ul>
++ * <li>ManagedLayotus and their child Connectors
++ * <li>Elements for which there is at least one ElementResizeListener
++ * <li>Elements for which at least one ManagedLayout has registered a
++ * dependency
++ * </ul>
++ *
++ * A negative number is returned if the element has not been measured. If 0
++ * is returned, it might indicate that the element is not attached to the
++ * DOM.
++ *
++ * @param element
++ * the element to get the measured margin for
++ * @return the measured left+right margin of the element in pixels.
++ */
++ public int getMarginWidth(Element element) {
++ return getMarginLeft(element) + getMarginRight(element);
++ }
++
+ /**
+ * Registers the outer height (including margins, borders and paddings) of a
+ * component. This can be used as an optimization by ManagedLayouts; by
+ * informing the LayoutManager about what size a component will have, the
+ * layout propagation can continue directly without first measuring the
+ * potentially resized elements.
+ *
+ * @param component
+ * the component for which the size is reported
+ * @param outerHeight
+ * the new outer height (including margins, borders and paddings)
+ * of the component in pixels
+ */
+ public void reportOuterHeight(ComponentConnector component, int outerHeight) {
+ MeasuredSize measuredSize = getMeasuredSize(component);
+ if (isLayoutRunning()) {
+ boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
+
+ if (heightChanged) {
+ onConnectorChange(component, false, true);
+ notifyListenersAndDepdendents(component.getWidget()
+ .getElement(), false, true);
+ }
+ currentDependencyTree.setNeedsVerticalMeasure(component, false);
+ } else if (measuredSize.getOuterHeight() != outerHeight) {
+ setNeedsMeasure(component);
+ }
+ }
+
+ /**
+ * Registers the height reserved for a relatively sized component. This can
+ * be used as an optimization by ManagedLayouts; by informing the
+ * LayoutManager about what size a component will have, the layout
+ * propagation can continue directly without first measuring the potentially
+ * resized elements.
+ *
+ * @param component
+ * the relatively sized component for which the size is reported
+ * @param assignedHeight
+ * the inner height of the relatively sized component's parent
+ * element in pixels
+ */
+ public void reportHeightAssignedToRelative(ComponentConnector component,
+ int assignedHeight) {
+ assert component.isRelativeHeight();
+
+ float percentSize = parsePercent(component.getState().getHeight());
+ int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
+
+ reportOuterHeight(component, effectiveHeight);
+ }
+
+ /**
+ * Registers the width reserved for a relatively sized component. This can
+ * be used as an optimization by ManagedLayouts; by informing the
+ * LayoutManager about what size a component will have, the layout
+ * propagation can continue directly without first measuring the potentially
+ * resized elements.
+ *
+ * @param component
+ * the relatively sized component for which the size is reported
+ * @param assignedWidth
+ * the inner width of the relatively sized component's parent
+ * element in pixels
+ */
+ public void reportWidthAssignedToRelative(ComponentConnector component,
+ int assignedWidth) {
+ assert component.isRelativeWidth();
+
+ float percentSize = parsePercent(component.getState().getWidth());
+ int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
+
+ reportOuterWidth(component, effectiveWidth);
+ }
+
+ private static float parsePercent(String size) {
+ return Float.parseFloat(size.substring(0, size.length() - 1));
+ }
+
+ /**
+ * Registers the outer width (including margins, borders and paddings) of a
+ * component. This can be used as an optimization by ManagedLayouts; by
+ * informing the LayoutManager about what size a component will have, the
+ * layout propagation can continue directly without first measuring the
+ * potentially resized elements.
+ *
+ * @param component
+ * the component for which the size is reported
+ * @param outerWidth
+ * the new outer width (including margins, borders and paddings)
+ * of the component in pixels
+ */
+ public void reportOuterWidth(ComponentConnector component, int outerWidth) {
+ MeasuredSize measuredSize = getMeasuredSize(component);
+ if (isLayoutRunning()) {
+ boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
+
+ if (widthChanged) {
+ onConnectorChange(component, true, false);
+ notifyListenersAndDepdendents(component.getWidget()
+ .getElement(), true, false);
+ }
+ currentDependencyTree.setNeedsHorizontalMeasure(component, false);
+ } else if (measuredSize.getOuterWidth() != outerWidth) {
+ setNeedsMeasure(component);
+ }
+ }
+
+ /**
+ * Adds a listener that will be notified whenever the size of a specific
+ * element changes. Adding a listener to an element also ensures that all
+ * sizes for that element will be available starting from the next layout
+ * phase.
+ *
+ * @param element
+ * the element that should be checked for size changes
+ * @param listener
+ * an ElementResizeListener that will be informed whenever the
+ * size of the target element has changed
+ */
+ public void addElementResizeListener(Element element,
+ ElementResizeListener listener) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ if (listeners == null) {
+ listeners = new HashSet<ElementResizeListener>();
+ elementResizeListeners.put(element, listeners);
+ ensureMeasured(element);
+ }
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes an element resize listener from the provided element. This might
+ * cause this LayoutManager to stop tracking the size of the element if no
+ * other sources are interested in the size.
+ *
+ * @param element
+ * the element to which the element resize listener was
+ * previously added
+ * @param listener
+ * the ElementResizeListener that should no longer get informed
+ * about size changes to the target element.
+ */
+ public void removeElementResizeListener(Element element,
+ ElementResizeListener listener) {
+ Collection<ElementResizeListener> listeners = elementResizeListeners
+ .get(element);
+ if (listeners != null) {
+ listeners.remove(listener);
+ if (listeners.isEmpty()) {
+ elementResizeListeners.remove(element);
+ stopMeasuringIfUnecessary(element);
+ }
+ }
+ }
+
+ private void stopMeasuringIfUnecessary(Element element) {
+ if (!needsMeasure(element)) {
+ measuredNonConnectorElements.remove(element);
+ setMeasuredSize(element, null);
+ }
+ }
+
+ /**
+ * Informs this LayoutManager that the size of a component might have
+ * changed. If there is no upcoming layout phase, a new layout phase is
+ * scheduled. This method should be used whenever a size might have changed
+ * from outside of Vaadin's normal update phase, e.g. when an icon has been
+ * loaded or when the user resizes some part of the UI using the mouse.
+ *
+ * @param component
+ * the component whose size might have changed.
+ */
+ public void setNeedsMeasure(ComponentConnector component) {
+ if (isLayoutRunning()) {
+ currentDependencyTree.setNeedsMeasure(component, true);
+ } else {
+ needsMeasure.add(component);
+ layoutLater();
+ }
+ }
+
+ public void setEverythingNeedsMeasure() {
+ everythingNeedsMeasure = true;
+ }
+
+ /**
+ * Clean measured sizes which are no longer needed. Only for IE8.
+ */
+ protected void cleanMeasuredSizes() {
+ }
+
+ }
--- /dev/null
+ /*
+ * Copyright 2011 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;
+
+ import java.util.Collection;
+ import java.util.List;
+
+ import com.google.gwt.event.shared.GwtEvent;
+ import com.google.web.bindery.event.shared.HandlerRegistration;
+ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.communication.ClientRpc;
+ import com.vaadin.shared.communication.SharedState;
+
+ /**
+ * Interface implemented by all client side classes that can be communicate with
+ * the server. Classes implementing this interface are initialized by the
+ * framework when needed and have the ability to communicate with the server.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0.0
+ */
+ public interface ServerConnector extends Connector {
+
+ /**
+ * Gets ApplicationConnection instance that created this connector.
+ *
+ * @return The ApplicationConnection as set by
+ * {@link #doInit(String, ApplicationConnection)}
+ */
+ public ApplicationConnection getConnection();
+
+ /**
+ * Tests whether the connector is enabled or not. This method checks that
+ * the connector is enabled in context, i.e. if the parent connector is
+ * disabled, this method must return false.
+ *
+ * @return true if the connector is enabled, false otherwise
+ */
+ public boolean isEnabled();
+
+ /**
+ *
+ * Called once by the framework to initialize the connector.
+ * <p>
+ * Note that the shared state is not yet available at this point nor any
+ * hierarchy information.
+ */
+ public void doInit(String connectorId, ApplicationConnection connection);
+
+ /**
+ * For internal use by the framework: returns the registered RPC
+ * implementations for an RPC interface identifier.
+ *
+ * TODO interface identifier type or format may change
+ *
+ * @param rpcInterfaceId
+ * RPC interface identifier: fully qualified interface type name
+ * @return RPC interface implementations registered for an RPC interface,
+ * not null
+ */
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId);
+
+ /**
+ * Adds a handler that is called whenever any part of the state has been
+ * updated by the server.
+ *
+ * @param handler
+ * The handler that should be added.
+ * @return A handler registration reference that can be used to unregister
+ * the handler
+ */
+ public HandlerRegistration addStateChangeHandler(StateChangeHandler handler);
+
++ /**
++ * Removes a handler that is called whenever any part of the state has been
++ * updated by the server.
++ *
++ * @param handler
++ * The handler that should be removed.
++ */
++ public void removeStateChangeHandler(StateChangeHandler handler);
++
+ /**
+ * Adds a handler that is called whenever the given part of the state has
+ * been updated by the server.
+ *
+ * @param propertyName
+ * the name of the property for which the handler should be
+ * called
+ * @param handler
+ * The handler that should be added.
+ * @return A handler registration reference that can be used to unregister
+ * the handler
+ */
+ public HandlerRegistration addStateChangeHandler(String propertyName,
+ StateChangeHandler handler);
+
++ /**
++ * Removes a handler that is called whenever any part of the state has been
++ * updated by the server.
++ *
++ * @param propertyName
++ * the name of the property for which the handler should be
++ * called
++ * @param handler
++ * The handler that should be removed.
++ */
++ public void removeStateChangeHandler(String propertyName,
++ StateChangeHandler handler);
++
+ /**
+ * Sends the given event to all registered handlers.
+ *
+ * @param event
+ * The event to send.
+ */
+ public void fireEvent(GwtEvent<?> event);
+
+ /**
+ * Event called when connector has been unregistered.
+ */
+ public void onUnregister();
+
+ /**
+ * Returns the parent of this connector. Can be null for only the root
+ * connector.
+ *
+ * @return The parent of this connector, as set by
+ * {@link #setParent(ServerConnector)}.
+ */
+ @Override
+ public ServerConnector getParent();
+
+ /**
+ * Sets the parent for this connector. This method should only be called by
+ * the framework to ensure that the connector hierarchy on the client side
+ * and the server side are in sync.
+ * <p>
+ * Note that calling this method does not fire a
+ * {@link ConnectorHierarchyChangeEvent}. The event is fired only when the
+ * whole hierarchy has been updated.
+ *
+ * @param parent
+ * The new parent of the connector
+ */
+ public void setParent(ServerConnector parent);
+
+ public void updateEnabledState(boolean enabledState);
+
+ public void setChildren(List<ServerConnector> children);
+
+ public List<ServerConnector> getChildren();
+
+ /**
+ * Gets the current shared state of the connector.
+ *
+ * @since 7.0.
+ * @return state The shared state object. Can be any sub type of
+ * {@link SharedState}. Never null.
+ */
+ public SharedState getState();
+ }
--- /dev/null
- implements ComponentConnector {
+ /*
+ * Copyright 2011 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.List;
+ import java.util.Set;
+
+ import com.google.gwt.dom.client.Element;
+ import com.google.gwt.user.client.ui.Focusable;
+ import com.google.gwt.user.client.ui.HasEnabled;
+ import com.google.gwt.user.client.ui.Widget;
+ import com.vaadin.client.ApplicationConnection;
+ import com.vaadin.client.ComponentConnector;
+ import com.vaadin.client.ComponentContainerConnector;
+ import com.vaadin.client.ConnectorMap;
+ import com.vaadin.client.LayoutManager;
+ import com.vaadin.client.ServerConnector;
+ import com.vaadin.client.TooltipInfo;
+ import com.vaadin.client.UIDL;
+ import com.vaadin.client.Util;
+ import com.vaadin.client.VConsole;
+ import com.vaadin.client.communication.StateChangeEvent;
+ import com.vaadin.client.metadata.NoDataException;
+ import com.vaadin.client.metadata.Type;
+ import com.vaadin.client.metadata.TypeData;
+ import com.vaadin.client.ui.UI.UIConnector;
+ import com.vaadin.client.ui.datefield.PopupDateFieldConnector;
+ import com.vaadin.shared.ComponentConstants;
+ import com.vaadin.shared.ComponentState;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.ui.TabIndexState;
++import com.vaadin.ui.themes.BaseTheme;
+
+ public abstract class AbstractComponentConnector extends AbstractConnector
- // Set v-connector style names for the widget
- getWidget().setStyleName("v-connector", true);
++implements ComponentConnector {
+
+ private Widget widget;
+
+ private String lastKnownWidth = "";
+ private String lastKnownHeight = "";
+
+ /**
+ * The style names from getState().getStyles() which are currently applied
+ * to the widget.
+ */
+ protected List<String> styleNames = new ArrayList<String>();
+
+ /**
+ * Default constructor
+ */
+ public AbstractComponentConnector() {
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ getConnection().getVTooltip().connectHandlersToWidget(getWidget());
+
- e);
++ // Set the core 'v' style name for the widget
++ getWidget().setStyleName(BaseTheme.UI_WIDGET, true);
+ }
+
+ /**
+ * Creates and returns the widget for this VPaintableWidget. This method
+ * should only be called once when initializing the paintable.
+ *
+ * @return
+ */
+ protected Widget createWidget() {
+ Type type = TypeData.getType(getClass());
+ try {
+ Type widgetType = type.getMethod("getWidget").getReturnType();
+ Object instance = widgetType.createInstance();
+ return (Widget) instance;
+ } catch (NoDataException e) {
+ throw new IllegalStateException(
+ "There is no information about the widget for "
+ + Util.getSimpleName(this)
+ + ". Did you remember to compile the right widgetset?",
++ e);
+ }
+ }
+
+ /**
+ * Returns the widget associated with this paintable. The widget returned by
+ * this method must not changed during the life time of the paintable.
+ *
+ * @return The widget associated with this paintable
+ */
+ @Override
+ public Widget getWidget() {
+ if (widget == null) {
+ widget = createWidget();
+ }
+
+ return widget;
+ }
+
+ @Deprecated
+ public static boolean isRealUpdate(UIDL uidl) {
+ return !uidl.hasAttribute("cached");
+ }
+
+ @Override
+ public ComponentState getState() {
+ return (ComponentState) super.getState();
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ ConnectorMap paintableMap = ConnectorMap.get(getConnection());
+
+ if (getState().getId() != null) {
+ getWidget().getElement().setId(getState().getId());
+ } else {
+ getWidget().getElement().removeAttribute("id");
+
+ }
+
+ /*
+ * Disabled state may affect (override) tabindex so the order must be
+ * first setting tabindex, then enabled state (through super
+ * implementation).
+ */
+ if (getState() instanceof TabIndexState
+ && getWidget() instanceof Focusable) {
+ ((Focusable) getWidget()).setTabIndex(((TabIndexState) getState())
+ .getTabIndex());
+ }
+
+ super.onStateChanged(stateChangeEvent);
+
+ // Style names
+ updateWidgetStyleNames();
+
+ // Set captions
+ if (delegateCaptionHandling()) {
+ ServerConnector parent = getParent();
+ if (parent instanceof ComponentContainerConnector) {
+ ((ComponentContainerConnector) parent).updateCaption(this);
+ } else if (parent == null && !(this instanceof UIConnector)) {
+ VConsole.error("Parent of connector "
+ + Util.getConnectorString(this)
+ + " is null. This is typically an indication of a broken component hierarchy");
+ }
+ }
+
+ /*
+ * updateComponentSize need to be after caption update so caption can be
+ * taken into account
+ */
+
+ updateComponentSize();
+ }
+
+ @Override
+ public void setWidgetEnabled(boolean widgetEnabled) {
+ // add or remove v-disabled style name from the widget
+ setWidgetStyleName(ApplicationConnection.DISABLED_CLASSNAME,
+ !widgetEnabled);
+
+ if (getWidget() instanceof HasEnabled) {
+ // set widget specific enabled state
+ ((HasEnabled) getWidget()).setEnabled(widgetEnabled);
+
+ // make sure the caption has or has not v-disabled style
+ if (delegateCaptionHandling()) {
+ ServerConnector parent = getParent();
+ if (parent instanceof ComponentContainerConnector) {
+ ((ComponentContainerConnector) parent).updateCaption(this);
+ } else if (parent == null && !(this instanceof UIConnector)) {
+ VConsole.error("Parent of connector "
+ + Util.getConnectorString(this)
+ + " is null. This is typically an indication of a broken component hierarchy");
+ }
+ }
+ }
+ }
+
+ private void updateComponentSize() {
+ String newWidth = getState().getWidth();
+ String newHeight = getState().getHeight();
+
+ // Parent should be updated if either dimension changed between relative
+ // and non-relative
+ if (newWidth.endsWith("%") != lastKnownWidth.endsWith("%")) {
+ Connector parent = getParent();
+ if (parent instanceof ManagedLayout) {
+ getLayoutManager().setNeedsHorizontalLayout(
+ (ManagedLayout) parent);
+ }
+ }
+
+ if (newHeight.endsWith("%") != lastKnownHeight.endsWith("%")) {
+ Connector parent = getParent();
+ if (parent instanceof ManagedLayout) {
+ getLayoutManager().setNeedsVerticalLayout(
+ (ManagedLayout) parent);
+ }
+ }
+
+ lastKnownWidth = newWidth;
+ lastKnownHeight = newHeight;
+
+ // Set defined sizes
+ Widget widget = getWidget();
+
+ widget.setStyleName("v-has-width", !isUndefinedWidth());
+ widget.setStyleName("v-has-height", !isUndefinedHeight());
+
+ widget.setHeight(newHeight);
+ widget.setWidth(newWidth);
+ }
+
+ @Override
+ public boolean isRelativeHeight() {
+ return getState().getHeight().endsWith("%");
+ }
+
+ @Override
+ public boolean isRelativeWidth() {
+ return getState().getWidth().endsWith("%");
+ }
+
+ @Override
+ public boolean isUndefinedHeight() {
+ return getState().getHeight().length() == 0;
+ }
+
+ @Override
+ public boolean isUndefinedWidth() {
+ return getState().getWidth().length() == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ComponentConnector#delegateCaptionHandling ()
+ */
+ @Override
+ public boolean delegateCaptionHandling() {
+ return true;
+ }
+
+ /**
+ * Updates the user defined, read-only and error style names for the widget
+ * based the shared state. User defined style names are prefixed with the
+ * primary style name of the widget returned by {@link #getWidget()}
+ * <p>
+ * This method can be overridden to provide additional style names for the
+ * component, for example see
+ * {@link AbstractFieldConnector#updateWidgetStyleNames()}
+ * </p>
+ */
+ protected void updateWidgetStyleNames() {
+ ComponentState state = getState();
+
+ String primaryStyleName = getWidget().getStylePrimaryName();
+
+ // should be in AbstractFieldConnector ?
+ // add / remove read-only style name
+ setWidgetStyleName("v-readonly", isReadOnly());
+
+ // add / remove error style name
+ setWidgetStyleNameWithPrefix(primaryStyleName,
+ ApplicationConnection.ERROR_CLASSNAME_EXT,
+ null != state.getErrorMessage());
+
+ // add additional user defined style names as class names, prefixed with
+ // component default class name. remove nonexistent style names.
+ if (state.hasStyles()) {
+ // add new style names
+ List<String> newStyles = new ArrayList<String>();
+ newStyles.addAll(state.getStyles());
+ newStyles.removeAll(styleNames);
+ for (String newStyle : newStyles) {
+ setWidgetStyleName(newStyle, true);
+ setWidgetStyleNameWithPrefix(primaryStyleName + "-", newStyle,
+ true);
+ }
+ // remove nonexistent style names
+ styleNames.removeAll(state.getStyles());
+ for (String oldStyle : styleNames) {
+ setWidgetStyleName(oldStyle, false);
+ setWidgetStyleNameWithPrefix(primaryStyleName + "-", oldStyle,
+ false);
+ }
+ styleNames.clear();
+ styleNames.addAll(state.getStyles());
+ } else {
+ // remove all old style names
+ for (String oldStyle : styleNames) {
+ setWidgetStyleName(oldStyle, false);
+ setWidgetStyleNameWithPrefix(primaryStyleName + "-", oldStyle,
+ false);
+ }
+ styleNames.clear();
+ }
+
+ }
+
+ /**
+ * This is used to add / remove state related style names from the widget.
+ * <p>
+ * Override this method for example if the style name given here should be
+ * updated in another widget in addition to the one returned by the
+ * {@link #getWidget()}.
+ * </p>
+ *
+ * @param styleName
+ * the style name to be added or removed
+ * @param add
+ * <code>true</code> to add the given style, <code>false</code>
+ * to remove it
+ */
+ protected void setWidgetStyleName(String styleName, boolean add) {
+ getWidget().setStyleName(styleName, add);
+ }
+
+ /**
+ * This is used to add / remove state related prefixed style names from the
+ * widget.
+ * <p>
+ * Override this method if the prefixed style name given here should be
+ * updated in another widget in addition to the one returned by the
+ * <code>Connector</code>'s {@link #getWidget()}, or if the prefix should be
+ * different. For example see
+ * {@link PopupDateFieldConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
+ * </p>
+ *
+ * @param styleName
+ * the style name to be added or removed
+ * @param add
+ * <code>true</code> to add the given style, <code>false</code>
+ * to remove it
+ * @deprecated This will be removed once styles are no longer added with
+ * prefixes.
+ */
+ @Deprecated
+ protected void setWidgetStyleNameWithPrefix(String prefix,
+ String styleName, boolean add) {
+ if (!styleName.startsWith("-")) {
+ if (!prefix.endsWith("-")) {
+ prefix += "-";
+ }
+ } else {
+ if (prefix.endsWith("-")) {
+ styleName.replaceFirst("-", "");
+ }
+ }
+ getWidget().setStyleName(prefix + styleName, add);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ComponentConnector#isReadOnly()
+ */
+ @Override
+ @Deprecated
+ public boolean isReadOnly() {
+ return getState().isReadOnly();
+ }
+
+ @Override
+ public LayoutManager getLayoutManager() {
+ return LayoutManager.get(getConnection());
+ }
+
+ /**
+ * Checks if there is a registered server side listener for the given event
+ * identifier.
+ *
+ * @param eventIdentifier
+ * The identifier to check for
+ * @return true if an event listener has been registered with the given
+ * event identifier on the server side, false otherwise
+ */
+ @Override
+ public boolean hasEventListener(String eventIdentifier) {
+ Set<String> reg = getState().getRegisteredEventListeners();
+ return (reg != null && reg.contains(eventIdentifier));
+ }
+
+ @Override
+ public void updateEnabledState(boolean enabledState) {
+ super.updateEnabledState(enabledState);
+
+ setWidgetEnabled(isEnabled());
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+
+ // Show an error if widget is still attached to DOM. It should never be
+ // at this point.
+ if (getWidget() != null && getWidget().isAttached()) {
+ getWidget().removeFromParent();
+ VConsole.error("Widget is still attached to the DOM after the connector ("
+ + Util.getConnectorString(this)
+ + ") has been unregistered. Widget was removed.");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ComponentConnector#getTooltipInfo(com.
+ * google.gwt.dom.client.Element)
+ */
+ @Override
+ public TooltipInfo getTooltipInfo(Element element) {
+ return new TooltipInfo(getState().getDescription(), getState()
+ .getErrorMessage());
+ }
+
+ /**
+ * Gets the icon set for this component.
+ *
+ * @return the URL of the icon, or <code>null</code> if no icon has been
+ * defined.
+ */
+ protected String getIcon() {
+ return getResourceUrl(ComponentConstants.ICON_RESOURCE);
+ }
+ }
--- /dev/null
-/*
++/*
+ * Copyright 2011 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.Collection;
+ import java.util.Collections;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+
+ import com.google.gwt.event.shared.GwtEvent;
+ import com.google.gwt.event.shared.HandlerManager;
+ import com.google.web.bindery.event.shared.HandlerRegistration;
+ import com.vaadin.client.ApplicationConnection;
+ import com.vaadin.client.ServerConnector;
+ import com.vaadin.client.Util;
+ import com.vaadin.client.VConsole;
+ import com.vaadin.client.communication.StateChangeEvent;
+ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+ import com.vaadin.client.metadata.NoDataException;
+ import com.vaadin.client.metadata.Type;
+ import com.vaadin.client.metadata.TypeData;
+ import com.vaadin.shared.communication.ClientRpc;
+ import com.vaadin.shared.communication.SharedState;
+ import com.vaadin.shared.communication.URLReference;
+
+ /**
+ * An abstract implementation of Connector.
+ *
+ * @author Vaadin Ltd
+ * @since 7.0.0
+ *
+ */
+ public abstract class AbstractConnector implements ServerConnector,
- StateChangeHandler {
++StateChangeHandler {
+
+ private ApplicationConnection connection;
+ private String id;
+
+ private HandlerManager handlerManager;
+ private Map<String, HandlerManager> statePropertyHandlerManagers;
+ private Map<String, Collection<ClientRpc>> rpcImplementations;
+ private final boolean debugLogging = false;
+
+ private SharedState state;
+ private ServerConnector parent;
+
+ /**
+ * Temporary storage for last enabled state to be able to see if it has
+ * changed. Can be removed once we are able to listen specifically for
+ * enabled changes in the state. Widget.isEnabled() cannot be used as all
+ * Widgets do not implement HasEnabled
+ */
+ private boolean lastEnabledState = true;
+ private List<ServerConnector> children;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.VPaintable#getConnection()
+ */
+ @Override
+ public final ApplicationConnection getConnection() {
+ return connection;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.Connector#getId()
+ */
+ @Override
+ public String getConnectorId() {
+ return id;
+ }
+
+ /**
+ * Called once by the framework to initialize the connector.
+ * <p>
+ * Note that the shared state is not yet available when this method is
+ * called.
+ * <p>
+ * Connector classes should override {@link #init()} instead of this method.
+ */
+ @Override
+ public final void doInit(String connectorId,
+ ApplicationConnection connection) {
+ this.connection = connection;
+ id = connectorId;
+
+ addStateChangeHandler(this);
+ init();
+ }
+
+ /**
+ * Called when the connector has been initialized. Override this method to
+ * perform initialization of the connector.
+ */
+ // FIXME: It might make sense to make this abstract to force users to
+ // use init instead of constructor, where connection and id has not yet been
+ // set.
+ protected void init() {
+
+ }
+
+ /**
+ * Registers an implementation for a server to client RPC interface.
+ *
+ * Multiple registrations can be made for a single interface, in which case
+ * all of them receive corresponding RPC calls.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation that should receive RPC calls
+ * @param <T>
+ * The type of the RPC interface that is being registered
+ */
+ protected <T extends ClientRpc> void registerRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null == rpcImplementations) {
+ rpcImplementations = new HashMap<String, Collection<ClientRpc>>();
+ }
+ if (null == rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.put(rpcInterfaceId, new ArrayList<ClientRpc>());
+ }
+ rpcImplementations.get(rpcInterfaceId).add(implementation);
+ }
+
+ /**
+ * Unregisters an implementation for a server to client RPC interface.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation to unregister
+ */
+ protected <T extends ClientRpc> void unregisterRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null != rpcImplementations
+ && null != rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.get(rpcInterfaceId).remove(implementation);
+ }
+ }
+
+ @Override
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId) {
+ if (null == rpcImplementations) {
+ return Collections.emptyList();
+ }
+ return (Collection<T>) rpcImplementations.get(rpcInterfaceId);
+ }
+
+ @Override
+ public void fireEvent(GwtEvent<?> event) {
+ if (handlerManager != null) {
+ handlerManager.fireEvent(event);
+ }
+ if (statePropertyHandlerManagers != null
+ && event instanceof StateChangeEvent) {
+ for (String property : ((StateChangeEvent) event)
+ .getChangedProperties()) {
+ HandlerManager manager = statePropertyHandlerManagers
+ .get(property);
+ if (manager != null) {
+ manager.fireEvent(event);
+ }
+ }
+ }
+ }
+
+ protected HandlerManager ensureHandlerManager() {
+ if (handlerManager == null) {
+ handlerManager = new HandlerManager(this);
+ }
+
+ return handlerManager;
+ }
+
+ @Override
+ public HandlerRegistration addStateChangeHandler(StateChangeHandler handler) {
+ return ensureHandlerManager()
+ .addHandler(StateChangeEvent.TYPE, handler);
+ }
+
++ @Override
++ public void removeStateChangeHandler(StateChangeHandler handler) {
++ ensureHandlerManager().removeHandler(StateChangeEvent.TYPE, handler);
++ }
++
+ @Override
+ public HandlerRegistration addStateChangeHandler(String propertyName,
+ StateChangeHandler handler) {
+ return ensureHandlerManager(propertyName).addHandler(
+ StateChangeEvent.TYPE, handler);
+ }
++
++ @Override
++ public void removeStateChangeHandler(String propertyName,
++ StateChangeHandler handler) {
++ ensureHandlerManager(propertyName).removeHandler(StateChangeEvent.TYPE,
++ handler);
++ }
+
+ private HandlerManager ensureHandlerManager(String propertyName) {
+ if (statePropertyHandlerManagers == null) {
+ statePropertyHandlerManagers = new HashMap<String, HandlerManager>();
+ }
+ HandlerManager manager = statePropertyHandlerManagers.get(propertyName);
+ if (manager == null) {
+ manager = new HandlerManager(this);
+ statePropertyHandlerManagers.put(propertyName, manager);
+ }
+ return manager;
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ if (debugLogging) {
+ VConsole.log("State change event for "
+ + Util.getConnectorString(stateChangeEvent.getConnector())
+ + " received by " + Util.getConnectorString(this));
+ }
+
+ updateEnabledState(isEnabled());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.client.ServerConnector#onUnregister()
+ */
+ @Override
+ public void onUnregister() {
+ if (debugLogging) {
+ VConsole.log("Unregistered connector "
+ + Util.getConnectorString(this));
+ }
+
+ }
+
+ /**
+ * Returns the shared state object for this connector.
+ *
+ * Override this method to define the shared state type for your connector.
+ *
+ * @return the current shared state (never null)
+ */
+ @Override
+ public SharedState getState() {
+ if (state == null) {
+ state = createState();
+ }
+
+ return state;
+ }
+
+ /**
+ * Creates a state object with default values for this connector. The
+ * created state object must be compatible with the return type of
+ * {@link #getState()}. The default implementation creates a state object
+ * using GWT.create() using the defined return type of {@link #getState()}.
+ *
+ * @return A new state object
+ */
+ protected SharedState createState() {
+ try {
+ Type stateType = getStateType(this);
+ Object stateInstance = stateType.createInstance();
+ return (SharedState) stateInstance;
+ } catch (NoDataException e) {
+ throw new IllegalStateException(
+ "There is no information about the state for "
+ + Util.getSimpleName(this)
+ + ". Did you remember to compile the right widgetset?",
+ e);
+ }
+
+ }
+
+ public static Type getStateType(ServerConnector connector) {
+ try {
+ return TypeData.getType(connector.getClass()).getMethod("getState")
+ .getReturnType();
+ } catch (NoDataException e) {
+ throw new IllegalStateException(
+ "There is no information about the state for "
+ + Util.getSimpleName(connector)
+ + ". Did you remember to compile the right widgetset?",
+ e);
+ }
+ }
+
+ @Override
+ public ServerConnector getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(ServerConnector parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public List<ServerConnector> getChildren() {
+ if (children == null) {
+ return Collections.emptyList();
+ }
+ return children;
+ }
+
+ @Override
+ public void setChildren(List<ServerConnector> children) {
+ this.children = children;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ if (!getState().isEnabled()) {
+ return false;
+ }
+
+ if (getParent() == null) {
+ return true;
+ } else {
+ return getParent().isEnabled();
+ }
+ }
+
+ @Override
+ public void updateEnabledState(boolean enabledState) {
+ if (lastEnabledState == enabledState) {
+ return;
+ }
+ lastEnabledState = enabledState;
+
+ for (ServerConnector c : getChildren()) {
+ // Update children as they might be affected by the enabled state of
+ // their parent
+ c.updateEnabledState(c.isEnabled());
+ }
+ }
+
+ /**
+ * Gets the URL for a resource that has been added by the server-side
+ * connector using
+ * {@link com.vaadin.terminal.AbstractClientConnector#setResource(String, com.vaadin.terminal.Resource)}
+ * with the same key. <code>null</code> is returned if no corresponding
+ * resource is found.
+ * <p>
+ * To get an event when a resource changes, you can use
+ * {@link #addStateChangeHandler(String, StateChangeHandler)} with
+ * <code>resources.[key]</code> as the property name.
+ *
+ * @param key
+ * a string identifying the resource.
+ * @return the resource URL as a string, or <code>null</code> if no
+ * corresponding resource is found.
+ */
+ public String getResourceUrl(String key) {
+ URLReference urlReference = getState().resources.get(key);
+ if (urlReference == null) {
+ return null;
+ } else {
+ return urlReference.getURL();
+ }
+ }
+ }
--- /dev/null
--- /dev/null
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import java.util.HashMap;
++import java.util.HashSet;
++import java.util.List;
++
++import com.google.gwt.dom.client.Style.Unit;
++import com.google.gwt.user.client.Element;
++import com.vaadin.client.ComponentConnector;
++import com.vaadin.client.ConnectorHierarchyChangeEvent;
++import com.vaadin.client.Util;
++import com.vaadin.client.communication.RpcProxy;
++import com.vaadin.client.communication.StateChangeEvent;
++import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
++import com.vaadin.client.ui.AbstractFieldConnector;
++import com.vaadin.client.ui.AbstractLayoutConnector;
++import com.vaadin.client.ui.LayoutClickEventHandler;
++import com.vaadin.client.ui.layout.ElementResizeEvent;
++import com.vaadin.client.ui.layout.ElementResizeListener;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout.CaptionPosition;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout.Slot;
++import com.vaadin.shared.AbstractFieldState;
++import com.vaadin.shared.ComponentConstants;
++import com.vaadin.shared.communication.URLReference;
++import com.vaadin.shared.ui.AlignmentInfo;
++import com.vaadin.shared.ui.LayoutClickRpc;
++import com.vaadin.shared.ui.MarginInfo;
++import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
++import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
++
++public abstract class AbstractBoxLayoutConnector extends
++ AbstractLayoutConnector /* implements PostLayoutListener */{
++
++ AbstractOrderedLayoutServerRpc rpc;
++
++ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
++ this) {
++
++ @Override
++ protected ComponentConnector getChildComponent(Element element) {
++ return Util.getConnectorForElement(getConnection(), getWidget(),
++ element);
++ }
++
++ @Override
++ protected LayoutClickRpc getLayoutClickRPC() {
++ return rpc;
++ };
++
++ };
++
++ @Override
++ public void init() {
++ super.init();
++ rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this);
++ getWidget().setLayoutManager(getLayoutManager());
++ }
++
++ @Override
++ public AbstractOrderedLayoutState getState() {
++ return (AbstractOrderedLayoutState) super.getState();
++ }
++
++ @Override
++ public VBoxLayout getWidget() {
++ return (VBoxLayout) super.getWidget();
++ }
++
++ /**
++ * For bookkeeping. Used to determine if extra calculations are needed for
++ * horizontal layout.
++ */
++ private HashSet<ComponentConnector> hasVerticalAlignment = new HashSet<ComponentConnector>();
++
++ /**
++ * For bookkeeping. Used to determine if extra calculations are needed for
++ * horizontal layout.
++ */
++ private HashSet<ComponentConnector> hasRelativeHeight = new HashSet<ComponentConnector>();
++
++ /**
++ * For bookkeeping. Used to determine if extra calculations are needed for
++ * horizontal layout.
++ */
++ private HashSet<ComponentConnector> hasExpandRatio = new HashSet<ComponentConnector>();
++
++ /**
++ * For bookkeeping. Used in extra calculations for horizontal layout.
++ */
++ private HashSet<Element> needsMeasure = new HashSet<Element>();
++
++ /**
++ * For bookkeeping. Used in extra calculations for horizontal layout.
++ */
++ // private HashMap<Element, Integer> childElementHeight = new
++ // HashMap<Element, Integer>();
++
++ /**
++ * For bookkeeping. Used in extra calculations for horizontal layout.
++ */
++ private HashMap<Element, Integer> childCaptionElementHeight = new HashMap<Element, Integer>();
++
++ @Override
++ public void updateCaption(ComponentConnector child) {
++ Slot slot = getWidget().getSlot(child);
++
++
++ String caption = child.getState().getCaption();
++ URLReference iconUrl = child.getState().resources
++ .get(ComponentConstants.ICON_RESOURCE);
++ String iconUrlString = iconUrl != null ? iconUrl.toString() : null;
++ List<String> styles = child.getState().getStyles();
++ String error = child.getState().getErrorMessage();
++ boolean showError = error != null;
++ if (child.getState() instanceof AbstractFieldState) {
++ AbstractFieldState abstractFieldState = (AbstractFieldState) child
++ .getState();
++ showError = showError && !abstractFieldState.isHideErrors();
++ }
++ boolean required = false;
++ if (child instanceof AbstractFieldConnector) {
++ required = ((AbstractFieldConnector) child).isRequired();
++ }
++ boolean enabled = child.getState().isEnabled();
++
++ slot.setCaption(caption, iconUrlString, styles, error, showError,
++ required,
++ enabled);
++
++ slot.setRelativeWidth(child.isRelativeWidth());
++ slot.setRelativeHeight(child.isRelativeHeight());
++
++ if (slot.hasCaption()) {
++ CaptionPosition pos = slot.getCaptionPosition();
++ getLayoutManager().addElementResizeListener(
++ slot.getCaptionElement(), slotCaptionResizeListener);
++ if (child.isRelativeHeight()
++ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++ getWidget().updateCaptionOffset(slot.getCaptionElement());
++ } else if (child.isRelativeWidth()
++ && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
++ getWidget().updateCaptionOffset(slot.getCaptionElement());
++ }
++ } else {
++ childCaptionElementHeight.remove(child.getWidget().getElement());
++ }
++
++ updateLayoutHeight();
++
++ if (needsExpand()) {
++ updateExpand();
++ }
++ }
++
++ @Override
++ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
++ super.onConnectorHierarchyChange(event);
++
++ List<ComponentConnector> previousChildren = event.getOldChildren();
++ int currentIndex = 0;
++ VBoxLayout layout = getWidget();
++
++ for (ComponentConnector child : getChildComponents()) {
++ Slot slot = layout.getSlot(child);
++ if (slot.getParent() != layout) {
++ child.addStateChangeHandler(childStateChangeHandler);
++ }
++ layout.addOrMoveSlot(slot, currentIndex++);
++ }
++
++ for (ComponentConnector child : previousChildren) {
++ if (child.getParent() != this) {
++ Slot slot = layout.getSlot(child);
++ hasVerticalAlignment.remove(child);
++ hasRelativeHeight.remove(child);
++ hasExpandRatio.remove(child);
++ needsMeasure.remove(child.getWidget().getElement());
++ // childElementHeight.remove(child.getWidget().getElement());
++ childCaptionElementHeight
++ .remove(child.getWidget().getElement());
++ getLayoutManager().removeElementResizeListener(
++ child.getWidget().getElement(),
++ childComponentResizeListener);
++ if (slot.hasCaption()) {
++ getLayoutManager()
++ .removeElementResizeListener(
++ slot.getCaptionElement(),
++ slotCaptionResizeListener);
++ }
++ if (slot.getSpacingElement() != null) {
++ getLayoutManager().removeElementResizeListener(
++ slot.getSpacingElement(), spacingResizeListener);
++ }
++ child.removeStateChangeHandler(childStateChangeHandler);
++ layout.removeSlot(child.getWidget());
++ }
++ }
++
++ // If some component is added/removed, we need to recalculate the expand
++ if (needsExpand()) {
++ updateExpand();
++ } else {
++ getWidget().clearExpand();
++ }
++
++ }
++
++ @Override
++ public void onStateChanged(StateChangeEvent stateChangeEvent) {
++ super.onStateChanged(stateChangeEvent);
++
++ clickEventHandler.handleEventHandlerRegistration();
++ getWidget().setMargin(new MarginInfo(getState().getMarginsBitmask()));
++ getWidget().setSpacing(getState().isSpacing());
++
++ hasExpandRatio.clear();
++ hasVerticalAlignment.clear();
++ hasRelativeHeight.clear();
++ needsMeasure.clear();
++
++ boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight()
++ : !isUndefinedWidth();
++ for (ComponentConnector child : getChildComponents()) {
++ double expandRatio = getState().getChildData().get(child)
++ .getExpandRatio();
++ if (expandRatio > 0) {
++ equalExpandRatio = false;
++ break;
++ }
++ }
++
++ for (ComponentConnector child : getChildComponents()) {
++ Slot slot = getWidget().getSlot(child);
++
++ AlignmentInfo alignment = new AlignmentInfo(getState()
++ .getChildData().get(child).getAlignmentBitmask());
++ slot.setAlignment(alignment);
++
++ double expandRatio = getState().getChildData().get(child)
++ .getExpandRatio();
++ if (equalExpandRatio) {
++ expandRatio = 1;
++ } else if (expandRatio == 0) {
++ expandRatio = -1;
++ }
++ slot.setExpandRatio(expandRatio);
++
++ // Bookkeeping to identify special cases that need extra
++ // calculations
++ if (alignment.isVerticalCenter() || alignment.isBottom()) {
++ hasVerticalAlignment.add(child);
++ }
++
++ if (expandRatio > 0) {
++ hasExpandRatio.add(child);
++ }
++
++ // if (child.getState().isRelativeHeight()) {
++ // hasRelativeHeight.add(child);
++ // } else {
++ // needsMeasure.add(child.getWidget().getElement());
++ // }
++ }
++
++ updateAllSlotListeners();
++
++ updateLayoutHeight();
++ }
++
++ StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
++ @Override
++ public void onStateChanged(StateChangeEvent stateChangeEvent) {
++
++ ComponentConnector child = (ComponentConnector) stateChangeEvent
++ .getConnector();
++
++ // We need to update the slot size if the component size is changed
++ // to relative
++ Slot slot = getWidget().getSlot(child);
++ slot.setRelativeWidth(child.isRelativeWidth());
++ slot.setRelativeHeight(child.isRelativeHeight());
++
++ // For relative sized widgets, we need to set the caption offset
++ // if (slot.hasCaption()) {
++ // CaptionPosition pos = slot.getCaptionPosition();
++ // if (child.isRelativeHeight()
++ // && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM))
++ // {
++ // getWidget().updateCaptionOffset(slot.getCaptionElement());
++ // } else if (child.isRelativeWidth()
++ // && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT))
++ // {
++ // getWidget().updateCaptionOffset(slot.getCaptionElement());
++ // }
++ // }
++
++ updateSlotListeners(child);
++ // updateAllSlotListeners();
++
++ updateLayoutHeight();
++ }
++ };
++
++ private boolean needsFixedHeight() {
++ if (!getWidget().vertical
++ && isUndefinedHeight()
++ && (hasRelativeHeight.size() > 0 || (hasVerticalAlignment
++ .size() > 0 && hasVerticalAlignment.size() < getChildren()
++ .size()))) {
++ return true;
++ }
++ return false;
++ }
++
++ private boolean needsExpand() {
++ boolean canApplyExpand = (getWidget().vertical && !isUndefinedHeight())
++ || (!getWidget().vertical && !isUndefinedWidth());
++ return hasExpandRatio.size() > 0 && canApplyExpand;
++ }
++
++ private void updateAllSlotListeners() {
++ for (ComponentConnector child : getChildComponents()) {
++ updateSlotListeners(child);
++ }
++ // if (needsFixedHeight()) {
++ // getWidget().clearHeight();
++ // setLayoutHeightListener(true);
++ // getLayoutManager().setNeedsMeasure(AbstractBoxLayoutConnector.this);
++ // } else {
++ // setLayoutHeightListener(false);
++ // }
++ }
++
++ /**
++ * Add/remove necessary ElementResizeListeners for one slot. This should be
++ * called after each update to the slot's or it's widget.
++ */
++ private void updateSlotListeners(ComponentConnector child) {
++ Slot slot = getWidget().getSlot(child);
++
++ // Clear all possible listeners first
++ dontListen(slot.getWidget().getElement(), childComponentResizeListener);
++ if (slot.hasCaption()) {
++ dontListen(slot.getCaptionElement(), slotCaptionResizeListener);
++ }
++ if (slot.hasSpacing()) {
++ dontListen(slot.getSpacingElement(), spacingResizeListener);
++ }
++
++ // Add all necessary listeners
++ if (needsFixedHeight()) {
++ listen(slot.getWidget().getElement(), childComponentResizeListener);
++ if (slot.hasCaption()) {
++ listen(slot.getCaptionElement(), slotCaptionResizeListener);
++ }
++ } else if ((child.isRelativeHeight() || child.isRelativeWidth())
++ && slot.hasCaption()) {
++ // If the slot has caption, we need to listen for it's size changes
++ // in order to update the padding/margin offset for relative sized
++ // components
++ listen(slot.getCaptionElement(), slotCaptionResizeListener);
++ }
++
++ if (needsExpand()) {
++ listen(slot.getWidget().getElement(), childComponentResizeListener);
++ if (slot.hasSpacing()) {
++ listen(slot.getSpacingElement(), spacingResizeListener);
++ }
++ }
++
++ if (child.isRelativeHeight()) {
++ hasRelativeHeight.add(child);
++ needsMeasure.remove(child.getWidget().getElement());
++ } else {
++ hasRelativeHeight.remove(child);
++ needsMeasure.add(child.getWidget().getElement());
++ }
++
++ }
++
++ // public void postLayout() {
++ // if (needsFixedHeight()) {
++ // // Re-measure all elements that are available
++ // for (Element el : needsMeasure) {
++ // childElementHeight.put(el, getLayoutManager()
++ // .getOuterHeight(el));
++ //
++ // Element captionElement = el.getParentElement()
++ // .getFirstChildElement().cast();
++ // if (captionElement.getClassName().contains("v-caption")) {
++ // childCaptionElementHeight.put(el, getLayoutManager()
++ // .getOuterHeight(captionElement));
++ // }
++ // }
++ // // System.out.println(" ### Child sizes: "
++ // // + childElementHeight.values().toString());
++ // // System.out.println(" ### Caption sizes: "
++ // // + childCaptionElementHeight.values().toString());
++ //
++ // int height = getMaxHeight()
++ // + getLayoutManager().getBorderHeight(
++ // getWidget().getElement())
++ // + getLayoutManager().getPaddingHeight(
++ // getWidget().getElement());
++ // getWidget().getElement().getStyle().setHeight(height, Unit.PX);
++ // }
++ // }
++
++ // private ElementResizeListener layoutResizeListener = new
++ // ElementResizeListener() {
++ // public void onElementResize(ElementResizeEvent e) {
++ // updateLayoutHeight();
++ // if (needsExpand() && (isUndefinedHeight() || isUndefinedWidth())) {
++ // updateExpand();
++ // }
++ // }
++ // };
++
++ private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() {
++ @Override
++ public void onElementResize(ElementResizeEvent e) {
++
++ // Get all needed element references
++ Element captionElement = (Element) e.getElement().cast();
++
++ // Caption position determines if the widget element is the first or
++ // last child inside the caption wrap
++ CaptionPosition pos = getWidget().getCaptionPositionFromElement(
++ (Element) captionElement.getParentElement().cast());
++
++ // The default is the last child
++ Element widgetElement = captionElement.getParentElement()
++ .getLastChild().cast();
++
++ // ...but if caption position is bottom or right, the widget is the
++ // first child
++ if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) {
++ widgetElement = captionElement.getParentElement()
++ .getFirstChildElement().cast();
++ }
++
++ if (captionElement == widgetElement) {
++ // Caption element already detached
++ dontListen(captionElement, slotCaptionResizeListener);
++ childCaptionElementHeight.remove(widgetElement);
++ return;
++ }
++
++ String widgetWidth = widgetElement.getStyle().getWidth();
++ String widgetHeight = widgetElement.getStyle().getHeight();
++
++ if (widgetHeight.endsWith("%")
++ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++ getWidget().updateCaptionOffset(captionElement);
++ } else if (widgetWidth.endsWith("%")
++ && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
++ getWidget().updateCaptionOffset(captionElement);
++ }
++
++ int h = getLayoutManager().getOuterHeight(captionElement)
++ - getLayoutManager().getMarginHeight(captionElement);
++ childCaptionElementHeight.put(widgetElement, h);
++
++ // if (needsFixedHeight()) {
++ // getWidget().clearHeight();
++ // getLayoutManager().setNeedsMeasure(
++ // AbstractBoxLayoutConnector.this);
++ // }
++
++ updateLayoutHeight();
++
++ if (needsExpand()) {
++ updateExpand();
++ }
++ }
++ };
++
++ private ElementResizeListener childComponentResizeListener = new ElementResizeListener() {
++ @Override
++ public void onElementResize(ElementResizeEvent e) {
++ // int h = getLayoutManager().getOuterHeight(e.getElement());
++ // childElementHeight.put((Element) e.getElement().cast(), h);
++ updateLayoutHeight();
++
++ if (needsExpand()) {
++ updateExpand();
++ }
++ }
++ };
++
++ private ElementResizeListener spacingResizeListener = new ElementResizeListener() {
++ @Override
++ public void onElementResize(ElementResizeEvent e) {
++ if (needsExpand()) {
++ updateExpand();
++ }
++ }
++ };
++
++ private void updateLayoutHeight() {
++ if (needsFixedHeight()) {
++ int h = getMaxHeight();
++ h += getLayoutManager().getBorderHeight(getWidget().getElement())
++ + getLayoutManager().getPaddingHeight(
++ getWidget().getElement());
++ getWidget().getElement().getStyle().setHeight(h, Unit.PX);
++ getLayoutManager().setNeedsMeasure(this);
++ }
++ }
++
++ private void updateExpand() {
++ // System.out.println("All sizes: "
++ // + childElementHeight.values().toString() + " - Caption sizes: "
++ // + childCaptionElementHeight.values().toString());
++ getWidget().updateExpand();
++ }
++
++ private int getMaxHeight() {
++ int highestNonRelative = -1;
++ int highestRelative = -1;
++
++ for (ComponentConnector child : getChildComponents()) {
++ // TODO would be more efficient to measure the slot element if both
++ // caption and child widget elements need to be measured. Keeping
++ // track of what to measure is the most difficult part of this
++ // layout.
++ Element el = child.getWidget().getElement();
++ CaptionPosition pos = getWidget().getCaptionPositionFromElement(
++ (Element) el.getParentElement().cast());
++ if (needsMeasure.contains(el)) {
++ int h = getLayoutManager().getOuterHeight(el);
++ String sHeight = el.getStyle().getHeight();
++ // Only add the caption size to the height of the slot if
++ // coption position is top or bottom
++ if (childCaptionElementHeight.containsKey(el)
++ && (sHeight == null || !sHeight.endsWith("%"))
++ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++ h += childCaptionElementHeight.get(el);
++ }
++ if (h > highestNonRelative) {
++ highestNonRelative = h;
++ }
++ } else {
++ int h = getLayoutManager().getOuterHeight(el);
++ if (childCaptionElementHeight.containsKey(el)
++ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++ h += childCaptionElementHeight.get(el);
++ }
++ if (h > highestRelative) {
++ highestRelative = h;
++ }
++ }
++ }
++ return highestNonRelative > -1 ? highestNonRelative : highestRelative;
++ }
++
++ @Override
++ public void onUnregister() {
++ // Cleanup all ElementResizeListeners
++
++ // dontListen(getWidget().getElement(), layoutResizeListener);
++
++ for (ComponentConnector child : getChildComponents()) {
++ Slot slot = getWidget().getSlot(child);
++ if (slot.hasCaption()) {
++ dontListen(slot.getCaptionElement(), slotCaptionResizeListener);
++ }
++
++ if (slot.getSpacingElement() != null) {
++ dontListen(slot.getSpacingElement(), spacingResizeListener);
++ }
++
++ dontListen(slot.getWidget().getElement(),
++ childComponentResizeListener);
++ }
++
++ super.onUnregister();
++ }
++
++ // private void setLayoutHeightListener(boolean add) {
++ // if (add) {
++ // listen(getWidget().getElement(), layoutResizeListener);
++ // } else {
++ // dontListen(getWidget().getElement(), layoutResizeListener);
++ // if (!needsExpand()) {
++ // System.out.println("Clearing element sizes");
++ // childElementHeight.clear();
++ // childCaptionElementHeight.clear();
++ // }
++ // }
++ // }
++
++ /*
++ * Convenience methods
++ */
++
++ private void listen(Element el, ElementResizeListener listener) {
++ getLayoutManager().addElementResizeListener(el, listener);
++ }
++
++ private void dontListen(Element el, ElementResizeListener listener) {
++ getLayoutManager().removeElementResizeListener(el, listener);
++ }
++
++}
--- /dev/null
--- /dev/null
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import com.vaadin.shared.ui.Connect;
++import com.vaadin.shared.ui.Connect.LoadStyle;
++import com.vaadin.ui.HorizontalLayout;
++
++@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
++public class HorizontalBoxLayoutConnector extends AbstractBoxLayoutConnector {
++
++ @Override
++ public void init() {
++ super.init();
++ getWidget().setVertical(false);
++ }
++
++}
--- /dev/null
-import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.Connect.LoadStyle;
-import com.vaadin.ui.HorizontalLayout;
+ /*
+ * Copyright 2011 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.orderedlayout;
+
-@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
+
++//@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
+ public class HorizontalLayoutConnector extends AbstractOrderedLayoutConnector {
+
+ @Override
+ public VHorizontalLayout getWidget() {
+ return (VHorizontalLayout) super.getWidget();
+ }
+
+ }
--- /dev/null
--- /dev/null
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import java.util.HashMap;
++import java.util.List;
++import java.util.Map;
++
++import com.google.gwt.dom.client.Node;
++import com.google.gwt.dom.client.Style;
++import com.google.gwt.dom.client.Style.Unit;
++import com.google.gwt.regexp.shared.MatchResult;
++import com.google.gwt.regexp.shared.RegExp;
++import com.google.gwt.user.client.DOM;
++import com.google.gwt.user.client.Element;
++import com.google.gwt.user.client.Event;
++import com.google.gwt.user.client.ui.FlowPanel;
++import com.google.gwt.user.client.ui.SimplePanel;
++import com.google.gwt.user.client.ui.UIObject;
++import com.google.gwt.user.client.ui.Widget;
++import com.vaadin.client.ComponentConnector;
++import com.vaadin.client.LayoutManager;
++import com.vaadin.shared.ui.AlignmentInfo;
++import com.vaadin.shared.ui.MarginInfo;
++import com.vaadin.ui.themes.BaseTheme;
++
++public class VBoxLayout extends FlowPanel {
++
++ public static final String CLASSNAME = "v-boxlayout";
++
++ private static final String ALIGN_CLASS_PREFIX = "v-align-";
++
++ protected boolean spacing = false;
++
++ protected boolean vertical = true;
++
++ protected boolean definedHeight = false;
++
++ private Map<Widget, Slot> widgetToSlot = new HashMap<Widget, Slot>();
++
++ private LayoutManager layoutManager;
++
++ public VBoxLayout() {
++ setStyleName(CLASSNAME);
++ setVertical(true);
++ }
++
++ public void setVertical(boolean isVertical) {
++ vertical = isVertical;
++ if (vertical) {
++ addStyleName("v-vertical");
++ removeStyleName("v-horizontal");
++ } else {
++ addStyleName("v-horizontal");
++ removeStyleName("v-vertical");
++ }
++ }
++
++ public void addOrMoveSlot(Slot slot, int index) {
++ if (slot.getParent() == this) {
++ int currentIndex = getWidgetIndex(slot);
++ if (index == currentIndex) {
++ return;
++ }
++ }
++ insert(slot, index);
++ }
++
++ @Override
++ protected void insert(Widget child, Element container, int beforeIndex,
++ boolean domInsert) {
++ // Validate index; adjust if the widget is already a child of this
++ // panel.
++ beforeIndex = adjustIndex(child, beforeIndex);
++
++ // Detach new child.
++ child.removeFromParent();
++
++ // Logical attach.
++ getChildren().insert(child, beforeIndex);
++
++ // Physical attach.
++ container = expandWrapper != null ? expandWrapper : getElement();
++ if (domInsert) {
++ DOM.insertChild(container, child.getElement(),
++ spacing ? beforeIndex * 2 : beforeIndex);
++ } else {
++ DOM.appendChild(container, child.getElement());
++ }
++
++ // Adopt.
++ adopt(child);
++ }
++
++ public Slot removeSlot(Widget widget) {
++ Slot slot = widgetToSlot.get(widget);
++ remove(slot);
++ widgetToSlot.remove(widget);
++ return slot;
++ }
++
++ public Slot getSlot(ComponentConnector connector) {
++ Slot slot = widgetToSlot.get(connector.getWidget());
++ if (slot == null) {
++ slot = new Slot(connector);
++ widgetToSlot.put(connector.getWidget(), slot);
++ }
++ return slot;
++ }
++
++ public enum CaptionPosition {
++ TOP, RIGHT, BOTTOM, LEFT
++ }
++
++ protected class Slot extends SimplePanel {
++
++ private ComponentConnector connector;
++
++ private Element spacer;
++
++ private Element captionWrap;
++ private Element caption;
++ private Element captionText;
++ private Icon icon;
++ private Element errorIcon;
++ private Element requiredIcon;
++
++ // Caption is placed after component unless there is some part which
++ // moves it above.
++ private CaptionPosition captionPosition = CaptionPosition.RIGHT;
++
++ private AlignmentInfo alignment;
++ private double expandRatio = -1;
++
++ public Slot(ComponentConnector connector) {
++ this.connector = connector;
++ setWidget(connector.getWidget());
++ setStylePrimaryName("v-slot");
++ }
++
++ public AlignmentInfo getAlignment() {
++ return alignment;
++ }
++
++ public void setAlignment(AlignmentInfo alignment) {
++ this.alignment = alignment;
++
++ if (alignment.isHorizontalCenter()) {
++ addStyleName(ALIGN_CLASS_PREFIX + "center");
++ removeStyleName(ALIGN_CLASS_PREFIX + "right");
++ } else if (alignment.isRight()) {
++ addStyleName(ALIGN_CLASS_PREFIX + "right");
++ removeStyleName(ALIGN_CLASS_PREFIX + "center");
++ } else {
++ removeStyleName(ALIGN_CLASS_PREFIX + "right");
++ removeStyleName(ALIGN_CLASS_PREFIX + "center");
++ }
++ if (alignment.isVerticalCenter()) {
++ addStyleName(ALIGN_CLASS_PREFIX + "middle");
++ removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
++ } else if (alignment.isBottom()) {
++ addStyleName(ALIGN_CLASS_PREFIX + "bottom");
++ removeStyleName(ALIGN_CLASS_PREFIX + "middle");
++ } else {
++ removeStyleName(ALIGN_CLASS_PREFIX + "middle");
++ removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
++ }
++ }
++
++ public void setExpandRatio(double expandRatio) {
++ this.expandRatio = expandRatio;
++ }
++
++ public double getExpandRatio() {
++ return expandRatio;
++ }
++
++ public void setSpacing(boolean spacing) {
++ if (spacing && spacer == null) {
++ spacer = DOM.createDiv();
++ spacer.addClassName("v-spacing");
++ getElement().getParentElement().insertBefore(spacer,
++ getElement());
++ } else if (!spacing && spacer != null) {
++ spacer.removeFromParent();
++ spacer = null;
++ }
++ }
++
++ public Element getSpacingElement() {
++ return spacer;
++ }
++
++ public boolean hasSpacing() {
++ return getSpacingElement() != null;
++ }
++
++ protected int getSpacingSize(boolean vertical) {
++ if (spacer == null) {
++ return 0;
++ }
++
++ if (layoutManager != null) {
++ if (vertical) {
++ return layoutManager.getOuterHeight(spacer);
++ } else {
++ return layoutManager.getOuterWidth(spacer);
++ }
++ }
++ // TODO place for optimization (in expense of theme
++ // flexibility): only measure one of the elements and cache the
++ // value
++ return vertical ? spacer.getOffsetHeight() : spacer
++ .getOffsetWidth();
++ // }
++ }
++
++ public void setCaptionPosition(CaptionPosition captionPosition) {
++ if (caption == null) {
++ return;
++ }
++
++ captionWrap.removeClassName("v-caption-on-"
++ + this.captionPosition.name().toLowerCase());
++
++ this.captionPosition = captionPosition;
++ if (captionPosition == CaptionPosition.BOTTOM
++ || captionPosition == CaptionPosition.RIGHT) {
++ captionWrap.appendChild(caption);
++ } else {
++ captionWrap.insertFirst(caption);
++ }
++
++ captionWrap.addClassName("v-caption-on-"
++ + captionPosition.name().toLowerCase());
++ }
++
++ public CaptionPosition getCaptionPosition() {
++ return captionPosition;
++ }
++
++ // TODO refactor VCaption and use that instead: creates a tight coupling
++ // between this layout and Vaadin, but it's already coupled
++ public void setCaption(String captionText, String iconUrl,
++ List<String> styles, String error, boolean showError,
++ boolean required, boolean enabled) {
++
++ // TODO place for optimization: check if any of these have changed
++ // since last time, and only run those changes
++
++ // Caption wrappers
++ if (captionText != null || iconUrl != null || error != null
++ || required) {
++ if (caption == null) {
++ caption = DOM.createDiv();
++ captionWrap = DOM.createDiv();
++ captionWrap.addClassName(BaseTheme.UI_WIDGET);
++ captionWrap.addClassName("v-has-caption");
++ getElement().appendChild(captionWrap);
++ captionWrap.appendChild(getWidget().getElement());
++ }
++ } else if (caption != null) {
++ getElement().appendChild(getWidget().getElement());
++ captionWrap.removeFromParent();
++ caption = null;
++ captionWrap = null;
++ }
++
++ // Caption text
++ if (captionText != null) {
++ if (this.captionText == null) {
++ this.captionText = DOM.createSpan();
++ this.captionText.addClassName("v-captiontext");
++ caption.appendChild(this.captionText);
++ }
++ if (captionText.trim().equals("")) {
++ this.captionText.setInnerHTML(" ");
++ } else {
++ this.captionText.setInnerText(captionText);
++ }
++ } else if (this.captionText != null) {
++ this.captionText.removeFromParent();
++ this.captionText = null;
++ }
++
++ // Icon
++ if (iconUrl != null) {
++ if (icon == null) {
++ icon = new Icon();
++ // icon = DOM.createImg();
++ // icon.setClassName("v-icon");
++ caption.insertFirst(icon.getElement());
++ }
++ // icon.setAttribute("src", iconUrl);
++ icon.setUri(iconUrl);
++ } else if (icon != null) {
++ icon.getElement().removeFromParent();
++ icon = null;
++ }
++
++ // Required
++ if (required) {
++ if (requiredIcon == null) {
++ requiredIcon = DOM.createSpan();
++ // TODO decide something better (e.g. use CSS to insert the
++ // character)
++ requiredIcon.setInnerHTML("*");
++ requiredIcon.setClassName("v-required-field-indicator");
++ }
++ caption.appendChild(requiredIcon);
++ } else if (requiredIcon != null) {
++ requiredIcon.removeFromParent();
++ requiredIcon = null;
++ }
++
++ // Error
++ if (error != null && showError) {
++ if (errorIcon == null) {
++ errorIcon = DOM.createSpan();
++ errorIcon.setClassName("v-errorindicator");
++ }
++ caption.appendChild(errorIcon);
++ } else if (errorIcon != null) {
++ errorIcon.removeFromParent();
++ errorIcon = null;
++ }
++
++ if (caption != null) {
++ // Styles
++ caption.setClassName("v-caption");
++
++ if (styles != null) {
++ for (String style : styles) {
++ caption.addClassName("v-caption-" + style);
++ }
++ }
++
++ if (enabled) {
++ caption.removeClassName("v-disabled");
++ } else {
++ caption.addClassName("v-disabled");
++ }
++
++ // Caption position
++ if (captionText != null || iconUrl != null) {
++ setCaptionPosition(CaptionPosition.TOP);
++ } else {
++ setCaptionPosition(CaptionPosition.RIGHT);
++ }
++ }
++
++ // TODO theme flexibility: add extra styles to captionWrap as well?
++
++ }
++
++ public boolean hasCaption() {
++ return caption != null;
++ }
++
++ public Element getCaptionElement() {
++ return caption;
++ }
++
++ public void setRelativeWidth(boolean relativeWidth) {
++ updateRelativeSize(relativeWidth, "width");
++ }
++
++ public void setRelativeHeight(boolean relativeHeight) {
++ updateRelativeSize(relativeHeight, "height");
++ }
++
++ private void updateRelativeSize(boolean isRelativeSize, String direction) {
++ if (isRelativeSize && hasCaption()) {
++ captionWrap.getStyle().setProperty(
++ direction,
++ getWidget().getElement().getStyle()
++ .getProperty(direction));
++ captionWrap.addClassName("v-has-" + direction);
++ } else if (hasCaption()) {
++ if (direction.equals("height")) {
++ captionWrap.getStyle().clearHeight();
++ } else {
++ captionWrap.getStyle().clearWidth();
++ }
++ captionWrap.removeClassName("v-has-" + direction);
++ captionWrap.getStyle().clearPaddingTop();
++ captionWrap.getStyle().clearPaddingRight();
++ captionWrap.getStyle().clearPaddingBottom();
++ captionWrap.getStyle().clearPaddingLeft();
++ caption.getStyle().clearMarginTop();
++ caption.getStyle().clearMarginRight();
++ caption.getStyle().clearMarginBottom();
++ caption.getStyle().clearMarginLeft();
++ }
++ }
++
++ @Override
++ public void onBrowserEvent(Event event) {
++ super.onBrowserEvent(event);
++ if (DOM.eventGetType(event) == Event.ONLOAD
++ && icon.getElement() == DOM.eventGetTarget(event)) {
++ if (layoutManager != null) {
++ layoutManager.layoutLater();
++ } else {
++ updateCaptionOffset(caption);
++ }
++ }
++ }
++
++ @Override
++ protected Element getContainerElement() {
++ if (captionWrap == null) {
++ return getElement();
++ } else {
++ return captionWrap;
++ }
++ }
++
++ @Override
++ protected void onDetach() {
++ if (spacer != null) {
++ spacer.removeFromParent();
++ }
++ super.onDetach();
++ }
++
++ @Override
++ protected void onAttach() {
++ super.onAttach();
++ if (spacer != null) {
++ getElement().getParentElement().insertBefore(spacer,
++ getElement());
++ }
++ }
++
++ }
++
++ protected class Icon extends UIObject {
++ public static final String CLASSNAME = "v-icon";
++ private String myUrl;
++
++ public Icon() {
++ setElement(DOM.createImg());
++ DOM.setElementProperty(getElement(), "alt", "");
++ setStyleName(CLASSNAME);
++ }
++
++ public void setUri(String url) {
++ if (!url.equals(myUrl)) {
++ /*
++ * Start sinking onload events, widgets responsibility to react.
++ * We must do this BEFORE we set src as IE fires the event
++ * immediately if the image is found in cache (#2592).
++ */
++ sinkEvents(Event.ONLOAD);
++
++ DOM.setElementProperty(getElement(), "src", url);
++ myUrl = url;
++ }
++ }
++
++ }
++
++ void setLayoutManager(LayoutManager manager) {
++ layoutManager = manager;
++ }
++
++ private static final RegExp captionPositionRegexp = RegExp
++ .compile("v-caption-on-(\\S+)");
++
++ CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
++ // Get caption position from the classname
++ MatchResult matcher = captionPositionRegexp.exec(captionWrap
++ .getClassName());
++ if (matcher == null || matcher.getGroupCount() < 2) {
++ return CaptionPosition.TOP;
++ }
++ String captionClass = matcher.getGroup(1);
++ CaptionPosition captionPosition = CaptionPosition.valueOf(
++ CaptionPosition.class, captionClass.toUpperCase());
++ return captionPosition;
++ }
++
++ void updateCaptionOffset(Element caption) {
++
++ Element captionWrap = caption.getParentElement().cast();
++
++ Style captionWrapStyle = captionWrap.getStyle();
++ captionWrapStyle.clearPaddingTop();
++ captionWrapStyle.clearPaddingRight();
++ captionWrapStyle.clearPaddingBottom();
++ captionWrapStyle.clearPaddingLeft();
++
++ Style captionStyle = caption.getStyle();
++ captionStyle.clearMarginTop();
++ captionStyle.clearMarginRight();
++ captionStyle.clearMarginBottom();
++ captionStyle.clearMarginLeft();
++
++ // Get caption position from the classname
++ CaptionPosition captionPosition = getCaptionPositionFromElement(captionWrap);
++
++ if (captionPosition == CaptionPosition.LEFT
++ || captionPosition == CaptionPosition.RIGHT) {
++ int captionWidth;
++ if (layoutManager != null) {
++ captionWidth = layoutManager.getOuterWidth(caption)
++ - layoutManager.getMarginWidth(caption);
++ } else {
++ captionWidth = caption.getOffsetWidth();
++ }
++ if (captionWidth > 0) {
++ if (captionPosition == CaptionPosition.LEFT) {
++ captionWrapStyle.setPaddingLeft(captionWidth, Unit.PX);
++ captionStyle.setMarginLeft(-captionWidth, Unit.PX);
++ } else {
++ captionWrapStyle.setPaddingRight(captionWidth, Unit.PX);
++ captionStyle.setMarginRight(-captionWidth, Unit.PX);
++ }
++ }
++ }
++ if (captionPosition == CaptionPosition.TOP
++ || captionPosition == CaptionPosition.BOTTOM) {
++ int captionHeight;
++ if (layoutManager != null) {
++ captionHeight = layoutManager.getOuterHeight(caption)
++ - layoutManager.getMarginHeight(caption);
++ } else {
++ captionHeight = caption.getOffsetHeight();
++ }
++ if (captionHeight > 0) {
++ if (captionPosition == CaptionPosition.TOP) {
++ captionWrapStyle.setPaddingTop(captionHeight, Unit.PX);
++ captionStyle.setMarginTop(-captionHeight, Unit.PX);
++ } else {
++ captionWrapStyle.setPaddingBottom(captionHeight, Unit.PX);
++ captionStyle.setMarginBottom(-captionHeight, Unit.PX);
++ }
++ }
++ }
++ }
++
++ private void toggleStyleName(String name, boolean enabled) {
++ if (enabled) {
++ addStyleName(name);
++ } else {
++ removeStyleName(name);
++ }
++ }
++
++ void setMargin(MarginInfo marginInfo) {
++ toggleStyleName("v-margin-top", marginInfo.hasTop());
++ toggleStyleName("v-margin-right", marginInfo.hasRight());
++ toggleStyleName("v-margin-bottom", marginInfo.hasBottom());
++ toggleStyleName("v-margin-left", marginInfo.hasLeft());
++ }
++
++ protected void setSpacing(boolean spacingEnabled) {
++ spacing = spacingEnabled;
++ for (Slot slot : widgetToSlot.values()) {
++ if (getWidgetIndex(slot) > 0) {
++ slot.setSpacing(spacingEnabled);
++ }
++ }
++ }
++
++ private void recalculateExpands() {
++ double total = 0;
++ for (Slot slot : widgetToSlot.values()) {
++ if (slot.getExpandRatio() > -1) {
++ total += slot.getExpandRatio();
++ } else {
++ if (vertical) {
++ slot.getElement().getStyle().clearHeight();
++ } else {
++ slot.getElement().getStyle().clearWidth();
++ }
++ }
++ }
++ for (Slot slot : widgetToSlot.values()) {
++ if (slot.getExpandRatio() > -1) {
++ if (vertical) {
++ slot.setHeight((100 * (slot.getExpandRatio() / total))
++ + "%");
++ if (slot.connector.isRelativeHeight()) {
++ layoutManager.setNeedsMeasure(slot.connector);
++ }
++ } else {
++ slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%");
++ if (slot.connector.isRelativeWidth()) {
++ layoutManager.setNeedsMeasure(slot.connector);
++ }
++ }
++ }
++ }
++ }
++
++ private Element expandWrapper;
++
++ void clearExpand() {
++ if (expandWrapper != null) {
++ for (; expandWrapper.getChildCount() > 0;) {
++ Element el = expandWrapper.getChild(0).cast();
++ getElement().appendChild(el);
++ if (vertical) {
++ el.getStyle().clearHeight();
++ el.getStyle().clearMarginTop();
++ } else {
++ el.getStyle().clearWidth();
++ el.getStyle().clearMarginLeft();
++ }
++ }
++ expandWrapper.removeFromParent();
++ expandWrapper = null;
++ }
++ }
++
++ public void updateExpand() {
++ boolean isExpanding = false;
++ for (Widget slot : getChildren()) {
++ if (((Slot) slot).getExpandRatio() > -1) {
++ isExpanding = true;
++ } else {
++ if (vertical) {
++ slot.getElement().getStyle().clearHeight();
++ } else {
++ slot.getElement().getStyle().clearWidth();
++ }
++ }
++ slot.getElement().getStyle().clearMarginLeft();
++ slot.getElement().getStyle().clearMarginTop();
++ }
++
++ if (isExpanding) {
++ if (expandWrapper == null) {
++ expandWrapper = DOM.createDiv();
++ expandWrapper.setClassName("v-expand");
++ for (; getElement().getChildCount() > 0;) {
++ Node el = getElement().getChild(0);
++ expandWrapper.appendChild(el);
++ }
++ getElement().appendChild(expandWrapper);
++ }
++
++ int totalSize = 0;
++ for (Widget w : getChildren()) {
++ Slot slot = (Slot) w;
++ if (slot.getExpandRatio() == -1) {
++ if (layoutManager != null) {
++ // TODO check caption position
++ if (vertical) {
++ int size = layoutManager.getOuterHeight(slot
++ .getWidget().getElement())
++ - layoutManager.getMarginHeight(slot
++ .getWidget().getElement());
++ if (slot.hasCaption()) {
++ size += layoutManager.getOuterHeight(slot
++ .getCaptionElement())
++ - layoutManager.getMarginHeight(slot
++ .getCaptionElement());
++ }
++ if (size > 0) {
++ totalSize += size;
++ }
++ } else {
++ int max = -1;
++ max = layoutManager.getOuterWidth(slot.getWidget()
++ .getElement())
++ - layoutManager.getMarginWidth(slot
++ .getWidget().getElement());
++ if (slot.hasCaption()) {
++ int max2 = layoutManager.getOuterWidth(slot
++ .getCaptionElement())
++ - layoutManager.getMarginWidth(slot
++ .getCaptionElement());
++ max = Math.max(max, max2);
++ }
++ if (max > 0) {
++ totalSize += max;
++ }
++ }
++ } else {
++ totalSize += vertical ? slot.getOffsetHeight() : slot
++ .getOffsetWidth();
++ }
++ }
++ // TODO fails in Opera, always returns 0
++ int spacingSize = slot.getSpacingSize(vertical);
++ if (spacingSize > 0) {
++ totalSize += spacingSize;
++ }
++ }
++
++ // When we set the margin to the first child, we don't need
++ // overflow:hidden in the layout root element, since the wrapper
++ // would otherwise be placed outside of the layout root element
++ // and block events on elements below it.
++ if (vertical) {
++ expandWrapper.getStyle().setPaddingTop(totalSize, Unit.PX);
++ expandWrapper.getFirstChildElement().getStyle()
++ .setMarginTop(-totalSize, Unit.PX);
++ } else {
++ expandWrapper.getStyle().setPaddingLeft(totalSize, Unit.PX);
++ expandWrapper.getFirstChildElement().getStyle()
++ .setMarginLeft(-totalSize, Unit.PX);
++ }
++
++ recalculateExpands();
++ }
++ }
++
++ public void recalculateLayoutHeight() {
++ // Only needed if a horizontal layout is undefined high, and contains
++ // relative height children or vertical alignments
++ if (vertical || definedHeight) {
++ return;
++ }
++
++ boolean hasRelativeHeightChildren = false;
++ boolean hasVAlign = false;
++
++ for (Widget slot : getChildren()) {
++ Widget widget = ((Slot) slot).getWidget();
++ String h = widget.getElement().getStyle().getHeight();
++ if (h != null && h.indexOf("%") > -1) {
++ hasRelativeHeightChildren = true;
++ }
++ AlignmentInfo a = ((Slot) slot).getAlignment();
++ if (a != null && (a.isVerticalCenter() || a.isBottom())) {
++ hasVAlign = true;
++ }
++ }
++
++ if (hasRelativeHeightChildren || hasVAlign) {
++ int newHeight;
++ if (layoutManager != null) {
++ newHeight = layoutManager.getOuterHeight(getElement())
++ - layoutManager.getMarginHeight(getElement());
++ } else {
++ newHeight = getElement().getOffsetHeight();
++ }
++ VBoxLayout.this.getElement().getStyle()
++ .setHeight(newHeight, Unit.PX);
++ }
++
++ }
++
++ void clearHeight() {
++ getElement().getStyle().clearHeight();
++ }
++
++ @Override
++ public void setHeight(String height) {
++ super.setHeight(height);
++ definedHeight = (height != null && !"".equals(height));
++ }
++}
--- /dev/null
--- /dev/null
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import com.vaadin.shared.ui.Connect;
++import com.vaadin.shared.ui.Connect.LoadStyle;
++import com.vaadin.ui.VerticalLayout;
++
++@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
++public class VerticalBoxLayoutConnector extends AbstractBoxLayoutConnector {
++
++ @Override
++ public void init() {
++ super.init();
++ getWidget().setVertical(true);
++ }
++
++}
--- /dev/null
-import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.Connect.LoadStyle;
-import com.vaadin.ui.VerticalLayout;
+ /*
+ * Copyright 2011 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.orderedlayout;
+
-@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
+
++//@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
+ public class VerticalLayoutConnector extends AbstractOrderedLayoutConnector {
+
+ @Override
+ public VVerticalLayout getWidget() {
+ return (VVerticalLayout) super.getWidget();
+ }
+
+ }
+++ /dev/null
--<module>
- <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module
- you want to extend when creating an extended widget set, or when creating
- a specialized widget set with a subset of the components. -->
-
- <!-- Hint for WidgetSetBuilder not to automatically update the file -->
- <!-- WS Compiler: manually edited -->
-
- <inherits name="com.vaadin.Vaadin" />
-
- <entry-point class="com.vaadin.terminal.gwt.client.ApplicationConfiguration" />
- <!-- This GWT module is provided for backwards compatibility only. You
- should inherit com.vaadin.DefaultWidgetSet instead. -->
--
- <!-- Hint for WidgetSetBuilder not to automatically update the file -->
- <!-- WS Compiler: manually edited -->
- <inherits name="com.vaadin.DefaultWidgetSet" />
-
--</module>
--- /dev/null
- import com.vaadin.terminal.ThemeResource;
- import com.vaadin.terminal.UserError;
- import com.vaadin.terminal.WrappedRequest;
+package com.vaadin.tests.components.orderedlayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.event.LayoutEvents.LayoutClickEvent;
+import com.vaadin.event.LayoutEvents.LayoutClickListener;
++import com.vaadin.server.ThemeResource;
++import com.vaadin.server.UserError;
++import com.vaadin.server.WrappedRequest;
+import com.vaadin.shared.ui.label.ContentMode;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractField;
+import com.vaadin.ui.AbstractOrderedLayout;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.themes.Reindeer;
+
+@Theme("tests-components")
+public class BoxLayoutTest extends AbstractTestUI {
+
+ protected AbstractOrderedLayout view;
+
+ protected AbstractOrderedLayout l;
+
+ protected AbstractComponent target;
+
+ protected NativeSelect componentWidth;
+ protected NativeSelect componentHeight;
+ protected NativeSelect componentCaption;
+ protected NativeSelect componentIcon;
+ protected TextField componentDescription;
+ protected CheckBox componentError;
+ protected CheckBox componentRequired;
+
+ protected NativeSelect align;
+ protected CheckBox expand;
+
+ @Override
+ protected void setup(WrappedRequest request) {
+
+ view = new VerticalLayout();
+ // view.setSizeFull();
+ view.setMargin(true);
+ view.setSpacing(true);
+
+ // view.addComponent(createControls(false));
+ // view.addComponent(createTestLayout(false));
+ // view.setExpandRatio(view.getComponent(1), 1);
+
+ for (int i = 0; i < 20; i++) {
+ view.addComponent(createHorizontalTest());
+ }
+
+ setContent(view);
+ getApplication().setUiPreserved(true);
+ }
+
+ private Component createHorizontalTest() {
+ HorizontalLayout l = new HorizontalLayout();
+ l.setWidth("100%");
+ l.setSpacing(true);
+
+ Label exp;
+
+ // l.addComponent(new Embedded(null, new ThemeResource(
+ // "../runo/icons/32/document.png")));
+ l.addComponent(exp = new Label(
+ "Mauris iaculis porttitor posuere. Praesent id metus massa, ut blandit odio. Proin quis tortor orci. Etiam at risus et justo dignissim congue. Donec."));
+ // exp.setWidth("300px");
+ l.addComponent(new Button("Edit"));
+ l.addComponent(new Button("Delete"));
+ l.setExpandRatio(exp, 1);
+
+ for (int i = 0; i < l.getComponentCount(); i++) {
+ l.setComponentAlignment(l.getComponent(i), Alignment.MIDDLE_LEFT);
+ }
+
+ return l;
+ }
+
+ protected AbstractOrderedLayout createControls(boolean horizontal) {
+ VerticalLayout root = new VerticalLayout();
+ root.setSpacing(true);
+
+ // First row
+ HorizontalLayout header = new HorizontalLayout();
+ header.setSpacing(true);
+ root.addComponent(header);
+
+ Label title = new Label("BoxLayout Test");
+ title.addStyleName(Reindeer.LABEL_H1);
+ header.addComponent(title);
+
+ final CheckBox vertical = new CheckBox("Vertical", !horizontal);
+ vertical.setImmediate(true);
+ vertical.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ view.removeAllComponents();
+
+ view.addComponent(createControls(!vertical.getValue()
+ .booleanValue()));
+ view.addComponent(createTestLayout(!vertical.getValue()
+ .booleanValue()));
+
+ view.setExpandRatio(view.getComponent(1), 1);
+
+ }
+ });
+ header.addComponent(vertical);
+
+ Button addComponent = new Button("Add Component",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ GridLayout grid = new GridLayout(2, 2);
+ Button grow = new Button("Grow Me",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ if (event.getButton().getWidth() == -1) {
+ event.getButton().setHeight("50px");
+ event.getButton().setWidth("200px");
+ } else {
+ event.getButton()
+ .setSizeUndefined();
+ }
+ }
+ });
+ grid.addComponent(new Label("Grid cell 1"));
+ grid.addComponent(new Label("Grid cell 2"));
+ grid.addComponent(grow);
+ grid.addComponent(new Label("Grid cell 4"));
+ l.addComponent(grid);
+ // l.addComponent(new TextField("Some field"));
+ }
+ });
+ header.addComponent(addComponent);
+
+ Button removeComponent = new Button("Remove Component",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Component last = l.getComponent(l.getComponentCount() - 1);
+ l.removeComponent(last);
+ }
+ });
+ header.addComponent(removeComponent);
+
+ // Second row
+ HorizontalLayout controls = new HorizontalLayout();
+ controls.setSpacing(true);
+ root.addComponent(controls);
+
+ // Layout controls
+ HorizontalLayout layout = new HorizontalLayout();
+ layout.addStyleName("fieldset");
+ layout.setSpacing(true);
+ controls.addComponent(layout);
+ layout.addComponent(new Label("Layout"));
+
+ ArrayList<String> sizes = new ArrayList<String>();
+ sizes.addAll(Arrays.asList("100px", "30em", "100%"));
+
+ final NativeSelect width = new NativeSelect(null, sizes);
+ width.setImmediate(true);
+ width.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (width.getValue() != null) {
+ l.setWidth(width.getValue().toString());
+ } else {
+ l.setWidth(null);
+ }
+ }
+ });
+ layout.addComponent(width);
+ layout.addComponent(new Label("×", ContentMode.XHTML));
+ final NativeSelect height = new NativeSelect(null, sizes);
+ height.setImmediate(true);
+ height.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (height.getValue() != null) {
+ l.setHeight(height.getValue().toString());
+ } else {
+ l.setHeight(null);
+ }
+ }
+ });
+ layout.addComponent(height);
+
+ final CheckBox margin = new CheckBox("Margin", false);
+ margin.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ l.setMargin(margin.getValue().booleanValue());
+ }
+ });
+ margin.setImmediate(true);
+ layout.addComponent(margin);
+ layout.addComponent(margin);
+
+ final CheckBox spacing = new CheckBox("Spacing", false);
+ spacing.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ l.setSpacing(spacing.getValue().booleanValue());
+ }
+ });
+ spacing.setImmediate(true);
+ layout.addComponent(spacing);
+
+ // Cell controls
+ HorizontalLayout cell = new HorizontalLayout();
+ cell.addStyleName("fieldset");
+ cell.setSpacing(true);
+ controls.addComponent(cell);
+ cell.addComponent(new Label("Cell"));
+
+ ArrayList<Alignment> alignments = new ArrayList<Alignment>();
+ alignments.addAll(Arrays.asList(Alignment.TOP_LEFT,
+ Alignment.MIDDLE_LEFT, Alignment.BOTTOM_LEFT,
+ Alignment.TOP_CENTER, Alignment.MIDDLE_CENTER,
+ Alignment.BOTTOM_CENTER, Alignment.TOP_RIGHT,
+ Alignment.MIDDLE_RIGHT, Alignment.BOTTOM_RIGHT));
+
+ align = new NativeSelect(null, alignments);
+ for (Alignment a : alignments) {
+ align.setItemCaption(a,
+ a.getVerticalAlignment() + "-" + a.getHorizontalAlignment());
+ }
+ align.setImmediate(true);
+ align.setEnabled(false);
+ align.setNullSelectionAllowed(false);
+ align.select(Alignment.TOP_LEFT);
+ align.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (target == null) {
+ return;
+ }
+ l.setComponentAlignment(target, ((Alignment) align.getValue()));
+ }
+ });
+ cell.addComponent(align);
+
+ expand = new CheckBox("Expand");
+ expand.setImmediate(true);
+ expand.setEnabled(false);
+ expand.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null) {
+ l.setExpandRatio(target, expand.getValue() ? 1 : 0);
+ }
+ }
+ });
+ cell.addComponent(expand);
+
+ // Component controls
+ HorizontalLayout component = new HorizontalLayout();
+ component.addStyleName("fieldset");
+ component.setSpacing(true);
+ root.addComponent(component);
+ component.addComponent(new Label("Component"));
+
+ sizes = new ArrayList<String>();
+ sizes.addAll(Arrays.asList("50px", "200px", "10em", "50%", "100%"));
+
+ componentWidth = new NativeSelect(null, sizes);
+ componentWidth.setImmediate(true);
+ componentWidth.setEnabled(false);
+ componentWidth.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (target == null) {
+ return;
+ }
+ if (componentWidth.getValue() != null) {
+ target.setWidth(componentWidth.getValue().toString());
+ } else {
+ target.setWidth(null);
+ }
+ }
+ });
+ component.addComponent(componentWidth);
+ component.addComponent(new Label("×", ContentMode.XHTML));
+
+ componentHeight = new NativeSelect(null, sizes);
+ componentHeight.setImmediate(true);
+ componentHeight.setEnabled(false);
+ componentHeight.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (componentHeight.getValue() != null) {
+ target.setHeight(componentHeight.getValue().toString());
+ } else {
+ target.setHeight(null);
+ }
+ }
+ });
+ component.addComponent(componentHeight);
+
+ componentCaption = new NativeSelect("Caption", Arrays.asList("Short",
+ "Slightly Longer Caption"));
+ componentCaption.setImmediate(true);
+ componentCaption.setEnabled(false);
+ componentCaption.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (componentCaption.getValue() != null) {
+ target.setCaption(componentCaption.getValue().toString());
+ } else {
+ target.setCaption(null);
+ }
+ }
+ });
+ component.addComponent(componentCaption);
+
+ componentIcon = new NativeSelect("Icon", Arrays.asList(
+ "../runo/icons/16/folder.png", "../runo/icons/32/document.png"));
+ componentIcon.setImmediate(true);
+ componentIcon.setEnabled(false);
+ componentIcon.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (componentIcon.getValue() != null) {
+ target.setIcon(new ThemeResource(componentIcon.getValue()
+ .toString()));
+ } else {
+ target.setIcon(null);
+ }
+ }
+ });
+ component.addComponent(componentIcon);
+
+ componentDescription = new TextField("Description");
+ componentDescription.setImmediate(true);
+ componentDescription.setEnabled(false);
+ componentDescription.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ target.setDescription(componentDescription.getValue());
+ }
+ });
+ component.addComponent(componentDescription);
+
+ componentError = new CheckBox("Error");
+ componentError.setImmediate(true);
+ componentError.setEnabled(false);
+ componentError.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null) {
+ target.setComponentError(componentError.getValue() ? new UserError(
+ "Error message") : null);
+ }
+ }
+ });
+ component.addComponent(componentError);
+
+ componentRequired = new CheckBox("Required");
+ componentRequired.setImmediate(true);
+ componentRequired.setEnabled(false);
+ componentRequired.addListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null && target instanceof AbstractField) {
+ ((AbstractField<?>) target).setRequired(componentRequired
+ .getValue());
+ }
+ }
+ });
+ component.addComponent(componentRequired);
+
+ for (int i = 0; i < component.getComponentCount(); i++) {
+ component.setComponentAlignment(component.getComponent(i),
+ Alignment.MIDDLE_LEFT);
+ }
+
+ return root;
+ }
+
+ protected AbstractOrderedLayout createTestLayout(boolean horizontal) {
+ l = horizontal ? new HorizontalLayout() : new VerticalLayout();
+ l.setSizeUndefined();
+ l.addStyleName("test");
+
+ Label label = new Label("Component 1");
+ l.addComponent(label);
+ l.addComponent(new Button("Component 2"));
+
+ l.addListener(new LayoutClickListener() {
+ @Override
+ public void layoutClick(LayoutClickEvent event) {
+ if (event.getChildComponent() == null
+ || target == event.getChildComponent()) {
+ if (target != null) {
+ target.removeStyleName("target");
+ }
+ target = null;
+ } else if (target != event.getChildComponent()) {
+ if (target != null) {
+ target.removeStyleName("target");
+ }
+ target = (AbstractComponent) event.getChildComponent();
+ target.addStyleName("target");
+ }
+ componentWidth.setEnabled(target != null);
+ componentHeight.setEnabled(target != null);
+ componentCaption.setEnabled(target != null);
+ componentIcon.setEnabled(target != null);
+ componentDescription.setEnabled(target != null);
+ componentError.setEnabled(target != null);
+ componentRequired.setEnabled(target != null
+ && target instanceof AbstractField);
+ align.setEnabled(target != null);
+ expand.setEnabled(target != null);
+ if (target != null) {
+ if (target.getWidth() > -1) {
+ componentWidth.select(new Float(target.getWidth())
+ .intValue()
+ + target.getWidthUnits().getSymbol());
+ } else {
+ componentWidth.select(null);
+ }
+ if (target.getHeight() > -1) {
+ componentHeight.select(new Float(target.getHeight())
+ .intValue()
+ + target.getHeightUnits().getSymbol());
+ } else {
+ componentHeight.select(null);
+ }
+
+ align.select(l.getComponentAlignment(target));
+ expand.setValue(new Boolean(l.getExpandRatio(target) > 0));
+
+ componentCaption.select(target.getCaption());
+ if (target.getIcon() != null) {
+ componentIcon.select(((ThemeResource) target.getIcon())
+ .getResourceId());
+ } else {
+ componentIcon.select(null);
+ }
+ componentDescription.setValue(target.getDescription());
+ componentError.setValue(target.getComponentError() != null);
+ if (target instanceof AbstractField) {
+ componentRequired.setValue(((AbstractField<?>) target)
+ .isRequired());
+ }
+ }
+ }
+ });
+
+ target = null;
+
+ return l;
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
--- /dev/null
- import com.vaadin.terminal.ThemeResource;
+package com.vaadin.tests.components.orderedlayout;
+
+import com.vaadin.Application.LegacyApplication;
++import com.vaadin.server.ThemeResource;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.themes.Reindeer;
+
+public class LayoutRenderTimeTest extends LegacyApplication {
+
+ @Override
+ public void init() {
+ UI.LegacyWindow main = new UI.LegacyWindow();
+ setMainWindow(main);
+
+ VerticalLayout root = new VerticalLayout();
+ root.setWidth("100%");
+ main.setContent(root);
+
+ for (int i = 1; i <= 100; i++) {
+ root.addComponent(getRow(i));
+ }
+ }
+
+ private HorizontalLayout getRow(int i) {
+ HorizontalLayout row = new HorizontalLayout();
+ // row.setWidth("100%");
+ // row.setSpacing(true);
+
+ Embedded icon = new Embedded(null, new ThemeResource(
+ "../runo/icons/32/document.png"));
+ // row.addComponent(icon);
+ // row.setComponentAlignment(icon, Alignment.MIDDLE_LEFT);
+
+ Label text = new Label(
+ "Row content #"
+ + i
+ + ". In pellentesque faucibus vestibulum. Nulla at nulla justo, eget luctus tortor. Nulla facilisi. Duis aliquet.");
+ // row.addComponent(text);
+ // row.setExpandRatio(text, 1);
+
+ Button button = new Button("Edit");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ // row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ button = new Button("Delete");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ // row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ return row;
+ }
+}
--- /dev/null
- import com.vaadin.terminal.Sizeable;
- import com.vaadin.terminal.ThemeResource;
- import com.vaadin.terminal.WrappedRequest;
+package com.vaadin.tests.components.orderedlayout;
+
+import com.vaadin.annotations.Theme;
++import com.vaadin.server.Sizeable;
++import com.vaadin.server.ThemeResource;
++import com.vaadin.server.WrappedRequest;
+import com.vaadin.shared.ui.MarginInfo;
+import com.vaadin.shared.ui.slider.SliderOrientation;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.HorizontalSplitPanel;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.NativeButton;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.Slider;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.VerticalLayout;
+
+@Theme("tests-components")
+public class VaadinTunesLayout extends AbstractTestUI {
+
+ @Override
+ public void setup(WrappedRequest request) {
+
+ /*
+ * We'll build the whole UI here, since the application will not contain
+ * any logic. Otherwise it would be more practical to separate parts of
+ * the UI into different classes and methods.
+ */
+
+ // Main (browser) window, needed in all Vaadin applications
+ VerticalLayout rootLayout = new VerticalLayout();
+ // final Window root = new Window("VaadinTunes", rootLayout);
+
+ /*
+ * We'll attach the window to the browser view already here, so we won't
+ * forget it later.
+ */
+ setContent(rootLayout);
+
+ // root.showNotification(
+ // "This is an example of how you can do layouts in Vaadin.<br/>It is not a working sound player.",
+ // Notification.TYPE_HUMANIZED_MESSAGE);
+
+ // Our root window contains one VerticalLayout, let's make
+ // sure it's 100% sized, and remove unwanted margins
+ rootLayout.setSizeFull();
+ rootLayout.setMargin(false);
+
+ // Top area, containing playback and volume controls, play status, view
+ // modes and search
+ HorizontalLayout top = new HorizontalLayout();
+ top.setWidth("100%");
+ top.setMargin(new MarginInfo(false, true, false, true)); // Enable
+ // horizontal
+ // margins
+ top.setSpacing(true);
+
+ // Let's attach that one straight away too
+ rootLayout.addComponent(top);
+
+ // Create the placeholders for all the components in the top area
+ HorizontalLayout playback = new HorizontalLayout();
+ HorizontalLayout volume = new HorizontalLayout();
+ HorizontalLayout status = new HorizontalLayout();
+ HorizontalLayout viewmodes = new HorizontalLayout();
+ ComboBox search = new ComboBox();
+
+ // Add the components and align them properly
+ top.addComponent(playback);
+ top.addComponent(volume);
+ top.addComponent(status);
+ top.addComponent(viewmodes);
+ top.addComponent(search);
+ top.setComponentAlignment(playback, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(volume, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(status, Alignment.MIDDLE_CENTER);
+ top.setComponentAlignment(viewmodes, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(search, Alignment.MIDDLE_LEFT);
+
+ /*
+ * We want our status area to expand if the user resizes the root
+ * window, and we want it to accommodate as much space as there is
+ * available. All other components in the top layout should stay fixed
+ * sized, so we don't need to specify any expand ratios for them (they
+ * will automatically revert to zero after the following line).
+ */
+ top.setExpandRatio(status, 1.0F);
+
+ // Playback controls
+ Button prev = new NativeButton("Previous");
+ Button play = new NativeButton("Play/pause");
+ Button next = new NativeButton("Next");
+ playback.addComponent(prev);
+ playback.addComponent(play);
+ playback.addComponent(next);
+ // Set spacing between the buttons
+ playback.setSpacing(true);
+
+ // Volume controls
+ Button mute = new NativeButton("mute");
+ Slider vol = new Slider();
+ vol.setOrientation(SliderOrientation.HORIZONTAL);
+ vol.setWidth("100px");
+ Button max = new NativeButton("max");
+ volume.addComponent(mute);
+ volume.addComponent(vol);
+ volume.addComponent(max);
+
+ // Status area
+ status.setWidth("80%");
+ status.setSpacing(true);
+
+ Button toggleVisualization = new NativeButton("Mode");
+ Label timeFromStart = new Label("0:00");
+
+ // We'll need another layout to show currently playing track and
+ // progress
+ VerticalLayout trackDetails = new VerticalLayout();
+ trackDetails.setWidth("100%");
+ Label track = new Label("Track Name");
+ Label album = new Label("Album Name - Artist");
+ track.setWidth(null);
+ album.setWidth(null);
+ Slider progress = new Slider();
+ progress.setOrientation(SliderOrientation.HORIZONTAL);
+ progress.setWidth("100%");
+ trackDetails.addComponent(track);
+ trackDetails.addComponent(album);
+ trackDetails.addComponent(progress);
+ trackDetails.setComponentAlignment(track, Alignment.TOP_CENTER);
+ trackDetails.setComponentAlignment(album, Alignment.TOP_CENTER);
+
+ Label timeToEnd = new Label("-4:46");
+ Button jumpToTrack = new NativeButton("Show");
+
+ // Place all components to the status layout and align them properly
+ status.addComponent(toggleVisualization);
+ status.setComponentAlignment(toggleVisualization, Alignment.MIDDLE_LEFT);
+ status.addComponent(timeFromStart);
+ status.setComponentAlignment(timeFromStart, Alignment.BOTTOM_LEFT);
+ status.addComponent(trackDetails);
+ status.addComponent(timeToEnd);
+ status.setComponentAlignment(timeToEnd, Alignment.BOTTOM_LEFT);
+ status.addComponent(jumpToTrack);
+ status.setComponentAlignment(jumpToTrack, Alignment.MIDDLE_LEFT);
+
+ // Then remember to specify the expand ratio
+ status.setExpandRatio(trackDetails, 1.0F);
+
+ // View mode buttons
+ Button viewAsTable = new NativeButton("Table");
+ Button viewAsGrid = new NativeButton("Grid");
+ Button coverflow = new NativeButton("Coverflow");
+ viewmodes.addComponent(viewAsTable);
+ viewmodes.addComponent(viewAsGrid);
+ viewmodes.addComponent(coverflow);
+
+ /*
+ * That covers the top bar. Now let's move on to the sidebar and track
+ * listing
+ */
+
+ // We'll need one splitpanel to separate the sidebar and track listing
+ HorizontalSplitPanel bottom = new HorizontalSplitPanel();
+ rootLayout.addComponent(bottom);
+
+ // The splitpanel is by default 100% x 100%, but we'll need to adjust
+ // our main window layout to accomodate the height
+ rootLayout.setExpandRatio(bottom, 1.0F);
+
+ // Give the sidebar less space than the listing
+ bottom.setSplitPosition(200, Sizeable.UNITS_PIXELS);
+
+ // Let's add some content to the sidebar
+ // First, we need a layout to but all components in
+ VerticalLayout sidebar = new VerticalLayout();
+ sidebar.setSizeFull();
+ bottom.setFirstComponent(sidebar);
+
+ /*
+ * Then we need some labels and buttons, and an album cover image The
+ * labels and buttons go into their own vertical layout, since we want
+ * the 'sidebar' layout to be expanding (cover image in the bottom).
+ * VerticalLayout is by default 100% wide.
+ */
+ VerticalLayout selections = new VerticalLayout();
+ Label library = new Label("Library");
+ Button music = new NativeButton("Music");
+ music.setWidth("100%");
+
+ Label store = new Label("Store");
+ Button vaadinTunesStore = new NativeButton("VaadinTunes Store");
+ vaadinTunesStore.setWidth("100%");
+ Button purchased = new NativeButton("Purchased");
+ purchased.setWidth("100%");
+
+ Label playlists = new Label("Playlists");
+ Button genius = new NativeButton("Geniues");
+ genius.setWidth("100%");
+ Button recent = new NativeButton("Recently Added");
+ recent.setWidth("100%");
+
+ // Lets add them to the 'selections' layout
+ selections.addComponent(library);
+ selections.addComponent(music);
+ selections.addComponent(store);
+ selections.addComponent(vaadinTunesStore);
+ selections.addComponent(purchased);
+ selections.addComponent(playlists);
+ selections.addComponent(genius);
+ selections.addComponent(recent);
+
+ // Then add the selections to the sidebar, and set it expanding
+ sidebar.addComponent(selections);
+ sidebar.setExpandRatio(selections, 1.0F);
+
+ // Then comes the cover artwork (we'll add the actual image in the
+ // themeing section)
+ Embedded cover = new Embedded("Currently Playing");
+ sidebar.addComponent(cover);
+
+ /*
+ * And lastly, we need the track listing table It should fill the whole
+ * left side of our bottom layout
+ */
+ Table listing = new Table();
+ listing.setSizeFull();
+ listing.setSelectable(true);
+ bottom.setSecondComponent(listing);
+
+ // Add the table headers
+ listing.addContainerProperty("Name", String.class, "");
+ listing.addContainerProperty("Time", String.class, "0:00");
+ listing.addContainerProperty("Artist", String.class, "");
+ listing.addContainerProperty("Album", String.class, "");
+ listing.addContainerProperty("Genre", String.class, "");
+ listing.addContainerProperty("Rating", NativeSelect.class,
+ new NativeSelect());
+
+ // Lets populate the table with random data
+ String[] tracks = new String[] { "Red Flag", "Millstone",
+ "Not The Sun", "Breath", "Here We Are", "Deep Heaven",
+ "Her Voice Resides", "Natural Tan", "End It All", "Kings",
+ "Daylight Slaving", "Mad Man", "Resolve", "Teargas",
+ "African Air", "Passing Bird" };
+ String[] times = new String[] { "4:12", "6:03", "5:43", "4:32", "3:42",
+ "4:45", "2:56", "9:34", "2:10", "3:44", "5:49", "6:30", "5:18",
+ "7:42", "3:13", "2:52" };
+ String[] artists = new String[] { "Billy Talent", "Brand New",
+ "Breaking Benjamin", "Becoming The Archetype",
+ "Bullet For My Valentine", "Chasing Victory", "Chimaira",
+ "Danko Jones", "Deadlock", "Deftones", "From Autumn To Ashes",
+ "Haste The Day", "Four Year Strong", "In Flames", "Kemopetrol",
+ "John Legend" };
+ String[] albums = new String[] { "Once Again", "The Caitiff Choir",
+ "The Devil And God", "Light Grenades", "Dicthonomy",
+ "Back In Black", "Dreamer", "Come Clarity", "Year Zero",
+ "Frames", "Fortress", "Phobia", "The Poison", "Manifesto",
+ "White Pony", "The Big Dirty" };
+ String[] genres = new String[] { "Rock", "Metal", "Hardcore", "Indie",
+ "Pop", "Alternative", "Blues", "Jazz", "Hip Hop",
+ "Electronica", "Punk", "Hard Rock", "Dance", "R'n'B", "Gospel",
+ "Country" };
+ for (int i = 0; i < 1000; i++) {
+ NativeSelect s = new NativeSelect();
+ s.addItem("1 star");
+ s.addItem("2 stars");
+ s.addItem("3 stars");
+ s.addItem("4 stars");
+ s.addItem("5 stars");
+ s.select(i % 5 + " stars");
+ final int index = i % 16;
+ listing.addItem(new Object[] { tracks[index], times[index],
+ artists[index], albums[index], genres[index], s }, i);
+ }
+
+ // We'll align the track time column to right as well
+ listing.setColumnAlignment("Time", Table.ALIGN_RIGHT);
+
+ // TODO the footer
+
+ // Now what's left to do? Themeing of course.
+ // setTheme("vaadintunes");
+
+ /*
+ * Let's give a namespace to our application window. This way, if
+ * someone uses the same theme for different applications, we don't get
+ * unwanted style conflicts.
+ */
+ // root.setStyleName("tTunes");
+
+ top.setStyleName("top");
+ top.setHeight("75px"); // Same as the background image height
+
+ playback.setStyleName("playback");
+ playback.setMargin(new MarginInfo(false, true, false, false)); // Add
+ // right-side
+ // margin
+ play.setStyleName("play");
+ next.setStyleName("next");
+ prev.setStyleName("prev");
+ playback.setComponentAlignment(prev, Alignment.MIDDLE_LEFT);
+ playback.setComponentAlignment(next, Alignment.MIDDLE_LEFT);
+
+ volume.setStyleName("volume");
+ mute.setStyleName("mute");
+ max.setStyleName("max");
+ vol.setWidth("78px");
+
+ status.setStyleName("status");
+ status.setMargin(true);
+ status.setHeight("46px"); // Height of the background image
+
+ toggleVisualization.setStyleName("toggle-vis");
+ jumpToTrack.setStyleName("jump");
+
+ viewAsTable.setStyleName("viewmode-table");
+ viewAsGrid.setStyleName("viewmode-grid");
+ coverflow.setStyleName("viewmode-coverflow");
+
+ sidebar.setStyleName("sidebar");
+
+ music.setStyleName("selected");
+
+ cover.setSource(new ThemeResource("images/album-cover.jpg"));
+ // Because this is an image, it will retain it's aspect ratio
+ cover.setWidth("100%");
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
--- /dev/null
- import com.vaadin.terminal.WrappedRequest;
+package com.vaadin.tests.themes;
+
+import com.vaadin.annotations.Theme;
++import com.vaadin.server.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.themes.LiferayTheme;
+
+@Theme("liferay")
+public class LiferayThemeTest extends AbstractTestUI {
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ Panel p = new Panel("Panel");
+ addComponent(p);
+ p.addComponent(new Label("Panel content"));
+
+ p = new Panel("Light Panel");
+ p.addStyleName(LiferayTheme.PANEL_LIGHT);
+ addComponent(p);
+ p.addComponent(new Label("Panel content"));
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
--- /dev/null
- // TODO Auto-generated method stub
- return null;
+ package com.vaadin.tests.components.flash;
+
+ import com.vaadin.server.ExternalResource;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Flash;
+
+ public class FlashIsVisible extends TestBase {
+
+ @Override
+ protected void setup() {
+ Flash player = new Flash();
+ player.setWidth("400px");
+ player.setHeight("300px");
+ String url = "http://www.youtube.com/v/qQ9N742QB4g&autoplay=0";
+ player.setSource(new ExternalResource(url));
+ addComponent(player);
+ }
+
+ @Override
+ protected String getDescription() {
++ return "Flash plugin should load and be visible on all browsers";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
--- /dev/null
-import com.vaadin.ui.JavaScript;
+ package com.vaadin.tests.components.orderedlayout;
+
+ import com.vaadin.server.ThemeResource;
+ import com.vaadin.shared.ui.label.ContentMode;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Alignment;
+ import com.vaadin.ui.Button;
+ import com.vaadin.ui.Button.ClickEvent;
+ import com.vaadin.ui.Embedded;
+ import com.vaadin.ui.HorizontalLayout;
+ import com.vaadin.ui.HorizontalSplitPanel;
- @Override
- public void buttonClick(ClickEvent event) {
- JavaScript
- .getCurrent()
- .execute(
- "setTimeout(function() {window.resizeTo(700,400)}, 500)");
- }
- });
+ import com.vaadin.ui.Label;
+ import com.vaadin.ui.Table;
+ import com.vaadin.ui.VerticalLayout;
+ import com.vaadin.ui.VerticalSplitPanel;
+ import com.vaadin.ui.themes.Reindeer;
+
+ public class LayoutResizeTest extends TestBase {
+
+ @Override
+ protected void setup() {
+ getLayout().setSizeFull();
+
+ HorizontalSplitPanel split1 = new HorizontalSplitPanel();
+ split1.setSizeFull();
+ addComponent(split1);
+
+ VerticalLayout left = new VerticalLayout();
+ left.setSizeFull();
+ split1.setFirstComponent(left);
+
+ left.setSpacing(true);
+ left.setMargin(true);
+
+ left.addComponent(new Label("<h2>Layout resize test</h2>",
+ ContentMode.XHTML));
+
+ Button resize = new Button("Resize to 700x400",
+ new Button.ClickListener() {
- JavaScript
- .getCurrent()
- .execute(
- "setTimeout(function() {window.resizeTo(900,600)}, 500)");
++ @Override
++ public void buttonClick(ClickEvent event) {
++ event.getButton()
++ .getUI()
++ .getPage()
++ .getJavaScript()
++ .execute(
++ "setTimeout(function() {window.resizeTo(700,400)}, 500)");
++ }
++ });
+ left.addComponent(resize);
+
+ resize = new Button("Resize to 900x600", new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
++ event.getButton()
++ .getUI()
++ .getPage()
++ .getJavaScript()
++ .execute(
++ "setTimeout(function() {window.resizeTo(900,600)}, 500)");
+ }
+ });
+ left.addComponent(resize);
+
+ left.addComponent(new Label(
+ "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin vel ante a orci tempus eleifend ut et magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus urna sed urna ultricies."));
+
+ Table table1 = new Table();
+ table1.setSizeFull();
+ table1.addContainerProperty("Column", String.class, "");
+ for (int i = 1; i <= 100; i++) {
+ table1.addItem(new Object[] { "Value " + i }, i);
+ }
+ left.addComponent(table1);
+ left.setExpandRatio(table1, 1);
+
+ VerticalSplitPanel split2 = new VerticalSplitPanel();
+ split2.setSizeFull();
+ split1.setSecondComponent(split2);
+
+ Table table2 = new Table();
+ table2.setSizeFull();
+ table2.addContainerProperty("Column 1", String.class, "");
+ table2.addContainerProperty("Column 2", String.class, "");
+ table2.addContainerProperty("Column 3", String.class, "");
+ table2.addContainerProperty("Column 4", String.class, "");
+ for (int i = 1; i <= 100; i++) {
+ table2.addItem(new Object[] { "Value " + i, "Value " + i,
+ "Value " + i, "Value " + i }, i);
+ }
+ split2.setFirstComponent(table2);
+
+ VerticalLayout rows = new VerticalLayout();
+ rows.setWidth("100%");
+ rows.setSpacing(true);
+ rows.setMargin(true);
+ for (int i = 1; i <= 100; i++) {
+ rows.addComponent(getRow(i));
+ }
+ split2.setSecondComponent(rows);
+ }
+
+ private HorizontalLayout getRow(int i) {
+ HorizontalLayout row = new HorizontalLayout();
+ row.setWidth("100%");
+ row.setSpacing(true);
+
+ Embedded icon = new Embedded(null, new ThemeResource(
+ "../runo/icons/32/document.png"));
+ row.addComponent(icon);
+ row.setComponentAlignment(icon, Alignment.MIDDLE_LEFT);
+
+ Label text = new Label(
+ "Row content #"
+ + i
+ + ". In pellentesque faucibus vestibulum. Nulla at nulla justo, eget luctus tortor. Nulla facilisi. Duis aliquet.");
+ row.addComponent(text);
+ row.setExpandRatio(text, 1);
+
+ Button button = new Button("Edit");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ button = new Button("Delete");
+ button.addStyleName(Reindeer.BUTTON_SMALL);
+ row.addComponent(button);
+ row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+
+ return row;
+ }
+
+ @Override
+ protected String getDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
--- /dev/null
- "800px", "100%", "50%" };
+ package com.vaadin.tests.components.orderedlayout;
+
+ import java.lang.reflect.Field;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.List;
+
++import com.vaadin.annotations.Theme;
+ import com.vaadin.data.Property.ValueChangeEvent;
+ import com.vaadin.data.Property.ValueChangeListener;
+ import com.vaadin.server.WrappedRequest;
+ import com.vaadin.tests.components.AbstractTestUI;
+ import com.vaadin.tests.util.TestUtils;
+ import com.vaadin.ui.AbstractOrderedLayout;
+ import com.vaadin.ui.Alignment;
+ 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.HorizontalLayout;
+ import com.vaadin.ui.NativeSelect;
+ import com.vaadin.ui.VerticalLayout;
+
++@Theme("tests-components")
+ public class OrderedLayoutCases extends AbstractTestUI {
++
+ private static final String[] dimensionValues = { "-1px", "5px", "350px",
- @Override
- public void valueChange(ValueChangeEvent event) {
- setWidth(event.getProperty().getValue().toString());
- }
- }, dimensionValues));
++ "800px", "100%", "50%" };
+
+ private static class SampleChild extends VerticalLayout {
+ public SampleChild() {
+ setStyleName("sampleChild");
+ addComponent(createSimpleSelector("Child width",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- setHeight(event.getProperty().getValue().toString());
- }
- }, dimensionValues));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ setWidth(event.getProperty().getValue().toString());
++ }
++ }, dimensionValues));
+ addComponent(createSimpleSelector("Child height",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- String value = event.getProperty().getValue()
- .toString();
- if (value.length() == 0) {
- setCaption(null);
- } else if (value.equals("Long")) {
- setCaption("A rather long caption just to see what happens");
- } else {
- setCaption(value);
- }
- }
- }, "", "Short", "Long"));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ setHeight(event.getProperty().getValue().toString());
++ }
++ }, dimensionValues));
+ addComponent(createSimpleSelector("Caption",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- AbstractOrderedLayout parent = (AbstractOrderedLayout) getParent();
- if (parent == null) {
- return;
- }
- String value = event.getProperty().getValue()
- .toString();
- parent.setExpandRatio(SampleChild.this,
- Float.parseFloat(value));
- }
- }, "0", "1", "2"));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ String value = event.getProperty().getValue()
++ .toString();
++ if (value.length() == 0) {
++ setCaption(null);
++ } else if (value.equals("Long")) {
++ setCaption("A rather long caption just to see what happens");
++ } else {
++ setCaption(value);
++ }
++ }
++ }, "", "Short", "Long"));
+
+ addComponent(createSimpleSelector("Expand ratio",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- String value = event.getProperty().getValue()
- .toString();
- AlignmentHandler parent = (AlignmentHandler) getParent();
- if (parent == null) {
- return;
- }
- try {
- Field field = Alignment.class
- .getDeclaredField(value);
- Alignment alignment = (Alignment) field
- .get(null);
- parent.setComponentAlignment(SampleChild.this,
- alignment);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }, alignmentValues, "TOP_LEFT")); // Sorry for not using
- // more reflection magic
- // just to find the
- // default value...
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ AbstractOrderedLayout parent = (AbstractOrderedLayout) getParent();
++ if (parent == null) {
++ return;
++ }
++ String value = event.getProperty().getValue()
++ .toString();
++ parent.setExpandRatio(SampleChild.this,
++ Float.parseFloat(value));
++ }
++ }, "0", "1", "2"));
+
+ // Why is Alignment not an enum? Now we have to use reflection just
+ // to get the different values as hardcoding is never an option! ;)
+ List<String> alignmentValues = new ArrayList<String>();
+ Field[] fields = Alignment.class.getDeclaredFields();
+ for (Field field : fields) {
+ if (field.getType() == Alignment.class) {
+ alignmentValues.add(field.getName());
+ }
+ }
+ addComponent(createSimpleSelector("Alignment",
+ new ValueChangeListener() {
- .injectCSS(
- getUI(),
- ".sampleChild, .theLayout {border: 1px solid black;}"
- + ".theLayout > div:first-child {background: aqua;}"
- + ".theLayout > div:first-child + div {background: yellow;}"
- + ".theLayout > div:first-child + div + div {background: lightgrey;}");
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ String value = event.getProperty().getValue()
++ .toString();
++ AlignmentHandler parent = (AlignmentHandler) getParent();
++ if (parent == null) {
++ return;
++ }
++ try {
++ Field field = Alignment.class
++ .getDeclaredField(value);
++ Alignment alignment = (Alignment) field
++ .get(null);
++ parent.setComponentAlignment(SampleChild.this,
++ alignment);
++ } catch (Exception e) {
++ throw new RuntimeException(e);
++ }
++ }
++ }, alignmentValues, "TOP_LEFT")); // Sorry for not using
++ // more reflection magic
++ // just to find the
++ // default value...
+
+ }
+ }
+
+ private AbstractOrderedLayout currentLayout;
+ private HorizontalLayout sizeBar;
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ TestUtils
- @Override
- public void valueChange(ValueChangeEvent event) {
- currentLayout.setWidth(event.getProperty().getValue()
- .toString());
- }
- }, dimensionValues));
++ .injectCSS(
++ getUI(),
++ ".sampleChild, .theLayout {border: 1px solid black;}"
++ + ".theLayout > div:first-child {background: aqua;}"
++ + ".theLayout > div:first-child + div {background: yellow;}"
++ + ".theLayout > div:first-child + div + div {background: lightgrey;}");
+
+ currentLayout = new HorizontalLayout();
+ for (int i = 0; i < 3; i++) {
+ currentLayout.addComponent(new SampleChild());
+ }
+
+ sizeBar = new HorizontalLayout();
+ sizeBar.setSpacing(true);
+
+ sizeBar.addComponent(createSimpleSelector("Layout width",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- currentLayout.setHeight(event.getProperty().getValue()
- .toString());
- }
- }, dimensionValues));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ currentLayout.setWidth(event.getProperty().getValue()
++ .toString());
++ }
++ }, dimensionValues));
+ sizeBar.addComponent(createSimpleSelector("Layout height",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- currentLayout.setSpacing(Boolean.parseBoolean(event
- .getProperty().getValue().toString()));
- }
- }, "false", "true"));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ currentLayout.setHeight(event.getProperty().getValue()
++ .toString());
++ }
++ }, dimensionValues));
+ sizeBar.addComponent(createSimpleSelector("Spacing",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- currentLayout.setMargin(Boolean.parseBoolean(event
- .getProperty().getValue().toString()));
- }
- }, "false", "true"));
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ currentLayout.setSpacing(Boolean.parseBoolean(event
++ .getProperty().getValue().toString()));
++ }
++ }, "false", "true"));
+ sizeBar.addComponent(createSimpleSelector("Margin",
+ new ValueChangeListener() {
- @Override
- public void valueChange(ValueChangeEvent event) {
- Object value = event.getProperty().getValue();
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ currentLayout.setMargin(Boolean.parseBoolean(event
++ .getProperty().getValue().toString()));
++ }
++ }, "false", "true"));
+ sizeBar.addComponent(createSimpleSelector("Direction",
+ new ValueChangeListener() {
- AbstractOrderedLayout newLayout;
- if (value.equals("Horizontal")) {
- newLayout = new HorizontalLayout();
- } else {
- newLayout = new VerticalLayout();
- }
++ @Override
++ public void valueChange(ValueChangeEvent event) {
++ Object value = event.getProperty().getValue();
+
- while (currentLayout.getComponentCount() > 0) {
- newLayout.addComponent(currentLayout
- .getComponent(0));
- }
- newLayout.setStyleName("theLayout");
++ AbstractOrderedLayout newLayout;
++ if (value.equals("Horizontal")) {
++ newLayout = new HorizontalLayout();
++ } else {
++ newLayout = new VerticalLayout();
++ }
+
- newLayout.setHeight(currentLayout.getHeight(),
- currentLayout.getHeightUnits());
- newLayout.setWidth(currentLayout.getWidth(),
- currentLayout.getWidthUnits());
++ while (currentLayout.getComponentCount() > 0) {
++ newLayout.addComponent(currentLayout
++ .getComponent(0));
++ }
++ newLayout.setStyleName("theLayout");
+
- newLayout.setMargin(currentLayout.getMargin());
- newLayout.setSpacing(currentLayout.isSpacing());
++ newLayout.setHeight(currentLayout.getHeight(),
++ currentLayout.getHeightUnits());
++ newLayout.setWidth(currentLayout.getWidth(),
++ currentLayout.getWidthUnits());
+
- getLayout().replaceComponent(currentLayout, newLayout);
- getLayout().setExpandRatio(newLayout, 1);
- currentLayout = newLayout;
- }
- }, "Horizontal", "Vertical"));
++ newLayout.setMargin(currentLayout.getMargin());
++ newLayout.setSpacing(currentLayout.isSpacing());
+
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- setState(sizeBar, 2, 1);
- // width: 350px to middle child
- setChildState(1, 0, 2);
- // middle center allign to middle child
- setChildState(1, 4, 5);
- // long captions to right child
- setChildState(2, 2, 2);
- }
- }));
++ getLayout().replaceComponent(currentLayout, newLayout);
++ getLayout().setExpandRatio(newLayout, 1);
++ currentLayout = newLayout;
++ }
++ }, "Horizontal", "Vertical"));
+
+ HorizontalLayout caseBar = new HorizontalLayout();
+ caseBar.addComponent(new Button("Undefined without relative",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // width: 100% to middle child
- setChildState(1, 0, 4);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ setState(sizeBar, 2, 1);
++ // width: 350px to middle child
++ setChildState(1, 0, 2);
++ // middle center allign to middle child
++ setChildState(1, 4, 5);
++ // long captions to right child
++ setChildState(2, 2, 2);
++ }
++ }));
+ caseBar.addComponent(new Button("Undefined with relative",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // layout width: 350px
- setState(sizeBar, 0, 2);
- // layout margin enabled
- setState(sizeBar, 3, 1);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // width: 100% to middle child
++ setChildState(1, 0, 4);
++ }
++ }));
+ caseBar.addComponent(new Button("Fixed with overflow",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // Layout width: 800px
- setState(sizeBar, 0, 3);
- // layout margin enabled
- setState(sizeBar, 3, 1);
- // width: 350px to middle child
- setChildState(1, 0, 2);
- // short caption for middle child
- setChildState(1, 2, 1);
- // top center align for middle child
- setChildState(1, 4, 2);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // layout width: 350px
++ setState(sizeBar, 0, 2);
++ // layout margin enabled
++ setState(sizeBar, 3, 1);
++ }
++ }));
+ caseBar.addComponent(new Button("Fixed with extra space",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // Layout width: 800px
- setState(sizeBar, 0, 3);
- // Layout height: 350px
- setState(sizeBar, 1, 2);
- // Expand: 1 to middle child
- setChildState(1, 3, 1);
- // Align bottom left to middle child
- setChildState(1, 4, 6);
- // Long caption to middle child
- setChildState(1, 2, 2);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // Layout width: 800px
++ setState(sizeBar, 0, 3);
++ // layout margin enabled
++ setState(sizeBar, 3, 1);
++ // width: 350px to middle child
++ setChildState(1, 0, 2);
++ // short caption for middle child
++ setChildState(1, 2, 1);
++ // top center align for middle child
++ setChildState(1, 4, 2);
++ }
++ }));
+
+ caseBar.addComponent(new Button("Expand with alignment",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // Layout width: 800px
- setState(sizeBar, 0, 3);
- // Layout height: 350px
- setState(sizeBar, 1, 2);
- // Long caption to left child
- setChildState(0, 2, 2);
- // Width 350px to middle child
- setChildState(1, 0, 2);
- // Apply to left and middle child
- for (int i = 0; i < 2; i++) {
- // Expand: 1
- setChildState(i, 3, 1);
- // Align: middle center
- setChildState(i, 4, 5);
- }
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // Layout width: 800px
++ setState(sizeBar, 0, 3);
++ // Layout height: 350px
++ setState(sizeBar, 1, 2);
++ // Expand: 1 to middle child
++ setChildState(1, 3, 1);
++ // Align bottom left to middle child
++ setChildState(1, 4, 6);
++ // Long caption to middle child
++ setChildState(1, 2, 2);
++ }
++ }));
+
+ caseBar.addComponent(new Button("Multiple expands",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // Layout height: 100%
- setState(sizeBar, 1, 4);
- // Height: 350px to left child
- setChildState(0, 1, 2);
- // Height: 100% to middle child
- setChildState(1, 1, 4);
- // Short caption to middle child
- setChildState(1, 2, 1);
- // Alignment: bottom left to right child
- setChildState(2, 4, 7);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // Layout width: 800px
++ setState(sizeBar, 0, 3);
++ // Layout height: 350px
++ setState(sizeBar, 1, 2);
++ // Long caption to left child
++ setChildState(0, 2, 2);
++ // Width 350px to middle child
++ setChildState(1, 0, 2);
++ // Apply to left and middle child
++ for (int i = 0; i < 2; i++) {
++ // Expand: 1
++ setChildState(i, 3, 1);
++ // Align: middle center
++ setChildState(i, 4, 5);
++ }
++ }
++ }));
+
+ caseBar.addComponent(new Button("Fixed + relative height",
+ new ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- resetState();
- // Height: 350px to left child
- setChildState(0, 1, 2);
- // Short caption to left child
- setChildState(0, 2, 1);
- // Height: 100% to middle child
- setChildState(1, 1, 4);
- // Alignment: bottom left to right child
- setChildState(2, 4, 7);
- }
- }));
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // Layout height: 100%
++ setState(sizeBar, 1, 4);
++ // Height: 350px to left child
++ setChildState(0, 1, 2);
++ // Height: 100% to middle child
++ setChildState(1, 1, 4);
++ // Short caption to middle child
++ setChildState(1, 2, 1);
++ // Alignment: bottom left to right child
++ setChildState(2, 4, 7);
++ }
++ }));
+
+ caseBar.addComponent(new Button("Undefined + relative height",
+ new ClickListener() {
++ @Override
++ public void buttonClick(ClickEvent event) {
++ resetState();
++ // Height: 350px to left child
++ setChildState(0, 1, 2);
++ // Short caption to left child
++ setChildState(0, 2, 1);
++ // Height: 100% to middle child
++ setChildState(1, 1, 4);
++ // Alignment: bottom left to right child
++ setChildState(2, 4, 7);
++ }
++ }));
+
+ caseBar.setSpacing(true);
+
+ addComponent(caseBar);
+ addComponent(sizeBar);
+ addComponent(currentLayout);
+
+ getLayout().setSpacing(true);
+ getContent().setSizeFull();
+ getLayout().setSizeFull();
+ getLayout().setExpandRatio(currentLayout, 1);
+ }
+
+ private void resetState() {
+ for (int i = 0; i < sizeBar.getComponentCount(); i++) {
+ setState(sizeBar, i, 0);
+ }
+ for (int i = 0; i < 3; i++) {
+ // Child width and height -> -1px
+ SampleChild child = (SampleChild) currentLayout.getComponent(i);
+ for (int j = 0; j < child.getComponentCount(); j++) {
+ if (j == 4) {
+ setState(child, j, 1);
+ } else {
+ setState(child, j, 0);
+ }
+ }
+ }
+ }
+
+ private void setChildState(int childIndex, int selectIndex, int valueIndex) {
+ Component child = currentLayout.getComponent(childIndex);
+ setState(child, selectIndex, valueIndex);
+ }
+
+ private static void setState(Component container, int selectIndex, int value) {
+ NativeSelect select = (NativeSelect) ((AbstractOrderedLayout) container)
+ .getComponent(selectIndex);
+ select.setValue(new ArrayList<Object>(select.getItemIds()).get(value));
+ }
+
+ private static NativeSelect createSimpleSelector(String caption,
+ ValueChangeListener listener, String... values) {
+ return createSimpleSelector(caption, listener, Arrays.asList(values),
+ values[0]);
+ }
+
+ private static NativeSelect createSimpleSelector(String caption,
+ ValueChangeListener listener, List<String> values,
+ String defaultValue) {
+ NativeSelect selector = new NativeSelect(caption, values);
+ selector.setNullSelectionAllowed(false);
+ selector.setImmediate(true);
+ selector.addListener(listener);
+ selector.setValue(defaultValue);
+ return selector;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return null;
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Tester application for exploring how Horizontal/VerticalLayout reacts to various settings ";
+ }
+
+ }
--- /dev/null
- private Button addButton = new Button("Add new row",
- (Button.ClickListener) this);
+ package com.vaadin.tests.components.table;
+
+ import com.vaadin.data.Item;
+ import com.vaadin.data.Property;
+ import com.vaadin.data.util.IndexedContainer;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Button;
+ import com.vaadin.ui.Button.ClickEvent;
+ import com.vaadin.ui.Component;
+ import com.vaadin.ui.Table;
+ import com.vaadin.ui.TextField;
+
+ public class TextFieldRelativeWidth extends TestBase {
+
+ @Override
+ public void setup() {
+ TextField tf = new TextField("test", "testing");
+ tf.setWidth("100%");
+
+ EditTable t = new EditTable();
+ t.setButtonCaption("Click to add new Key Research Question");
+ t.setInputPrompt("Key Reseach question");
+ t.setInputPromptChild("Question details");
+ t.addNewRow();
+ addComponent(t);
+ }
+
+ public class EditTable extends Table implements Button.ClickListener {
+
++ private Button addButton = new Button("Add new row", this);
+
+ private String inputPrompt;
+
+ private String inputPromptChild;
+
+ private int nextItemIndex = 1;
+
+ private static final long serialVersionUID = 3326806911297977454L;
+
+ public EditTable() {
+ setColumnHeaderMode(Table.COLUMN_HEADER_MODE_HIDDEN);
+ inputPrompt = "";
+ setPageLength(100);
+ setHeight("100%");
+ setSizeFull();
+ addContainerProperty("id", Integer.class, null);
+ addContainerProperty("text", Component.class, null);
+ addContainerProperty("button", Button.class, null);
+ setColumnExpandRatio("text", 1);
+ Item i = getItem(addItem());
+ i.getItemProperty("text").setValue(addButton);
+ setImmediate(true);
+ setSelectable(true);
+ addListener(new Property.ValueChangeListener() {
+ private static final long serialVersionUID = 448896474865195605L;
+
+ @Override
+ public void valueChange(
+ com.vaadin.data.Property.ValueChangeEvent event) {
+ // IndexedContainer idc = (IndexedContainer)
+ // getContainerDataSource();
+
+ }
+
+ });
+ }
+
+ public void addNewRow() {
+ IndexedContainer idc = (IndexedContainer) getContainerDataSource();
+ int size = idc.size();
+ Object itemId = idc.addItemAt(size - 1);
+ Item newItem = idc.getItem(itemId);
+ TextField tf = new TextField();
+ if (inputPrompt != null && inputPrompt.length() > 0) {
+ tf.setInputPrompt(inputPrompt);
+ }
+ tf.setWidth("100%");
+
+ newItem.getItemProperty("id").setValue(nextItemIndex);
+ nextItemIndex++;
+ newItem.getItemProperty("text").setValue(tf);
+ setValue(itemId);
+ itemId = idc.addItemAt(size);
+ newItem = idc.getItem(itemId);
+
+ tf = new TextField();
+ if (inputPromptChild != null && inputPromptChild.length() > 0) {
+ tf.setInputPrompt(inputPromptChild);
+ }
+ // tf.setRows(1);
+ // tf.setHeight("45px");
+ tf.setWidth("100%");
+ tf.addStyleName("childtf");
+ newItem.getItemProperty("text").setValue(tf);
+
+ }
+
+ public void setButtonCaption(String caption) {
+ addButton.setCaption(caption);
+ }
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Button b = event.getButton();
+ if (b == addButton) {
+ select(getNullSelectionItemId());
+ addNewRow();
+ }
+ }
+
+ public void setInputPrompt(String string) {
+ inputPrompt = string;
+ }
+
+ public void setInputPromptChild(String string) {
+ inputPromptChild = string;
+ }
+
+ }
+
+ @Override
+ protected String getDescription() {
+ return "The table has 3 columns. The second column is expanded and contains 100% wide textfields. These should fill the available space. The third column is empty.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 3145;
+ }
+ }
--- /dev/null
- <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[7]/domChild[0]/domChild[1]</td>
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head profile="http://selenium-ide.openqa.org/profiles/test-case">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <link rel="selenium.base" href="" />
+ <title>New Test</title>
+ </head>
+ <body>
+ <table cellpadding="1" cellspacing="1" border="1">
+ <thead>
+ <tr><td rowspan="1" colspan="3">New Test</td></tr>
+ </thead><tbody>
+ <tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.fieldgroup.BasicPersonForm?restartApplication</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td>initial</td>
+ </tr>
+ <tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTextArea[0]</td>
+ <td>90,38</td>
+ </tr>
+ <tr>
+ <td>enterCharacter</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTextArea[0]</td>
+ <td>Dover</td>
+ </tr>
+ <tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+ <td>1. Person [firstName=John, lastName=Doe, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=false, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ <tr>
+ <td>drag</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>108,9</td>
+ </tr>
+ <tr>
+ <td>drop</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]</td>
+ <td>0,587</td>
+ </tr>
+ <tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>-18,9</td>
+ </tr>
+ <tr>
+ <td>enterCharacter</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]</td>
+ <td>239,14</td>
+ </tr>
+ <!--error indicator-->
+ <tr>
+ <td>assertCSSClass</td>
++ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VBoxLayout[0]/VBoxLayout$Slot[1]/VBoxLayout[0]/domChild[7]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>v-errorindicator</td>
+ </tr>
+ <tr>
+ <td>assertValue</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>false</td>
+ </tr>
+ <!--show bean values-->
+ <tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+ <td>2. Person [firstName=John, lastName=Doe, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=false, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ <!--error message in tooltip-->
+ <tr>
+ <td>showTooltip</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>0,0</td>
+ </tr>
+ <tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::Root/VTooltip[0]/FlowPanel[0]/VErrorMessage[0]/HTML[0]</td>
+ <td>Could not convert value to Boolean</td>
+ </tr>
+ <tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>66,6</td>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+ <td>YAY!</td>
+ </tr>
+ <!--no error indicator-->
+ <tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[7]/domChild[0]/domChild[1]</td>
+ <td>v-errorindicator</td>
+ </tr>
+ <tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[8]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>closeNotification</td>
+ <td>//body/div[2]</td>
+ <td>0,0</td>
+ </tr>
+ <!--commit last name and new deceased status-->
+ <tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+ <td>4. Person [firstName=John, lastName=Dover, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=true, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ </tbody></table>
+ </body>
+ </html>