*/
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;
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 {
}
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
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()) {
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()) {
*/
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;
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;
private Object previousStyleName;
- Map<String, AbsoluteWrapper> pidToComponentWrappper = new HashMap<String, AbsoluteWrapper>();
-
protected ApplicationConnection client;
public VAbsoluteLayout() {
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;
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;
} catch (JSONException e) {
throw new PaintException(
"Failed to serialize shared state for connector "
- + connector.getConnectorId() + ": "
+ + connector.getClass().getName() + " ("
+ + connector.getConnectorId() + "): "
+ e.getMessage());
}
}
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) {
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;
}
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;
/**
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.
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();
}
/**
* @return the number of contained components
*/
public int getComponentCount() {
- return components.size();
+ return componentToCoordinates.size();
}
/**
public void replaceComponent(Component oldComponent, Component newComponent) {
ComponentPosition position = getPosition(oldComponent);
removeComponent(oldComponent);
- addComponent(newComponent);
- componentToCoordinates.put(newComponent, position);
+ addComponent(newComponent, position);
}
/*
*/
@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());
}
/**
* 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
* 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();
}
/**
}
- /*
- * (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,