]> source.dussan.org Git - vaadin-framework.git/commitdiff
Updated AbsoluteLayout to use hierarchy and state change events
authorArtur Signell <artur@vaadin.com>
Tue, 3 Apr 2012 07:46:59 +0000 (10:46 +0300)
committerArtur Signell <artur@vaadin.com>
Wed, 4 Apr 2012 21:08:42 +0000 (00:08 +0300)
src/com/vaadin/terminal/gwt/client/ui/AbsoluteLayoutConnector.java
src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
src/com/vaadin/terminal/gwt/server/JsonCodec.java
src/com/vaadin/ui/AbsoluteLayout.java

index 72386400fe729fe10130b52f47bfe5ee632554d5..51596adead95e115b6dec6a26c626fe7fb6cd1c4 100644 (file)
@@ -3,20 +3,23 @@
  */
 package com.vaadin.terminal.gwt.client.ui;
 
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
 import com.vaadin.terminal.gwt.client.DirectionalManagedLayout;
-import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VCaption;
 import com.vaadin.terminal.gwt.client.communication.RpcProxy;
 import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
 import com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout.AbsoluteWrapper;
 import com.vaadin.ui.AbsoluteLayout;
 
@@ -24,6 +27,25 @@ import com.vaadin.ui.AbsoluteLayout;
 public class AbsoluteLayoutConnector extends
         AbstractComponentContainerConnector implements DirectionalManagedLayout {
 
+    public static class AbsoluteLayoutState extends ComponentState {
+        // Maps each component to a position
+        private Map<String, String> connectorToCssPosition = new HashMap<String, String>();
+
+        public String getConnectorPosition(Connector connector) {
+            return connectorToCssPosition.get(connector.getConnectorId());
+        }
+
+        public Map<String, String> getConnectorToCssPosition() {
+            return connectorToCssPosition;
+        }
+
+        public void setConnectorToCssPosition(
+                Map<String, String> componentToCssPosition) {
+            connectorToCssPosition = componentToCssPosition;
+        }
+
+    }
+
     public interface AbsoluteLayoutServerRPC extends LayoutClickRPC, ServerRpc {
 
     }
@@ -45,49 +67,35 @@ public class AbsoluteLayoutConnector extends
 
     private AbsoluteLayoutServerRPC rpc;
 
+    private Map<String, AbsoluteWrapper> connectorIdToComponentWrapper = new HashMap<String, AbsoluteWrapper>();
+
     @Override
     protected void init() {
         super.init();
         rpc = RpcProxy.create(AbsoluteLayoutServerRPC.class, this);
     }
 
-    @Override
-    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-        getWidget().client = client;
-        // TODO margin handling
-        super.updateFromUIDL(uidl, client);
-        if (!isRealUpdate(uidl)) {
-            return;
-        }
+    public void updateCaption(ComponentConnector component) {
+        VAbsoluteLayout absoluteLayoutWidget = getWidget();
+        AbsoluteWrapper componentWrapper = getWrapper(component);
 
-        clickEventHandler.handleEventHandlerRegistration();
+        boolean captionIsNeeded = VCaption.isNeeded(component.getState());
+
+        VCaption caption = componentWrapper.getCaption();
 
-        HashSet<String> unrenderedPids = new HashSet<String>(
-                getWidget().pidToComponentWrappper.keySet());
-
-        for (Iterator<Object> childIterator = uidl.getChildIterator(); childIterator
-                .hasNext();) {
-            UIDL cc = (UIDL) childIterator.next();
-            if (cc.getTag().equals("cc")) {
-                UIDL componentUIDL = cc.getChildUIDL(0);
-                unrenderedPids.remove(componentUIDL.getId());
-                getWidget().getWrapper(client, componentUIDL)
-                        .updateFromUIDL(cc);
+        if (captionIsNeeded) {
+            if (caption == null) {
+                caption = new VCaption(component, getConnection());
+                absoluteLayoutWidget.add(caption);
+            }
+            caption.updateCaption();
+            componentWrapper.updateCaptionPosition();
+        } else {
+            if (caption != null) {
+                caption.removeFromParent();
             }
         }
 
-        for (String pid : unrenderedPids) {
-            AbsoluteWrapper absoluteWrapper = getWidget().pidToComponentWrappper
-                    .get(pid);
-            getWidget().pidToComponentWrappper.remove(pid);
-            absoluteWrapper.destroy();
-        }
-    }
-
-    public void updateCaption(ComponentConnector component) {
-        AbsoluteWrapper parent2 = (AbsoluteWrapper) (component.getWidget())
-                .getParent();
-        parent2.updateCaption();
     }
 
     @Override
@@ -100,6 +108,56 @@ public class AbsoluteLayoutConnector extends
         return (VAbsoluteLayout) super.getWidget();
     }
 
+    @Override
+    public AbsoluteLayoutState getState() {
+        return (AbsoluteLayoutState) super.getState();
+    }
+
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+        clickEventHandler.handleEventHandlerRegistration();
+
+        // TODO Margin handling
+
+        for (ComponentConnector child : getChildren()) {
+            getWrapper(child).setPosition(
+                    getState().getConnectorPosition(child));
+        }
+    };
+
+    private AbsoluteWrapper getWrapper(ComponentConnector child) {
+        String childId = child.getConnectorId();
+        AbsoluteWrapper wrapper = connectorIdToComponentWrapper.get(childId);
+        if (wrapper != null) {
+            return wrapper;
+        }
+
+        wrapper = new AbsoluteWrapper(child.getWidget());
+        connectorIdToComponentWrapper.put(childId, wrapper);
+        getWidget().add(wrapper);
+        return wrapper;
+
+    }
+
+    @Override
+    public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+        super.onConnectorHierarchyChange(event);
+
+        for (ComponentConnector child : getChildren()) {
+            getWrapper(child);
+        }
+
+        for (ComponentConnector oldChild : event.getOldChildren()) {
+            if (oldChild.getParent() != this) {
+                String connectorId = oldChild.getConnectorId();
+                AbsoluteWrapper absoluteWrapper = connectorIdToComponentWrapper
+                        .remove(connectorId);
+                absoluteWrapper.destroy();
+            }
+        }
+    }
+
     public void layoutVertically() {
         VAbsoluteLayout layout = getWidget();
         for (ComponentConnector paintable : getChildren()) {
@@ -134,8 +192,7 @@ public class AbsoluteLayoutConnector extends
     public void layoutHorizontally() {
         VAbsoluteLayout layout = getWidget();
         for (ComponentConnector paintable : getChildren()) {
-            Widget widget = paintable.getWidget();
-            AbsoluteWrapper wrapper = (AbsoluteWrapper) widget.getParent();
+            AbsoluteWrapper wrapper = getWrapper(paintable);
             Style wrapperStyle = wrapper.getElement().getStyle();
 
             if (paintable.isRelativeWidth()) {
index d4386e2b0422507b06a3ff3901c7dc6f3679a550..c5c3834d4b9632688fb67cb9f907e16d7f1f72d5 100644 (file)
@@ -3,9 +3,6 @@
  */
 package com.vaadin.terminal.gwt.client.ui;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import com.google.gwt.dom.client.DivElement;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Style;
@@ -16,8 +13,6 @@ import com.google.gwt.user.client.ui.SimplePanel;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ComponentConnector;
-import com.vaadin.terminal.gwt.client.ConnectorMap;
-import com.vaadin.terminal.gwt.client.UIDL;
 import com.vaadin.terminal.gwt.client.Util;
 import com.vaadin.terminal.gwt.client.VCaption;
 
@@ -35,8 +30,6 @@ public class VAbsoluteLayout extends ComplexPanel {
 
     private Object previousStyleName;
 
-    Map<String, AbsoluteWrapper> pidToComponentWrappper = new HashMap<String, AbsoluteWrapper>();
-
     protected ApplicationConnection client;
 
     public VAbsoluteLayout() {
@@ -52,24 +45,12 @@ public class VAbsoluteLayout extends ComplexPanel {
         canvas.setClassName(CLASSNAME + "-margin");
     }
 
-    AbsoluteWrapper getWrapper(ApplicationConnection client, UIDL componentUIDL) {
-        AbsoluteWrapper wrapper = pidToComponentWrappper.get(componentUIDL
-                .getId());
-        if (wrapper == null) {
-            wrapper = new AbsoluteWrapper(client.getPaintable(componentUIDL));
-            pidToComponentWrappper.put(componentUIDL.getId(), wrapper);
-            add(wrapper);
-        }
-        return wrapper;
-
-    }
-
     @Override
     public void add(Widget child) {
         super.add(child, canvas);
     }
 
-    public class AbsoluteWrapper extends SimplePanel {
+    public static class AbsoluteWrapper extends SimplePanel {
         private String css;
         String left;
         String top;
@@ -77,62 +58,28 @@ public class VAbsoluteLayout extends ComplexPanel {
         String bottom;
         private String zIndex;
 
-        private ComponentConnector paintable;
         private VCaption caption;
 
-        public AbsoluteWrapper(ComponentConnector paintable) {
-            this.paintable = paintable;
+        public AbsoluteWrapper(Widget child) {
+            setWidget(child);
             setStyleName(CLASSNAME + "-wrapper");
         }
 
-        public void updateCaption() {
-
-            boolean captionIsNeeded = VCaption.isNeeded(paintable.getState());
-            if (captionIsNeeded) {
-                if (caption == null) {
-                    caption = new VCaption(paintable, client);
-                    VAbsoluteLayout.this.add(caption);
-                }
-                caption.updateCaption();
-                updateCaptionPosition();
-            } else {
-                if (caption != null) {
-                    caption.removeFromParent();
-                    caption = null;
-                }
-            }
+        public VCaption getCaption() {
+            return caption;
         }
 
-        @Override
-        public void setWidget(Widget w) {
-            // this fixes #5457 (Widget implementation can change on-the-fly)
-            paintable = ConnectorMap.get(client).getConnector(w);
-            super.setWidget(w);
+        public void setCaption(VCaption caption) {
+            this.caption = caption;
         }
 
         public void destroy() {
             if (caption != null) {
                 caption.removeFromParent();
             }
-            client.unregisterPaintable(paintable);
             removeFromParent();
         }
 
-        public void updateFromUIDL(UIDL componentUIDL) {
-            setPosition(componentUIDL.getStringAttribute("css"));
-            if (getWidget() != paintable.getWidget()) {
-                setWidget(paintable.getWidget());
-            }
-            UIDL childUIDL = componentUIDL.getChildUIDL(0);
-            paintable.updateFromUIDL(childUIDL, client);
-            if (childUIDL.hasAttribute("cached")) {
-                // child may need relative size adjustment if wrapper details
-                // have changed this could be optimized (check if wrapper size
-                // has changed)
-                client.handleComponentRelativeSize(paintable.getWidget());
-            }
-        }
-
         public void setPosition(String stringAttribute) {
             if (css == null || !css.equals(stringAttribute)) {
                 css = stringAttribute;
index a7bd69d620f2f687233e301166b2c4c2df26dbbd..17e504a3bb743bcb5ba4fc7b3662acdac5acaa17 100644 (file)
@@ -821,7 +821,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
                 } catch (JSONException e) {
                     throw new PaintException(
                             "Failed to serialize shared state for connector "
-                                    + connector.getConnectorId() + ": "
+                                    + connector.getClass().getName() + " ("
+                                    + connector.getConnectorId() + "): "
                                     + e.getMessage());
                 }
             }
index 1113d71f73aefa382c9e46f929fd3ed28b2c51c0..21a189a435a5d9c8ec919cef02b41f4360057c90 100644 (file)
@@ -225,7 +225,7 @@ public class JsonCodec implements Serializable {
             JSONArray jsonArray = encodeArrayContents(array, application);
             return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray);
         } else if (value instanceof Map) {
-            Map<String, Object> map = (Map<String, Object>) value;
+            Map<Object, Object> map = (Map<Object, Object>) value;
             JSONObject jsonMap = encodeMapContents(map, application);
             return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap);
         } else if (value instanceof Connector) {
@@ -324,13 +324,17 @@ public class JsonCodec implements Serializable {
         return jsonArray;
     }
 
-    private static JSONObject encodeMapContents(Map<String, Object> map,
+    private static JSONObject encodeMapContents(Map<Object, Object> map,
             Application application) throws JSONException {
         JSONObject jsonMap = new JSONObject();
-        for (String mapKey : map.keySet()) {
-            // TODO handle object graph loops?
+        for (Object mapKey : map.keySet()) {
+            if (!(mapKey instanceof String)) {
+                throw new JSONException(
+                        "Only maps with String keys are currently supported (#8602)");
+            }
+
             Object mapValue = map.get(mapKey);
-            jsonMap.put(mapKey, encode(mapValue, application));
+            jsonMap.put((String) mapKey, encode(mapValue, application));
         }
         return jsonMap;
     }
index 42ad0c5e4c2a16809b683ed8a5e85b44ae4fd17f..d347b74829593c226ea296d3f729e1641e1c6b53 100644 (file)
@@ -4,22 +4,20 @@
 package com.vaadin.ui;
 
 import java.io.Serializable;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
+import java.util.LinkedHashMap;
 import java.util.Map;
 
 import com.vaadin.event.LayoutEvents.LayoutClickEvent;
 import com.vaadin.event.LayoutEvents.LayoutClickListener;
 import com.vaadin.event.LayoutEvents.LayoutClickNotifier;
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
 import com.vaadin.terminal.Sizeable;
 import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.terminal.gwt.client.MouseEventDetails;
 import com.vaadin.terminal.gwt.client.ui.AbsoluteLayoutConnector;
 import com.vaadin.terminal.gwt.client.ui.AbsoluteLayoutConnector.AbsoluteLayoutServerRPC;
+import com.vaadin.terminal.gwt.client.ui.AbsoluteLayoutConnector.AbsoluteLayoutState;
 import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler;
 
 /**
@@ -39,11 +37,8 @@ public class AbsoluteLayout extends AbstractLayout implements
                     mouseDetails, clickedConnector));
         }
     };
-    // The components in the layout
-    private Collection<Component> components = new LinkedHashSet<Component>();
-
     // Maps each component to a position
-    private Map<Component, ComponentPosition> componentToCoordinates = new HashMap<Component, ComponentPosition>();
+    private LinkedHashMap<Component, ComponentPosition> componentToCoordinates = new LinkedHashMap<Component, ComponentPosition>();
 
     /**
      * Creates an AbsoluteLayout with full size.
@@ -53,12 +48,17 @@ public class AbsoluteLayout extends AbstractLayout implements
         setSizeFull();
     }
 
+    @Override
+    public AbsoluteLayoutState getState() {
+        return (AbsoluteLayoutState) super.getState();
+    }
+
     /**
      * Gets an iterator for going through all components enclosed in the
      * absolute layout.
      */
     public Iterator<Component> getComponentIterator() {
-        return components.iterator();
+        return componentToCoordinates.keySet().iterator();
     }
 
     /**
@@ -68,7 +68,7 @@ public class AbsoluteLayout extends AbstractLayout implements
      * @return the number of contained components
      */
     public int getComponentCount() {
-        return components.size();
+        return componentToCoordinates.size();
     }
 
     /**
@@ -78,8 +78,7 @@ public class AbsoluteLayout extends AbstractLayout implements
     public void replaceComponent(Component oldComponent, Component newComponent) {
         ComponentPosition position = getPosition(oldComponent);
         removeComponent(oldComponent);
-        addComponent(newComponent);
-        componentToCoordinates.put(newComponent, position);
+        addComponent(newComponent, position);
     }
 
     /*
@@ -91,29 +90,7 @@ public class AbsoluteLayout extends AbstractLayout implements
      */
     @Override
     public void addComponent(Component c) {
-        components.add(c);
-        try {
-            super.addComponent(c);
-            requestRepaint();
-        } catch (IllegalArgumentException e) {
-            components.remove(c);
-            throw e;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui
-     * .Component)
-     */
-    @Override
-    public void removeComponent(Component c) {
-        components.remove(c);
-        componentToCoordinates.remove(c);
-        super.removeComponent(c);
-        requestRepaint();
+        addComponent(c, new ComponentPosition());
     }
 
     /**
@@ -131,28 +108,90 @@ public class AbsoluteLayout extends AbstractLayout implements
      *            The css position string
      */
     public void addComponent(Component c, String cssPosition) {
+        ComponentPosition position = new ComponentPosition();
+        position.setCSSString(cssPosition);
+        addComponent(c, position);
+    }
+
+    /**
+     * Adds the component using the given position. Ensures the position is only
+     * set if the component is added correctly.
+     * 
+     * @param c
+     *            The component to add
+     * @param position
+     *            The position info for the component. Must not be null.
+     * @throws IllegalArgumentException
+     *             If adding the component failed
+     */
+    private void addComponent(Component c, ComponentPosition position)
+            throws IllegalArgumentException {
         /*
          * Create position instance and add it to componentToCoordinates map. We
          * need to do this before we call addComponent so the attachListeners
          * can access this position. #6368
          */
-        ComponentPosition position = new ComponentPosition();
-        position.setCSSString(cssPosition);
-        componentToCoordinates.put(c, position);
-
+        internalSetPosition(c, position);
         try {
-            addComponent(c);
-
+            super.addComponent(c);
         } catch (IllegalArgumentException e) {
-            // Remove component coordinates if adding fails
-            componentToCoordinates.remove(c);
+            internalRemoveComponent(c);
             throw e;
         }
+        requestRepaint();
+    }
+
+    /**
+     * Removes the component from all internal data structures. Does not
+     * actually remove the component from the layout (this is assumed to have
+     * been done by the caller).
+     * 
+     * @param c
+     *            The component to remove
+     */
+    private void internalRemoveComponent(Component c) {
+        componentToCoordinates.remove(c);
+    }
+
+    @Override
+    public void updateState() {
+        super.updateState();
+
+        // This could be in internalRemoveComponent and internalSetComponent if
+        // Map<Connector,String> was supported. We cannot get the child
+        // connectorId unless the component is attached to the application so
+        // the String->String map cannot be populated in internal* either.
+        Map<String, String> connectorToPosition = new HashMap<String, String>();
+        for (Component c : this) {
+            connectorToPosition.put(c.getConnectorId(), getPosition(c)
+                    .getCSSString());
+        }
+        getState().setConnectorToCssPosition(connectorToPosition);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui
+     * .Component)
+     */
+    @Override
+    public void removeComponent(Component c) {
+        super.removeComponent(c);
+        internalRemoveComponent(c);
+        requestRepaint();
     }
 
     /**
      * Gets the position of a component in the layout. Returns null if component
      * is not attached to the layout.
+     * <p>
+     * Note that you cannot update the position by updating this object. Call
+     * {@link #setPosition(Component, ComponentPosition)} with the updated
+     * {@link ComponentPosition} object.
+     * </p>
      * 
      * @param component
      *            The component which position is needed
@@ -161,15 +200,36 @@ public class AbsoluteLayout extends AbstractLayout implements
      *         layout.
      */
     public ComponentPosition getPosition(Component component) {
-        if (component.getParent() != this) {
-            return null;
-        } else if (componentToCoordinates.containsKey(component)) {
-            return componentToCoordinates.get(component);
-        } else {
-            ComponentPosition coords = new ComponentPosition();
-            componentToCoordinates.put(component, coords);
-            return coords;
+        return componentToCoordinates.get(component);
+    }
+
+    /**
+     * Sets the position of a component in the layout.
+     * 
+     * @param component
+     * @param position
+     */
+    public void setPosition(Component component, ComponentPosition position) {
+        if (!componentToCoordinates.containsKey(component)) {
+            throw new IllegalArgumentException(
+                    "Component must be a child of this layout");
         }
+        internalSetPosition(component, position);
+    }
+
+    /**
+     * Updates the position for a component. Caller must ensure component is a
+     * child of this layout.
+     * 
+     * @param component
+     *            The component. Must be a child for this layout. Not enforced.
+     * @param position
+     *            New position. Must not be null.
+     */
+    private void internalSetPosition(Component component,
+            ComponentPosition position) {
+        componentToCoordinates.put(component, position);
+        requestRepaint();
     }
 
     /**
@@ -552,24 +612,6 @@ public class AbsoluteLayout extends AbstractLayout implements
 
     }
 
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.ui.AbstractLayout#paintContent(com.vaadin.terminal.PaintTarget
-     * )
-     */
-    @Override
-    public void paintContent(PaintTarget target) throws PaintException {
-        super.paintContent(target);
-        for (Component component : components) {
-            target.startTag("cc");
-            target.addAttribute("css", getPosition(component).getCSSString());
-            component.paint(target);
-            target.endTag("cc");
-        }
-    }
-
     public void addListener(LayoutClickListener listener) {
         addListener(LayoutClickEventHandler.LAYOUT_CLICK_EVENT_IDENTIFIER,
                 LayoutClickEvent.class, listener,