]> source.dussan.org Git - vaadin-framework.git/commitdiff
initial version of CssLayout (aka FlowLayout from FastLayouts) + sampler example
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Fri, 28 Aug 2009 13:38:54 +0000 (13:38 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Fri, 28 Aug 2009 13:38:54 +0000 (13:38 +0000)
svn changeset:8579/svn branch:6.1

src/com/vaadin/demo/sampler/FeatureSet.java
src/com/vaadin/demo/sampler/features/layouts/CssLayouts.java [new file with mode: 0644]
src/com/vaadin/demo/sampler/features/layouts/CssLayoutsExample.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java
src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java [new file with mode: 0644]
src/com/vaadin/ui/CssLayout.java [new file with mode: 0644]

index a20bbaba9227b6c976ea3fa832b63cc4c330c42e..8936d450dc320343e743f132a22b21322cd856de 100644 (file)
@@ -22,6 +22,7 @@ import com.vaadin.demo.sampler.features.dates.DatePopup;
 import com.vaadin.demo.sampler.features.dates.DateResolution;
 import com.vaadin.demo.sampler.features.form.FormBasic;
 import com.vaadin.demo.sampler.features.layouts.ApplicationLayout;
+import com.vaadin.demo.sampler.features.layouts.CssLayouts;
 import com.vaadin.demo.sampler.features.layouts.CustomLayouts;
 import com.vaadin.demo.sampler.features.layouts.ExpandingComponent;
 import com.vaadin.demo.sampler.features.layouts.GridLayoutBasic;
@@ -247,6 +248,7 @@ public class FeatureSet extends Feature {
                             new ApplicationLayout(), //
                             new WebLayout(), //
                             new CustomLayouts(), //
+                            new CssLayouts(),//
                     });
         }
     }
