]> source.dussan.org Git - vaadin-framework.git/commitdiff
new component, absolutelayout (aka coordinatelayout #1267) and simple test case
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 9 Apr 2009 08:41:24 +0000 (08:41 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 9 Apr 2009 08:41:24 +0000 (08:41 +0000)
svn changeset:7374/svn branch:6.0

WebContent/ITMILL/themes/default/absolutelayout/absolutelayout.css [new file with mode: 0644]
WebContent/ITMILL/themes/default/styles.css
WebContent/ITMILL/themes/tests-tickets/styles.css
src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java
src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java [new file with mode: 0644]
src/com/itmill/toolkit/tests/layouts/TestAbsoluteLayout.java [new file with mode: 0644]
src/com/itmill/toolkit/ui/AbsoluteLayout.java [new file with mode: 0644]

diff --git a/WebContent/ITMILL/themes/default/absolutelayout/absolutelayout.css b/WebContent/ITMILL/themes/default/absolutelayout/absolutelayout.css
new file mode 100644 (file)
index 0000000..e77e380
--- /dev/null
@@ -0,0 +1,4 @@
+.i-absolutelayout-wrapper {
+       position: absolute;
+       overflow:hidden;
+}
index 439f4f2aeca819088cbe1082cc7b11fedf37655c..cc8c3fda7ea6d4caf4710501a0778f9ab666d0c8 100644 (file)
@@ -1,5 +1,11 @@
 /* Automatically compiled css file from subdirectories. */
 
+/* ./WebContent/ITMILL/themes/default/absolutelayout/absolutelayout.css */
+.i-absolutelayout-wrapper {
+       position: absolute;
+       overflow:hidden;
+}
+
 /* ./WebContent/ITMILL/themes/default/accordion/accordion.css */
 .i-accordion {
        position: relative;
index 225bbd6d1d384a220965b433399ef9cdbe901595..2c2b359ed901c21d8b983acce3c4891abf98b6d1 100644 (file)
@@ -1276,4 +1276,16 @@ padding:2px;
 .i-button-nowraplink span {
    white-space: normal;
 }
+
+.cyan {
+       background:cyan;        
+}
    
+.yellow {
+       background:yellow;      
+}
+   
+.green {
+       background:green;       
+}
+   
\ No newline at end of file
index e81f5fff47c4efa89132f3ee421b771c26bd84b1..0bc849affa297a5a55921392c4ab4a41ea58bab0 100644 (file)
@@ -5,6 +5,7 @@
 package com.itmill.toolkit.terminal.gwt.client;
 
 import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ui.IAbsoluteLayout;
 import com.itmill.toolkit.terminal.gwt.client.ui.IAccordion;
 import com.itmill.toolkit.terminal.gwt.client.ui.IButton;
 import com.itmill.toolkit.terminal.gwt.client.ui.ICheckBox;
@@ -139,6 +140,8 @@ public class DefaultWidgetSet implements WidgetSet {
             return new IPopupView();
         } else if (IUriFragmentUtility.class == classType) {
             return new IUriFragmentUtility();
+        } else if (IAbsoluteLayout.class == classType) {
+            return new IAbsoluteLayout();
         }
 
         return new IUnknownComponent();
@@ -249,6 +252,8 @@ public class DefaultWidgetSet implements WidgetSet {
             return IPopupView.class;
         } else if ("urifragment".equals(tag)) {
             return IUriFragmentUtility.class;
+        } else if (IAbsoluteLayout.TAGNAME.equals(tag)) {
+            return IAbsoluteLayout.class;
         }
 
         return IUnknownComponent.class;
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IAbsoluteLayout.java
new file mode 100644 (file)
index 0000000..fc1f14c
--- /dev/null
@@ -0,0 +1,320 @@
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+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.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.ComplexPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
+import com.itmill.toolkit.terminal.gwt.client.Container;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+
+public class IAbsoluteLayout extends ComplexPanel implements Container {
+
+    /** Tag name for widget creation */
+    public static final String TAGNAME = "absolutelayout";
+
+    /** Class name, prefix in styling */
+    public static final String CLASSNAME = "i-absolutelayout";
+
+    private DivElement marginElement;
+
+    private Element canvas;
+
+    private int excessPixelsHorizontal;
+
+    private int excessPixelsVertical;
+
+    private Object previousStyleName;
+
+    private Map<String, AbsoluteWrapper> pidToComponentWrappper = new HashMap<String, AbsoluteWrapper>();
+
+    private ApplicationConnection client;
+
+    private boolean rendering;
+
+    public IAbsoluteLayout() {
+        setElement(Document.get().createDivElement());
+        setStyleName(CLASSNAME);
+        marginElement = Document.get().createDivElement();
+        canvas = DOM.createDiv();
+        canvas.getStyle().setProperty("position", "relative");
+        marginElement.appendChild(canvas);
+        getElement().appendChild(marginElement);
+    }
+
+    public RenderSpace getAllocatedSpace(Widget child) {
+        // TODO needs some special handling for components with only on edge
+        // horizontally or vertically defined
+        AbsoluteWrapper wrapper = (AbsoluteWrapper) child.getParent();
+        int w;
+        if (wrapper.left != null && wrapper.right != null) {
+            w = wrapper.getOffsetWidth();
+        } else if (wrapper.right != null) {
+            // left == null
+            // available width == right edge == offsetleft + width
+            w = wrapper.getOffsetWidth() + wrapper.getElement().getOffsetLeft();
+        } else {
+            // left != null && right == null || left == null &&
+            // right == null
+            // available width == canvas width - offset left
+            w = canvas.getOffsetWidth() - wrapper.getElement().getOffsetLeft();
+        }
+        int h;
+        if (wrapper.top != null && wrapper.bottom != null) {
+            h = wrapper.getOffsetHeight();
+        } else if (wrapper.bottom != null) {
+            // top not defined, available space 0... bottom of wrapper
+            h = wrapper.getElement().getOffsetTop() + wrapper.getOffsetHeight();
+        } else {
+            // top defined or both undefined, available space == canvas - top
+            h = canvas.getOffsetHeight() - wrapper.getElement().getOffsetTop();
+        }
+
+        return new RenderSpace(w, h);
+    }
+
+    public boolean hasChildComponent(Widget component) {
+        for (Iterator<Entry<String, AbsoluteWrapper>> iterator = pidToComponentWrappper
+                .entrySet().iterator(); iterator.hasNext();) {
+            if (iterator.next().getValue().paintable == component) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
+        for (Widget wrapper : getChildren()) {
+            AbsoluteWrapper w = (AbsoluteWrapper) wrapper;
+            if (w.getWidget() == oldComponent) {
+                w.setWidget(newComponent);
+                return;
+            }
+        }
+    }
+
+    public boolean requestLayout(Set<Paintable> children) {
+        // component inside an absolute panel never affects parent nor the
+        // layout
+        return true;
+    }
+
+    public void updateCaption(Paintable component, UIDL uidl) {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+        rendering = true;
+        this.client = client;
+        // TODO margin handling
+        if (client.updateComponent(this, uidl, true)) {
+            rendering = false;
+            return;
+        }
+
+        HashSet<String> unrenderedPids = new HashSet<String>(
+                pidToComponentWrappper.keySet());
+
+        for (Iterator<UIDL> childIterator = uidl.getChildIterator(); childIterator
+                .hasNext();) {
+            UIDL cc = childIterator.next();
+            UIDL componentUIDL = cc.getChildUIDL(0);
+            unrenderedPids.remove(componentUIDL.getId());
+            getWrapper(client, componentUIDL).updateFromUIDL(cc);
+        }
+
+        for (String pid : unrenderedPids) {
+            AbsoluteWrapper absoluteWrapper = pidToComponentWrappper.get(pid);
+            pidToComponentWrappper.remove(pid);
+            absoluteWrapper.destroy();
+        }
+        rendering = false;
+    }
+
+    private 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);
+    }
+
+    @Override
+    public void setStyleName(String style) {
+        super.setStyleName(style);
+        if (previousStyleName == null || !previousStyleName.equals(style)) {
+            excessPixelsHorizontal = -1;
+            excessPixelsVertical = -1;
+        }
+    }
+
+    @Override
+    public void setWidth(String width) {
+        super.setWidth(width);
+        // TODO do this so that canvas gets the sized properly (the area
+        // inside marginals)
+        canvas.getStyle().setProperty("width", width);
+
+        if (!rendering && BrowserInfo.get().isIE6()) {
+            relayoutWrappersForIe6();
+        }
+    }
+
+    @Override
+    public void setHeight(String height) {
+        super.setHeight(height);
+        // TODO do this so that canvas gets the sized properly (the area
+        // inside marginals)
+        canvas.getStyle().setProperty("height", height);
+
+        if (!rendering && BrowserInfo.get().isIE6()) {
+            relayoutWrappersForIe6();
+        }
+    }
+
+    private void relayoutWrappersForIe6() {
+        for (Widget wrapper : getChildren()) {
+            ((AbsoluteWrapper) wrapper).ie6Layout();
+        }
+    }
+
+    public class AbsoluteWrapper extends SimplePanel {
+        private String css;
+        private String left;
+        private String top;
+        private String right;
+        private String bottom;
+        private String zIndex;
+
+        private Paintable paintable;
+
+        public AbsoluteWrapper(Paintable paintable) {
+            this.paintable = paintable;
+            setStyleName(CLASSNAME + "-wrapper");
+        }
+
+        public void destroy() {
+            client.unregisterPaintable(paintable);
+            removeFromParent();
+        }
+
+        public void updateFromUIDL(UIDL componentUIDL) {
+            setPosition(componentUIDL.getStringAttribute("css"));
+            if (getWidget() != paintable) {
+                setWidget((Widget) paintable);
+            }
+            paintable.updateFromUIDL(componentUIDL.getChildUIDL(0), client);
+        }
+
+        public void setPosition(String stringAttribute) {
+            if (css == null || !css.equals(stringAttribute)) {
+                css = stringAttribute;
+                top = right = bottom = left = zIndex = null;
+                if (!css.equals("")) {
+                    String[] properties = css.split(";");
+                    for (int i = 0; i < properties.length; i++) {
+                        String[] keyValue = properties[i].split(":");
+                        if (keyValue[0].equals("left")) {
+                            left = keyValue[1];
+                        } else if (keyValue[0].equals("top")) {
+                            top = keyValue[1];
+                        } else if (keyValue[0].equals("right")) {
+                            right = keyValue[1];
+                        } else if (keyValue[0].equals("bottom")) {
+                            bottom = keyValue[1];
+                        } else if (keyValue[0].equals("z-index")) {
+                            zIndex = keyValue[1];
+                        }
+                    }
+                }
+                // ensure ne values
+                Style style = getElement().getStyle();
+                style.setProperty("zIndex", zIndex);
+                style.setProperty("top", top);
+                style.setProperty("left", left);
+                style.setProperty("right", right);
+                style.setProperty("bottom", bottom);
+
+                if (BrowserInfo.get().isIE6()) {
+                    ie6Layout();
+                }
+            }
+
+        }
+
+        private void ie6Layout() {
+            // special handling for IE6 is needed, it does not support
+            // setting both left/right or top/bottom
+            Style style = getElement().getStyle();
+            if (bottom != null && top != null) {
+                // define height for wrapper to simulate bottom property
+                int bottompixels = measureForIE6(bottom);
+                ApplicationConnection.getConsole().log("ALB" + bottompixels);
+                int height = canvas.getOffsetHeight() - bottompixels
+                        - getElement().getOffsetTop();
+                ApplicationConnection.getConsole().log("ALB" + height);
+                if (height < 0) {
+                    height = 0;
+                }
+                style.setPropertyPx("height", height);
+            } else {
+                // reset possibly existing value
+                style.setProperty("height", "");
+            }
+            if (left != null && right != null) {
+                // define width for wrapper to simulate right property
+                int rightPixels = measureForIE6(right);
+                ApplicationConnection.getConsole().log("ALR" + rightPixels);
+                int width = canvas.getOffsetWidth() - rightPixels
+                        - getElement().getOffsetWidth();
+                ApplicationConnection.getConsole().log("ALR" + width);
+                if (width < 0) {
+                    width = 0;
+                }
+                style.setPropertyPx("width", width);
+            } else {
+                // reset possibly existing value
+                style.setProperty("width", "");
+            }
+        }
+
+    }
+
+    private Element measureElement;
+
+    private int measureForIE6(String cssLength) {
+        if (measureElement == null) {
+            measureElement = DOM.createDiv();
+            measureElement.getStyle().setProperty("position", "absolute");
+            canvas.appendChild(measureElement);
+        }
+        measureElement.getStyle().setProperty("width", cssLength);
+        return measureElement.getOffsetWidth();
+    }
+
+}
diff --git a/src/com/itmill/toolkit/tests/layouts/TestAbsoluteLayout.java b/src/com/itmill/toolkit/tests/layouts/TestAbsoluteLayout.java
new file mode 100644 (file)
index 0000000..6631df2
--- /dev/null
@@ -0,0 +1,54 @@
+package com.itmill.toolkit.tests.layouts;\r
+\r
+import com.itmill.toolkit.tests.components.TestBase;\r
+import com.itmill.toolkit.ui.AbsoluteLayout;\r
+import com.itmill.toolkit.ui.Button;\r
+import com.itmill.toolkit.ui.Label;\r
+\r
+public class TestAbsoluteLayout extends TestBase {\r
+\r
+    @Override\r
+    protected String getDescription() {\r
+        return "This is absolute layout tester.";\r
+    }\r
+\r
+    @Override\r
+    protected Integer getTicketNumber() {\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    protected void setup() {\r
+        AbsoluteLayout layout = new AbsoluteLayout();\r
+        setTheme("tests-tickets");\r
+        layout.setStyleName("cyan");\r
+\r
+        layout.addComponent(new Label("Hello World"));\r
+\r
+        Button button = new Button("Centered button,z-index:10;");\r
+        button.setSizeFull();\r
+        layout.addComponent(button,\r
+                "top:40%;bottom:40%;right:20%;left:20%;z-index:10;");\r
+\r
+        Label label = new Label(\r
+                "Exotic positioned label. Fullsize, top:100px; left:2cm; right: 3.5in; bottom:12.12mm ");\r
+        label.setStyleName("yellow");\r
+        label.setSizeFull();\r
+        layout.addComponent(label,\r
+                "top:100px; left:2cm; right: 3.5in; bottom:12.12mm");\r
+\r
+        label = new Label("fullize, bottom:80%;left:80%;");\r
+        label.setStyleName("green");\r
+        label.setSizeFull();\r
+        layout.addComponent(label, "bottom:80%;left:80%;");\r
+\r
+        label = new Label("bottomright");\r
+        label.setSizeUndefined();\r
+        label.setStyleName("green");\r
+        layout.addComponent(label, "bottom:0px; right:0px;");\r
+\r
+        getLayout().setSizeFull();\r
+        getLayout().addComponent(layout);\r
+\r
+    }\r
+}\r
diff --git a/src/com/itmill/toolkit/ui/AbsoluteLayout.java b/src/com/itmill/toolkit/ui/AbsoluteLayout.java
new file mode 100644 (file)
index 0000000..61a12bb
--- /dev/null
@@ -0,0 +1,210 @@
+package com.itmill.toolkit.ui;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.itmill.toolkit.terminal.PaintException;
+import com.itmill.toolkit.terminal.PaintTarget;
+import com.itmill.toolkit.terminal.gwt.client.ui.IAbsoluteLayout;
+
+/**
+ * AbsoluteLayout is a layout implementation that mimics html absolute
+ * positioning.
+ * 
+ */
+public class AbsoluteLayout extends AbstractLayout {
+
+    private Collection<Component> components = new HashSet<Component>();
+    private Map<Component, ComponentPosition> componentToCoordinates = new HashMap<Component, ComponentPosition>();
+
+    public AbsoluteLayout() {
+        setSizeFull();
+    }
+
+    @Override
+    public String getTag() {
+        return IAbsoluteLayout.TAGNAME;
+    }
+
+    public Iterator<Component> getComponentIterator() {
+        return components.iterator();
+    }
+
+    public void replaceComponent(Component oldComponent, Component newComponent) {
+        ComponentPosition position = getPosition(oldComponent);
+        removeComponent(oldComponent);
+        addComponent(newComponent);
+        componentToCoordinates.put(newComponent, position);
+    }
+
+    @Override
+    public void addComponent(Component c) {
+        components.add(c);
+        super.addComponent(c);
+    }
+
+    @Override
+    public void removeComponent(Component c) {
+        components.remove(c);
+        super.removeComponent(c);
+    }
+
+    public void addComponent(Component c, String cssPosition) {
+        addComponent(c);
+        getPosition(c).setCSSString(cssPosition);
+    }
+
+    public ComponentPosition getPosition(Component component) {
+        if (componentToCoordinates.containsKey(component)) {
+            return componentToCoordinates.get(component);
+        } else {
+            ComponentPosition coords = new ComponentPosition();
+            componentToCoordinates.put(component, coords);
+            return coords;
+        }
+    }
+
+    /**
+     * TODO symmetric getters and setters for fields to make this simpler to use
+     * in generic java tools
+     * 
+     */
+    public class ComponentPosition {
+
+        private int zIndex = -1;
+        private float top = -1;
+        private float right = -1;
+        private float bottom = -1;
+        private float left = -1;
+
+        private int topUnits;
+        private int rightUnits;
+        private int bottomUnits;
+        private int leftUnits;
+
+        /**
+         * Sets the position attributes using CSS syntax. Example usage:
+         * 
+         * <code><pre>
+         * setCSSString("top:10px;left:20%;z-index:16;");
+         * </pre></code>
+         * 
+         * @param css
+         */
+        public void setCSSString(String css) {
+            String[] cssProperties = css.split(";");
+            for (int i = 0; i < cssProperties.length; i++) {
+                String[] keyValuePair = cssProperties[i].split(":");
+                String key = keyValuePair[0].trim();
+                if (key.equals("z-index")) {
+                    zIndex = Integer.parseInt(keyValuePair[1]);
+                } else {
+                    String value = keyValuePair[1].trim();
+                    String unit = value.replaceAll("[0-9\\.]+", "");
+                    if (!unit.equals("")) {
+                        value = value.substring(0, value.indexOf(unit)).trim();
+                    }
+                    float v = Float.parseFloat(value);
+                    int unitInt = parseCssUnit(unit);
+                    if (key.equals("top")) {
+                        top = v;
+                        topUnits = unitInt;
+                    } else if (key.equals("right")) {
+                        right = v;
+                        rightUnits = unitInt;
+                    } else if (key.equals("bottom")) {
+                        bottom = v;
+                        bottomUnits = unitInt;
+                    } else if (key.equals("left")) {
+                        left = v;
+                        leftUnits = unitInt;
+                    }
+                }
+            }
+            requestRepaint();
+        }
+
+        private int parseCssUnit(String string) {
+            for (int i = 0; i < UNIT_SYMBOLS.length; i++) {
+                if (UNIT_SYMBOLS[i].equals(string)) {
+                    return i;
+                }
+            }
+            return 0; // defaults to px (eg. top:0;)
+        }
+
+        public String getCSSString() {
+            String s = "";
+            if (top >= 0) {
+                s += "top:" + top + UNIT_SYMBOLS[topUnits] + ";";
+            }
+            if (right >= 0) {
+                s += "right:" + right + UNIT_SYMBOLS[rightUnits] + ";";
+            }
+            if (bottom >= 0) {
+                s += "bottom:" + bottom + UNIT_SYMBOLS[bottomUnits] + ";";
+            }
+            if (left >= 0) {
+                s += "left:" + left + UNIT_SYMBOLS[leftUnits] + ";";
+            }
+            if (zIndex >= 0) {
+                s += "z-index:" + zIndex + ";";
+            }
+            return s;
+        }
+
+        public void setTop(float topValue, int topUnits) {
+            validateLength(topValue, topUnits);
+            top = topValue;
+            this.topUnits = topUnits;
+            requestRepaint();
+        }
+
+        public void setRight(float rightValue, int rightUnits) {
+            validateLength(rightValue, rightUnits);
+            right = rightValue;
+            this.rightUnits = rightUnits;
+            requestRepaint();
+        }
+
+        public void setBottom(float bottomValue, int units) {
+            validateLength(bottomValue, units);
+            bottom = bottomValue;
+            bottomUnits = units;
+            requestRepaint();
+        }
+
+        public void setLeft(float leftValue, int units) {
+            validateLength(leftValue, units);
+            left = leftValue;
+            leftUnits = units;
+            requestRepaint();
+        }
+
+        public void setZIndex(int zIndex) {
+            this.zIndex = zIndex;
+            requestRepaint();
+        }
+
+    }
+
+    @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");
+        }
+    }
+
+    private static void validateLength(float topValue, int topUnits2) {
+        // TODO throw on invalid value
+
+    }
+
+}