From: Matti Tahvonen Date: Wed, 6 Feb 2008 07:58:22 +0000 (+0000) Subject: "Sizeable" Grid and OrderedLayout X-Git-Tag: 6.7.0.beta1~5103 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dc9d89453dfe5a87ceeb97b9ef4098362d4b0fbf;p=vaadin-framework.git "Sizeable" Grid and OrderedLayout svn changeset:3714/svn branch:trunk --- diff --git a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java index b3ee13f7a3..edf4bf3d81 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java @@ -43,6 +43,8 @@ import com.itmill.toolkit.terminal.gwt.client.ui.ITwinColSelect; import com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent; import com.itmill.toolkit.terminal.gwt.client.ui.IUpload; import com.itmill.toolkit.terminal.gwt.client.ui.IWindow; +import com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableGridLayout; +import com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableOrderedLayout; import com.itmill.toolkit.terminal.gwt.client.ui.richtextarea.IRichTextArea; public class DefaultWidgetSet implements WidgetSet { @@ -68,6 +70,9 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IWindow" .equals(className)) { return new IWindow(); + } else if ("com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableOrderedLayout" + .equals(className)) { + return new ISizeableOrderedLayout(); } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutVertical" .equals(className)) { return new IOrderedLayoutVertical(); @@ -80,6 +85,9 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("com.itmill.toolkit.terminal.gwt.client.ui.ILink" .equals(className)) { return new ILink(); + } else if ("com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableGridLayout" + .equals(className)) { + return new ISizeableGridLayout(); } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IGridLayout" .equals(className)) { return new IGridLayout(); @@ -192,7 +200,10 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("window".equals(tag)) { return "com.itmill.toolkit.terminal.gwt.client.ui.IWindow"; } else if ("orderedlayout".equals(tag)) { - if ("horizontal".equals(uidl.getStringAttribute("orientation"))) { + if (uidl.hasAttribute("height")) { + return "com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableOrderedLayout"; + } else if ("horizontal".equals(uidl + .getStringAttribute("orientation"))) { return "com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutHorizontal"; } else { return "com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutVertical"; @@ -202,7 +213,14 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("link".equals(tag)) { return "com.itmill.toolkit.terminal.gwt.client.ui.ILink"; } else if ("gridlayout".equals(tag)) { - return "com.itmill.toolkit.terminal.gwt.client.ui.IGridLayout"; + if (uidl.hasAttribute("height")) { + // height needs to be set to use sizeable grid layout, with + // width only or no size at all it fails to render properly. + return "com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid.ISizeableGridLayout"; + } else { + // Fall back to GWT FlexTable based implementation. + return "com.itmill.toolkit.terminal.gwt.client.ui.IGridLayout"; + } } else if ("tree".equals(tag)) { return "com.itmill.toolkit.terminal.gwt.client.ui.ITree"; } else if ("select".equals(tag)) { diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/AbsoluteGrid.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/AbsoluteGrid.java new file mode 100644 index 0000000000..e2fb89f733 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/AbsoluteGrid.java @@ -0,0 +1,297 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid; + +import java.util.HashMap; +import java.util.Iterator; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.AbsolutePanel; +import com.google.gwt.user.client.ui.Composite; +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.Caption; +import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener; +import com.itmill.toolkit.terminal.gwt.client.Util; +import com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo; + +/** + * Prototype helper widget to implement complex sized Toolkit layouts like + * GridLayout and OrderedLayout. Supports size, margins, spacing, but has bit + * expensive layout function. + */ +public class AbsoluteGrid extends Composite implements ContainerResizedListener { + + protected HashMap cells = new HashMap(); + + private int cols = 1; + private int rows = 1; + + private AbsolutePanel ap; + + protected int marginTop; + protected int marginBottom; + protected int marginLeft; + protected int marginRight; + + private int offsetWidth; + + private int offsetHeight; + + public AbsoluteGrid() { + ap = new AbsolutePanel(); + initWidget(ap); + } + + public AbsoluteGridCell getCell(int col, int row) { + AbsoluteGridCell p = (AbsoluteGridCell) cells.get(col + "." + row); + if (p == null) { + p = new AbsoluteGridCell(col, row); + cells.put(col + "." + row, p); + ap.add(p); + } + return p; + } + + public void clear() { + ap.clear(); + cells.clear(); + } + + public Iterator getCellIterator() { + return cells.values().iterator(); + } + + private float getCellWidth(int colspan) { + int total = ap.getOffsetWidth(); + total -= getMarginWidth(); + total -= getSpacingSize() * (cols - colspan); + if (total < 0) { + return 0; + } + return total * colspan / (float) cols; + } + + /** + * + * @return space used by left and right margin + */ + private int getMarginWidth() { + return marginLeft + marginRight; + } + + /** + * @return pixels reserved for space between components + */ + protected int getSpacingSize() { + return 0; + } + + private float getCellHeight(int rowspan) { + int total = ap.getOffsetHeight(); + total -= getMarginHeight(); + total -= getSpacingSize() * (rows - rowspan); + if (total < 0) { + return 0; + } + return total * rowspan / (float) rows; + } + + /** + * + * @return space used by top and bottom margin + */ + private int getMarginHeight() { + return marginBottom + marginTop; + } + + /** + * TODO contains Caption (which is a widget) in a very bad way, cannot be + * simple panel + */ + public class AbsoluteGridCell extends SimplePanel { + + int rowIndex; + int colIndex; + int colSpan = 1; + int rowSpan = 1; + private Element container = DOM.createDiv(); + + private Caption caption; + private AlignmentInfo alignmentInfo = new AlignmentInfo( + AlignmentInfo.ALIGNMENT_TOP + AlignmentInfo.ALIGNMENT_LEFT); + + AbsoluteGridCell(int colIndex, int rowIndex) { + super(); + DOM.appendChild(getElement(), container); + this.rowIndex = rowIndex; + this.colIndex = colIndex; + } + + public void clear() { + super.clear(); + if (caption != null) { + DOM.removeChild(getElement(), caption.getElement()); + caption = null; + } + } + + protected Element getContainerElement() { + return container; + } + + void setColSpan(int s) { + // TODO Should remove possibly collapsing cells + colSpan = s; + } + + void setRowSpan(int s) { + // TODO Should remove possibly collapsing cells + rowSpan = s; + } + + private int getLeft() { + int left = marginLeft; + left += colIndex * getCellWidth(1); + left += getSpacingSize() * colIndex; + return left; + } + + private int getTop() { + int top = marginTop; + top += rowIndex * getCellHeight(1); + top += getSpacingSize() * rowIndex; + return top; + } + + public void render() { + setPixelSize((int) getCellWidth(colSpan), + (int) getCellHeight(rowSpan)); + ap.setWidgetPosition(this, getLeft(), getTop()); + } + + /** + * Does vertical positioning based on DOM values + */ + public void vAling() { + DOM.setStyleAttribute(getElement(), "paddingTop", "0"); + if (!alignmentInfo.isTop()) { + Widget c = getWidget(); + if (c != null) { + + int oh = getOffsetHeight(); + int wt = DOM.getElementPropertyInt(container, "offsetTop"); + int wh = c.getOffsetHeight(); + + int freeSpace = getOffsetHeight() + - (DOM + .getElementPropertyInt(container, + "offsetTop") + c.getOffsetHeight()); + if (Util.isIE()) { + freeSpace -= DOM.getElementPropertyInt(c.getElement(), + "offsetTop"); + } + if (freeSpace < 0) { + freeSpace = 0; // clipping rest of contents when object + // larger than reserved area + } + if (alignmentInfo.isVerticalCenter()) { + ApplicationConnection.getConsole().log("oh" + oh); + ApplicationConnection.getConsole().log("wt" + wt); + ApplicationConnection.getConsole().log("wh" + wh); + ApplicationConnection.getConsole().log( + "freespace" + freeSpace); + + DOM.setStyleAttribute(getElement(), "paddingTop", + (freeSpace / 2) + "px"); + } else { + DOM.setStyleAttribute(getElement(), "paddingTop", + (freeSpace) + "px"); + } + } + } + } + + public void setPixelSize(int width, int height) { + super.setPixelSize(width, height); + DOM.setStyleAttribute(container, "width", width + "px"); + int contHeight = height - getCaptionHeight(); + if (contHeight < 0) { + contHeight = 0; + } + DOM.setStyleAttribute(container, "height", contHeight + "px"); + } + + private int getCaptionHeight() { + // remove hard coded caption height + return (caption == null) ? 0 : caption.getOffsetHeight(); + } + + public Caption getCaption() { + return caption; + } + + public void setCaption(Caption newCaption) { + // TODO check for existing, shouldn't happen though + caption = newCaption; + DOM.insertChild(getElement(), caption.getElement(), 0); + } + + public void setAlignment(int bitmask) { + if (alignmentInfo.getBitMask() != bitmask) { + alignmentInfo = new AlignmentInfo(bitmask); + setHorizontalAling(); + // vertical align is set in render() method + } + } + + private void setHorizontalAling() { + DOM.setStyleAttribute(getElement(), "textAlign", alignmentInfo + .getHorizontalAlignment()); + } + } + + public void iLayout() { + boolean sizeChanged = false; + int newWidth = getOffsetWidth(); + if (offsetWidth != newWidth) { + offsetWidth = newWidth; + sizeChanged = true; + } + int newHeight = getOffsetHeight(); + if (offsetHeight != newHeight) { + offsetHeight = newHeight; + sizeChanged = true; + } + if (sizeChanged) { + for (Iterator it = cells.values().iterator(); it.hasNext();) { + AbsoluteGridCell cell = (AbsoluteGridCell) it.next(); + cell.render(); + cell.vAling(); + } + Util.runDescendentsLayout(ap); + } + } + + public int getCols() { + return cols; + } + + public void setCols(int cols) { + this.cols = cols; + // force relayout + offsetHeight = 0; + offsetWidth = 0; + } + + public int getRows() { + return rows; + } + + public void setRows(int rows) { + this.rows = rows; + // force relayout + offsetHeight = 0; + offsetWidth = 0; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableGridLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableGridLayout.java new file mode 100644 index 0000000000..f149fea15d --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableGridLayout.java @@ -0,0 +1,166 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.Caption; +import com.itmill.toolkit.terminal.gwt.client.Container; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; +import com.itmill.toolkit.terminal.gwt.client.ui.MarginInfo; + +/** + * Proto level implementation of GridLayout. + * + * All cell's will be equally sized. + * + */ +public class ISizeableGridLayout extends AbsoluteGrid implements Paintable, + Container { + public static final String CLASSNAME = "i-gridlayout"; + private int spacing; + private HashMap paintableToCellMap = new HashMap(); + private ApplicationConnection client; + + public ISizeableGridLayout() { + super(); + setStyleName(CLASSNAME); + } + + protected int getSpacingSize() { + return spacing; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + + if (client.updateComponent(this, uidl, false)) { + return; + } + + if (uidl.hasAttribute("caption")) { + setTitle(uidl.getStringAttribute("caption")); + } + int row = 0, column = 0; + + final ArrayList oldCells = new ArrayList(); + for (final Iterator iterator = getCellIterator(); iterator.hasNext();) { + oldCells.add(iterator.next()); + } + clear(); + + setCols(uidl.getIntAttribute("w")); + setRows(uidl.getIntAttribute("h")); + + handleMargins(uidl); + spacing = uidl.getBooleanAttribute("spacing") ? detectSpacingSize() : 0; + + final int[] alignments = uidl.getIntArrayAttribute("alignments"); + int alignmentIndex = 0; + + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL r = (UIDL) i.next(); + if ("gr".equals(r.getTag())) { + column = 0; + for (final Iterator j = r.getChildIterator(); j.hasNext();) { + final UIDL c = (UIDL) j.next(); + if ("gc".equals(c.getTag())) { + + // Set cell width + int colSpan; + if (c.hasAttribute("w")) { + colSpan = c.getIntAttribute("w"); + } else { + colSpan = 1; + } + + // Set cell height + int rowSpan; + if (c.hasAttribute("h")) { + rowSpan = c.getIntAttribute("h"); + } else { + rowSpan = 1; + } + + final UIDL u = c.getChildUIDL(0); + if (u != null) { + final Paintable child = client.getPaintable(u); + AbsoluteGridCell cell = getCell(column, row); + paintableToCellMap.put(child, cell); + cell.rowSpan = rowSpan; + cell.colSpan = colSpan; + + oldCells.remove(cell); + + cell.setAlignment(alignments[alignmentIndex++]); + + cell.render(); + + cell.setWidget((Widget) child); + + if (!u.getBooleanAttribute("cached")) { + child.updateFromUIDL(u, client); + } + + cell.vAling(); + } + column += colSpan; + } + } + row++; + } + } + + // loop oldWidgetWrappers that where not re-attached and unregister them + for (final Iterator it = oldCells.iterator(); it.hasNext();) { + final AbsoluteGridCell w = (AbsoluteGridCell) it.next(); + client.unregisterPaintable((Paintable) w.getWidget()); + w.removeFromParent(); + paintableToCellMap.remove(w.getWidget()); + } + + } + + protected void handleMargins(UIDL uidl) { + final MarginInfo margins = new MarginInfo(uidl + .getIntAttribute("margins")); + // TODO build CSS detector to make margins configurable through css + marginTop = margins.hasTop() ? 15 : 0; + marginRight = margins.hasRight() ? 15 : 0; + marginBottom = margins.hasBottom() ? 15 : 0; + marginLeft = margins.hasLeft() ? 15 : 0; + } + + private int detectSpacingSize() { + // TODO Auto-generated method stub + return 15; + } + + public boolean hasChildComponent(Widget component) { + if (paintableToCellMap.containsKey(component)) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + // TODO Auto-generated method stub + + } + + public void updateCaption(Paintable component, UIDL uidl) { + AbsoluteGridCell cell = (AbsoluteGridCell) paintableToCellMap + .get(component); + Caption c = cell.getCaption(); + if (c == null) { + c = new Caption(component, client); + cell.setCaption(c); + } + c.updateCaption(uidl); + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableOrderedLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableOrderedLayout.java new file mode 100644 index 0000000000..a1e7cd831a --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/absolutegrid/ISizeableOrderedLayout.java @@ -0,0 +1,191 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.absolutegrid; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.Caption; +import com.itmill.toolkit.terminal.gwt.client.Container; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; +import com.itmill.toolkit.terminal.gwt.client.ui.MarginInfo; + +/** + * Proto level implementation of GridLayout. + * + * All cell's will be equally sized. + * + */ +public class ISizeableOrderedLayout extends AbsoluteGrid implements Paintable, + Container { + public static final String CLASSNAME = "i-orderedlayout"; + private static final int ORIENTETION_HORIZONTAL = 1; + private int spacing; + private HashMap paintableToCellMap = new HashMap(); + private ApplicationConnection client; + private int orientation; + + public ISizeableOrderedLayout() { + super(); + setStyleName(CLASSNAME); + } + + protected int getSpacingSize() { + return spacing; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + + if (client.updateComponent(this, uidl, false)) { + return; + } + + orientation = uidl.getIntAttribute("orientation"); + + if (uidl.hasAttribute("caption")) { + setTitle(uidl.getStringAttribute("caption")); + } + + handleMargins(uidl); + spacing = uidl.getBooleanAttribute("spacing") ? detectSpacingSize() : 0; + + final int[] alignments = uidl.getIntArrayAttribute("alignments"); + int alignmentIndex = 0; + + // Update contained components + + final ArrayList uidlWidgets = new ArrayList(); + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL uidlForChild = (UIDL) it.next(); + final Paintable child = client.getPaintable(uidlForChild); + uidlWidgets.add(child); + } + + if (orientation == ORIENTETION_HORIZONTAL) { + setCols(uidlWidgets.size()); + setRows(1); + } else { + setCols(1); + setRows(uidlWidgets.size()); + } + + final ArrayList oldWidgets = getPaintables(); + + final HashMap oldCaptions = new HashMap(); + + final Iterator newIt = uidlWidgets.iterator(); + final Iterator newUidl = uidl.getChildIterator(); + + int row = 0, column = 0; + while (newIt.hasNext()) { + final Widget child = (Widget) newIt.next(); + final UIDL childUidl = (UIDL) newUidl.next(); + + AbsoluteGridCell cell = getCell(column, row); + + Widget oldChild = cell.getWidget(); + if (oldChild != null) { + if (oldChild != child) { + oldCaptions.put(oldChild, cell.getCaption()); + cell.clear(); + cell.setWidget(child); + paintableToCellMap.remove(oldChild); + Caption newCaption = (Caption) oldCaptions.get(child); + if (newCaption == null) { + newCaption = new Caption((Paintable) child, client); + } + cell.setCaption(newCaption); + } + } else { + cell.setWidget(child); + } + + paintableToCellMap.put(child, cell); + + cell.setAlignment(alignments[alignmentIndex++]); + + cell.render(); + + ((Paintable) child).updateFromUIDL(childUidl, client); + + cell.vAling(); + + if (orientation == ORIENTETION_HORIZONTAL) { + column++; + } else { + row++; + } + oldWidgets.remove(child); + } + // remove possibly remaining old Paintable object which were not updated + Iterator oldIt = oldWidgets.iterator(); + while (oldIt.hasNext()) { + final Paintable p = (Paintable) oldIt.next(); + if (!uidlWidgets.contains(p)) { + removePaintable(p); + } + } + } + + private void removePaintable(Paintable oldChild) { + AbsoluteGridCell cell = (AbsoluteGridCell) paintableToCellMap + .get(oldChild); + if (cell != null) { + cell.clear(); + } + client.unregisterPaintable(oldChild); + } + + private ArrayList getPaintables() { + ArrayList paintables = new ArrayList(); + Iterator it = paintableToCellMap.keySet().iterator(); + while (it.hasNext()) { + Paintable p = (Paintable) it.next(); + paintables.add(p); + } + return paintables; + } + + protected void handleMargins(UIDL uidl) { + final MarginInfo margins = new MarginInfo(uidl + .getIntAttribute("margins")); + // TODO build CSS detector to make margins configurable through css + marginTop = margins.hasTop() ? 15 : 0; + marginRight = margins.hasRight() ? 15 : 0; + marginBottom = margins.hasBottom() ? 15 : 0; + marginLeft = margins.hasLeft() ? 15 : 0; + } + + private int detectSpacingSize() { + // TODO Auto-generated method stub + return 15; + } + + public boolean hasChildComponent(Widget component) { + if (paintableToCellMap.containsKey(component)) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + // TODO Auto-generated method stub + + } + + public void updateCaption(Paintable component, UIDL uidl) { + AbsoluteGridCell cell = (AbsoluteGridCell) paintableToCellMap + .get(component); + Caption c = cell.getCaption(); + if (c == null) { + c = new Caption(component, client); + cell.setCaption(c); + } + c.updateCaption(uidl); + } + +}