diff --git a/src/com/vaadin/demo/sampler/features/layouts/CssLayouts.java b/src/com/vaadin/demo/sampler/features/layouts/CssLayouts.java
new file mode 100644 (file)
index 0000000..04b2181
--- /dev/null
@@ -0,0 +1,49 @@
+package com.vaadin.demo.sampler.features.layouts;\r
+\r
+import com.vaadin.demo.sampler.APIResource;\r
+import com.vaadin.demo.sampler.Feature;\r
+import com.vaadin.demo.sampler.NamedExternalResource;\r
+import com.vaadin.ui.HorizontalLayout;\r
+import com.vaadin.ui.VerticalLayout;\r
+\r
+@SuppressWarnings("serial")\r
+public class CssLayouts extends Feature {\r
+\r
+    @Override\r
+    public String getName() {\r
+        return "Css layout";\r
+    }\r
+\r
+    @Override\r
+    public String getDescription() {\r
+        // TODO\r
+        return "Most commonly developers usign Vaadin don't want to think "\r
+                + "of the browser environment at all. With the flexible "\r
+                + "layout API found from grid, horizontal and vertical "\r
+                + "layout developers can build almost anything with plain "\r
+                + "Java. But sometimes experienced web developers miss "\r
+                + "flexibility of CSS and HTML. CssLayout is a simple "\r
+                + "layout that puts contained componets into div element. "\r
+                + "It has a simple DOM structure and it leaves all the power "\r
+                + "to CSS designer hands. Having a very narrow feature set"\r
+                + ", CssLayout is also the fastest layout to render in "\r
+                + "Vaadin.";\r
+    }\r
+\r
+    @Override\r
+    public APIResource[] getRelatedAPI() {\r
+        return new APIResource[] { new APIResource(HorizontalLayout.class),\r
+                new APIResource(VerticalLayout.class) };\r
+    }\r
+\r
+    @SuppressWarnings("unchecked")\r
+    @Override\r
+    public Class<? extends Feature>[] getRelatedFeatures() {\r
+        return new Class[] { ApplicationLayout.class, CustomLayouts.class };\r
+    }\r
+\r
+    @Override\r
+    public NamedExternalResource[] getRelatedResources() {\r
+        return null;\r
+    }\r
+}\r
diff --git a/src/com/vaadin/demo/sampler/features/layouts/CssLayoutsExample.java b/src/com/vaadin/demo/sampler/features/layouts/CssLayoutsExample.java
new file mode 100644 (file)
index 0000000..08077e4
--- /dev/null
@@ -0,0 +1,91 @@
+package com.vaadin.demo.sampler.features.layouts;\r
+\r
+import com.vaadin.ui.Component;\r
+import com.vaadin.ui.CssLayout;\r
+import com.vaadin.ui.Label;\r
+import com.vaadin.ui.Panel;\r
+import com.vaadin.ui.VerticalLayout;\r
+\r
+@SuppressWarnings("serial")\r
+public class CssLayoutsExample extends VerticalLayout {\r
+\r
+    public CssLayoutsExample() {\r
+        setMargin(true);\r
+\r
+        /*\r
+         * Note that adding inline style like this is a very bad programming\r
+         * habit in common. The correct place for css is in theme. We do it here\r
+         * to keep css in java code for demonstration purposes.\r
+         */\r
+        Label demostyle = new Label(\r
+                "<style>"\r
+                        + ".floatedpanel {float:right;} "\r
+                        + ".footer {width: 3in; padding: 3px;margin-left:auto;margin-right:auto;"\r
+                        + "clear:both; height:40px; background-color: grey; white-space:normal;} "\r
+                        + ".brick {text-align:center;width: 100px; height: 100px; margin: 10px;padding:10px;"\r
+                        + "background-color:#1E2123; color:white;float:left; border: 3px solid black;} "\r
+                        + "</style>", Label.CONTENT_XHTML);\r
+\r
+        addComponent(demostyle);\r
+\r
+        final Panel panel = new Panel("Panel");\r
+        panel.setStyleName("floatedpanel");\r
+        panel.setWidth("30%");\r
+        panel.setHeight("370px");\r
+        panel.addComponent(new Label("This panel is 30% width "\r
+                + "and 370px height(defined on server side) "\r
+                + "and floated right (with custom css). "\r
+                + "Try resizesing browser window to see "\r
+                + "how black boxes (floated to left) "\r
+                + "behave. Every third of them has red "\r
+                + "color to demonstrate dynamic css injection. "));\r
+\r
+        final Label bottomCenter = new Label(\r
+                "I'm 3 inches wide footer at the bottom "\r
+                        + "of the layout (and centered unless I'm in IE6)");\r
+        bottomCenter.setSizeUndefined(); // disable 100% default width\r
+        bottomCenter.setStyleName("footer");\r
+        // bottomCenter.setWidth("50px");\r
+\r
+        CssLayout cssLayout = new CssLayout() {\r
+            int brickCounter = 0;\r
+\r
+            @Override\r
+            protected String getCss(Component c) {\r
+                // colorize every third rendered brick\r
+                if (c instanceof Brick) {\r
+                    brickCounter++;\r
+                    if (brickCounter % 3 == 0) {\r
+                        // make every third brick red and bold\r
+                        return "color: #ff6611; font-weight:bold;";\r
+                    }\r
+                }\r
+                return null;\r
+            }\r
+        };\r
+\r
+        cssLayout.setWidth("100%");\r
+\r
+        cssLayout.addComponent(panel);\r
+        for (int i = 0; i < 15; i++) {\r
+            // add black labels that float left\r
+            cssLayout.addComponent(new Brick());\r
+        }\r
+        cssLayout.addComponent(bottomCenter);\r
+\r
+        addComponent(cssLayout);\r
+    }\r
+\r
+    /**\r
+     * A simple label containing text "Brick" and themed black square.\r
+     */\r
+    static class Brick extends Label {\r
+        public Brick() {\r
+            super("Brick");\r
+            // disable 100% that label has by default\r
+            setSizeUndefined();\r
+            setStyleName("brick");\r
+        }\r
+    }\r
+\r
+}
\ No newline at end of file
index f72859dbfbe3d940e15ee391d360c4cba9f06cef..4c3f9c83d14c0d5f344844f6f5f0cf6fbce4ba57 100644 (file)
@@ -14,6 +14,7 @@ import com.vaadin.terminal.gwt.client.ui.VCustomLayout;
 import com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar;
 import com.vaadin.terminal.gwt.client.ui.VEmbedded;
 import com.vaadin.terminal.gwt.client.ui.VFilterSelect;
+import com.vaadin.terminal.gwt.client.ui.VCssLayout;
 import com.vaadin.terminal.gwt.client.ui.VForm;
 import com.vaadin.terminal.gwt.client.ui.VFormLayout;
 import com.vaadin.terminal.gwt.client.ui.VGridLayout;
@@ -145,6 +146,8 @@ public class DefaultWidgetSet implements WidgetSet {
             return new VUriFragmentUtility();
         } else if (VAbsoluteLayout.class == classType) {
             return new VAbsoluteLayout();
+        } else if (VCssLayout.class == classType) {
+            return new VCssLayout();
         }
 
         return new VUnknownComponent();
@@ -259,6 +262,8 @@ public class DefaultWidgetSet implements WidgetSet {
             return VUriFragmentUtility.class;
         } else if (VAbsoluteLayout.TAGNAME.equals(tag)) {
             return VAbsoluteLayout.class;
+        } else if (VCssLayout.TAGNAME.equals(tag)) {
+            return VCssLayout.class;
         }
 
         return VUnknownComponent.class;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java
new file mode 100644 (file)
index 0000000..875d562
--- /dev/null
@@ -0,0 +1,239 @@
+/* 
+@ITMillApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+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.FlowPanel;
+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.Container;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.RenderSpace;
+import com.vaadin.terminal.gwt.client.StyleConstants;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.VCaption;
+import com.vaadin.terminal.gwt.client.ValueMap;
+
+public class VCssLayout extends SimplePanel implements Paintable, Container {
+    public static final String TAGNAME = "csslayout";
+    public static final String CLASSNAME = "v-" + TAGNAME;
+
+    private FlowPane panel = new FlowPane();
+
+    private Element margin = DOM.createDiv();
+
+    private boolean hasHeight;
+    private boolean hasWidth;
+
+    public VCssLayout() {
+        super();
+        DOM.appendChild(getElement(), margin);
+        DOM.setStyleAttribute(getElement(), "overflow", "hidden");
+        setStyleName(CLASSNAME);
+        setWidget(panel);
+    }
+
+    @Override
+    protected Element getContainerElement() {
+        return margin;
+    }
+
+    @Override
+    public void setWidth(String width) {
+        super.setWidth(width);
+        panel.setWidth(width);
+        hasWidth = width != null && !width.equals("");
+    }
+
+    @Override
+    public void setHeight(String height) {
+        super.setHeight(height);
+        panel.setHeight(height);
+        hasHeight = height != null && !height.equals("");
+    }
+
+    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+        if (client.updateComponent(this, uidl, true)) {
+            return;
+        }
+
+        final VMarginInfo margins = new VMarginInfo(uidl
+                .getIntAttribute("margins"));
+
+        setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP,
+                margins.hasTop());
+        setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT,
+                margins.hasRight());
+        setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM,
+                margins.hasBottom());
+        setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT,
+                margins.hasLeft());
+
+        setStyleName(margin, CLASSNAME + "-" + "spacing", uidl
+                .hasAttribute("spacing"));
+        panel.updateFromUIDL(uidl, client);
+    }
+
+    public boolean hasChildComponent(Widget component) {
+        return panel.hasChildComponent(component);
+    }
+
+    public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
+        panel.replaceChildComponent(oldComponent, newComponent);
+    }
+
+    public void updateCaption(Paintable component, UIDL uidl) {
+        panel.updateCaption(component, uidl);
+    }
+
+    public class FlowPane extends FlowPanel {
+
+        private final HashMap<Widget, VCaption> widgetToCaption = new HashMap<Widget, VCaption>();
+        private ApplicationConnection client;
+
+        public FlowPane() {
+            super();
+            setStyleName(CLASSNAME + "-grid");
+        }
+
+        public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+
+            // for later requests
+            this.client = client;
+
+            final ArrayList<Widget> oldWidgets = new ArrayList<Widget>();
+            for (final Iterator<Widget> iterator = iterator(); iterator
+                    .hasNext();) {
+                oldWidgets.add(iterator.next());
+            }
+            clear();
+
+            ValueMap mapAttribute = null;
+            if (uidl.hasAttribute("css")) {
+                mapAttribute = uidl.getMapAttribute("css");
+            }
+
+            for (final Iterator<Object> i = uidl.getChildIterator(); i
+                    .hasNext();) {
+                final UIDL r = (UIDL) i.next();
+                final Paintable child = client.getPaintable(r);
+                if (oldWidgets.contains(child)) {
+                    oldWidgets.remove(child);
+                }
+
+                add((Widget) child);
+                if (mapAttribute != null && mapAttribute.containsKey(r.getId())) {
+                    String css = null;
+                    try {
+                        Style style = ((Widget) child).getElement().getStyle();
+                        css = mapAttribute.getString(r.getId());
+                        String[] cssRules = css.split(";");
+                        for (int j = 0; j < cssRules.length; j++) {
+                            String[] rule = cssRules[j].split(":");
+                            if (rule.length == 0) {
+                                continue;
+                            } else {
+                                style.setProperty(
+                                        makeCamelCase(rule[0].trim()), rule[1]
+                                                .trim());
+                            }
+                        }
+                    } catch (Exception e) {
+                        ApplicationConnection.getConsole().log(
+                                "CssLayout encounterd invalid css string: "
+                                        + css);
+                    }
+                }
+
+                if (!r.getBooleanAttribute("cached")) {
+                    child.updateFromUIDL(r, client);
+                }
+            }
+
+            // loop oldWidgetWrappers that where not re-attached and unregister
+            // them
+            for (final Iterator<Widget> it = oldWidgets.iterator(); it
+                    .hasNext();) {
+                final Paintable w = (Paintable) it.next();
+                client.unregisterPaintable(w);
+                widgetToCaption.remove(w);
+            }
+        }
+
+        public boolean hasChildComponent(Widget component) {
+            return component.getParent() == this;
+        }
+
+        public void replaceChildComponent(Widget oldComponent,
+                Widget newComponent) {
+            VCaption caption = widgetToCaption.get(oldComponent);
+            if (caption != null) {
+                remove(caption);
+                widgetToCaption.remove(oldComponent);
+            }
+            int index = getWidgetIndex(oldComponent);
+            if (index >= 0) {
+                remove(oldComponent);
+                insert(newComponent, index);
+            }
+        }
+
+        public void updateCaption(Paintable component, UIDL uidl) {
+            VCaption caption = widgetToCaption.get(component);
+            if (VCaption.isNeeded(uidl)) {
+                Widget widget = (Widget) component;
+                if (caption == null) {
+                    caption = new VCaption(component, client);
+                    widgetToCaption.put(widget, caption);
+                    insert(caption, getWidgetIndex(widget));
+                } else if (!caption.isAttached()) {
+                    insert(caption, getWidgetIndex(widget));
+                }
+                caption.updateCaption(uidl);
+            } else if (caption != null) {
+                remove(caption);
+                widgetToCaption.remove(component);
+            }
+        }
+    }
+
+    public RenderSpace getAllocatedSpace(Widget child) {
+        com.google.gwt.dom.client.Element div = child.getElement()
+                .getParentElement();
+        return new RenderSpace(div.getOffsetWidth(), div.getOffsetHeight());
+    }
+
+    public boolean requestLayout(Set<Paintable> children) {
+        if (hasSize()) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private boolean hasSize() {
+        return hasWidth || hasHeight;
+    }
+
+    private static final String makeCamelCase(String cssProperty) {
+        // TODO this might be cleaner to implement with regexp
+        while (cssProperty.contains("-")) {
+            int indexOf = cssProperty.indexOf("-");
+            cssProperty = cssProperty.substring(0, indexOf)
+                    + String.valueOf(cssProperty.charAt(indexOf + 1))
+                            .toUpperCase() + cssProperty.substring(indexOf + 2);
+        }
+        return cssProperty;
+    }
+}
diff --git a/src/com/vaadin/ui/CssLayout.java b/src/com/vaadin/ui/CssLayout.java
new file mode 100644 (file)
index 0000000..65cfa36
--- /dev/null
@@ -0,0 +1,225 @@
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.Paintable;
+import com.vaadin.terminal.gwt.client.ui.VCssLayout;
+
+/**
+ * CssLayout is a layout component that can be used in browser environment only.
+ * It simply renders components and their captions into a same div element.
+ * Component layout can then be adjusted with css.
+ * <p>
+ * In comparison to {@link HorizontalLayout} and {@link VerticalLayout}
+ * <ul>
+ * <li>rather similar server side api
+ * <li>no spacing, alignment or expand ratios
+ * <li>much simpler DOM that can be styled by skilled web developer
+ * <li>no abstraction of browser differences (developer must ensure that the
+ * result works properly on each browser)
+ * <li>different kind of handling for relative sizes (that are set from server
+ * side) (*)
+ * <li>noticeable faster rendering time in some situations as we rely more on
+ * browsers rendering engine.
+ * </ul>
+ *<p>
+ * With {@link CustomLayout} one can often achieve similar results (good looking
+ * layouts with web technologies), but with CustomLayout developer needs to work
+ * with fixed templates.
+ * <p>
+ * By extending CssLayout one can also inject some css rules straight to child
+ * components using {@link #getCss(Component)}.
+ * 
+ * <p>
+ * (*) Relative sizes (set from server side) are treated bit differently than in
+ * other layouts in Vaadin. In cssLayout the size is calculated relatively to
+ * CSS layouts content area which is pretty much as in html and css. In other
+ * layouts the size of component is calculated relatively to the "slot" given by
+ * layout.
+ * <p>
+ * Also note that client side framework in Vaadin modifies inline style
+ * properties width and height. This happens on each update to component. If one
+ * wants to set component sizes with CSS, component must have undefined size on
+ * server side (which is not the default for all components) and the size must
+ * be defined with class styles - not by directly injecting width and height.
+ * 
+ * @since 6.1 brought in from "FastLayouts" incubator project
+ * 
+ */
+public class CssLayout extends AbstractLayout {
+
+    private static final long serialVersionUID = -6408703812053460073L;
+
+    @Override
+    public String getTag() {
+        return VCssLayout.TAGNAME;
+    }
+
+    /**
+     * Custom layout slots containing the components.
+     */
+    protected LinkedList<Component> components = new LinkedList<Component>();
+
+    /**
+     * Add a component into this container. The component is added to the right
+     * or under the previous component.
+     * 
+     * @param c
+     *            the component to be added.
+     */
+    @Override
+    public void addComponent(Component c) {
+        super.addComponent(c);
+        components.add(c);
+        requestRepaint();
+    }
+
+    /**
+     * Adds a component into this container. The component is added to the left
+     * or on top of the other components.
+     * 
+     * @param c
+     *            the component to be added.
+     */
+    public void addComponentAsFirst(Component c) {
+        super.addComponent(c);
+        components.addFirst(c);
+        requestRepaint();
+    }
+
+    /**
+     * Adds a component into indexed position in this container.
+     * 
+     * @param c
+     *            the component to be added.
+     * @param index
+     *            the Index of the component position. The components currently
+     *            in and after the position are shifted forwards.
+     */
+    public void addComponent(Component c, int index) {
+        super.addComponent(c);
+        components.add(index, c);
+        requestRepaint();
+    }
+
+    /**
+     * Removes the component from this container.
+     * 
+     * @param c
+     *            the component to be removed.
+     */
+    @Override
+    public void removeComponent(Component c) {
+        super.removeComponent(c);
+        components.remove(c);
+        requestRepaint();
+    }
+
+    /**
+     * Gets the component container iterator for going trough all the components
+     * in the container.
+     * 
+     * @return the Iterator of the components inside the container.
+     */
+    public Iterator getComponentIterator() {
+        return components.iterator();
+    }
+
+    /**
+     * Paints the content of this component.
+     * 
+     * @param target
+     *            the Paint Event.
+     * @throws PaintException
+     *             if the paint operation failed.
+     */
+    @Override
+    public void paintContent(PaintTarget target) throws PaintException {
+        super.paintContent(target);
+        HashMap<Paintable, String> componentCss = null;
+        // Adds all items in all the locations
+        for (Component c : components) {
+            // Paint child component UIDL
+            c.paint(target);
+            String componentCssString = getCss(c);
+            if (componentCssString != null) {
+                if (componentCss == null) {
+                    componentCss = new HashMap<Paintable, String>();
+                }
+                componentCss.put(c, componentCssString);
+            }
+        }
+        if (componentCss != null) {
+            target.addAttribute("css", componentCss);
+        }
+    }
+
+    /**
+     * Returns styles to be applied to given component. Override this method to
+     * inject custom style rules to components.
+     * 
+     * <p>
+     * Note that styles are injected over previous styles before actual child
+     * rendering. Previous styles are not cleared, but overridden.
+     * 
+     * <p>
+     * Note that one most often achieves better code style, by separating
+     * styling to theme (with custom theme and {@link #addStyleName(String)}.
+     * With own custom styles it is also very easy to break browser
+     * compatibility.
+     * 
+     * @param c
+     *            the component
+     * @return css rules to be applied to component
+     */
+    protected String getCss(Component c) {
+        return null;
+    }
+
+    /* Documented in superclass */
+    public void replaceComponent(Component oldComponent, Component newComponent) {
+
+        // Gets the locations
+        int oldLocation = -1;
+        int newLocation = -1;
+        int location = 0;
+        for (final Iterator i = components.iterator(); i.hasNext();) {
+            final Component component = (Component) i.next();
+
+            if (component == oldComponent) {
+                oldLocation = location;
+            }
+            if (component == newComponent) {
+                newLocation = location;
+            }
+
+            location++;
+        }
+
+        if (oldLocation == -1) {
+            addComponent(newComponent);
+        } else if (newLocation == -1) {
+            removeComponent(oldComponent);
+            addComponent(newComponent, oldLocation);
+        } else {
+            if (oldLocation > newLocation) {
+                components.remove(oldComponent);
+                components.add(newLocation, oldComponent);
+                components.remove(newComponent);
+                components.add(oldLocation, newComponent);
+            } else {
+                components.remove(newComponent);
+                components.add(oldLocation, newComponent);
+                components.remove(oldComponent);
+                components.add(newLocation, oldComponent);
+            }
+
+            requestRepaint();
+        }
+    }
+
+}