Browse Source

Initial MeasureManager and working Horizontal/VerticalLayout (#8313)

tags/7.0.0.alpha2
Leif Åstrand 12 years ago
parent
commit
5e14034fcc

+ 47
- 26
src/com/vaadin/terminal/gwt/client/ApplicationConnection.java View File

@@ -12,11 +12,14 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
@@ -34,7 +37,6 @@ import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;
import com.vaadin.terminal.gwt.client.ui.Field;
import com.vaadin.terminal.gwt.client.ui.VAbstractPaintableWidget;
import com.vaadin.terminal.gwt.client.ui.VContextMenu;
@@ -1000,6 +1002,8 @@ public class ApplicationConnection {
ArrayList<VPaintableWidget> updatedVPaintableWidgets = new ArrayList<VPaintableWidget>();
componentCaptionSizeChanges.clear();

Duration updateDuration = new Duration();

int length = changes.length();
for (int i = 0; i < length; i++) {
try {
@@ -1045,25 +1049,10 @@ public class ApplicationConnection {
json.getValueMap("dd"));
}

// Check which widgets' size has been updated
Set<Widget> sizeUpdatedWidgets = new HashSet<Widget>();

sizeUpdatedWidgets.addAll(componentCaptionSizeChanges);

for (VPaintableWidget paintable : updatedVPaintableWidgets) {
Widget widget = paintable.getWidgetForPaintable();
Size oldSize = paintableMap.getOffsetSize(paintable);
Size newSize = new Size(widget.getOffsetWidth(),
widget.getOffsetHeight());

if (oldSize == null || !oldSize.equals(newSize)) {
sizeUpdatedWidgets.add(widget);
paintableMap.setOffsetSize(paintable, newSize);
}

}
VConsole.log("updateFromUidl: "
+ updateDuration.elapsedMillis() + " ms");

Util.componentSizeUpdated(sizeUpdatedWidgets);
doLayout(false);

if (meta != null) {
if (meta.containsKey("appError")) {
@@ -1806,6 +1795,17 @@ public class ApplicationConnection {

Widget component = paintableMap.getWidget(paintable);

Style style = component.getElement().getStyle();

// Dirty if either dimension changed between relative and non-relative
if (w.endsWith("%") != style.getWidth().endsWith("%")
|| h.endsWith("%") != style.getHeight().endsWith("%")) {
MeasureManager.MeasuredSize measuredSize = getMeasuredSize(paintable);
if (measuredSize != null) {
measuredSize.setDirty(true);
}
}

// Set defined sizes
component.setHeight(h);
component.setWidth(w);
@@ -1838,13 +1838,11 @@ public class ApplicationConnection {
* development. Published to JavaScript.
*/
public void forceLayout() {
Set<Widget> set = new HashSet<Widget>();
for (VPaintable paintable : paintableMap.getPaintables()) {
if (paintable instanceof VPaintableWidget) {
set.add(((VPaintableWidget) paintable).getWidgetForPaintable());
}
}
Util.componentSizeUpdated(set);
Duration duration = new Duration();

doLayout(false);

VConsole.log("forceLayout in " + duration.elapsedMillis() + " ms");
}

private void internalRunDescendentsLayout(HasWidgets container) {
@@ -2249,4 +2247,27 @@ public class ApplicationConnection {
eventIdentifier);
}

private boolean layoutScheduled = false;
private ScheduledCommand layoutCommand = new ScheduledCommand() {
public void execute() {
layoutScheduled = false;

MeasureManager.get().doLayout(ApplicationConnection.this);
}
};

public void doLayout(boolean lazy) {
if (!lazy) {
layoutCommand.execute();
} else if (!layoutScheduled) {
layoutScheduled = true;
Scheduler.get().scheduleDeferred(layoutCommand);
}
}

public MeasureManager.MeasuredSize getMeasuredSize(
VPaintableWidget paintable) {
return paintableMap.getMeasuredSize(paintable);
}

}

+ 6
- 0
src/com/vaadin/terminal/gwt/client/ComponentDetail.java View File

@@ -6,11 +6,13 @@ package com.vaadin.terminal.gwt.client;
import java.util.HashMap;

import com.google.gwt.core.client.JsArrayString;
import com.vaadin.terminal.gwt.client.MeasureManager.MeasuredSize;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;

class ComponentDetail {

private TooltipInfo tooltipInfo = new TooltipInfo();
private MeasureManager.MeasuredSize measuredSize = new MeasureManager.MeasuredSize();

public ComponentDetail() {

@@ -108,4 +110,8 @@ class ComponentDetail {
}
return false;
}

public MeasureManager.MeasuredSize getMeasuredSize() {
return measuredSize;
}
}

+ 40
- 0
src/com/vaadin/terminal/gwt/client/FastStringSet.java View File

@@ -0,0 +1,40 @@
package com.vaadin.terminal.gwt.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;

public final class FastStringSet extends JavaScriptObject {
protected FastStringSet() {
// JSO constructor
}

public native boolean contains(String string)
/*-{
return this.hasOwnProperty(string);
}-*/;

public native void add(String string)
/*-{
this[string] = true;
}-*/;

public native void addAll(JsArrayString array)
/*-{
for(var i = 0; i < array.length; i++) {
this[array[i]] = true;
}
}-*/;

public native JsArrayString dump()
/*-{
var array = [];
for(var string in this) {
array.push(string);
}
return array;
}-*/;

public static FastStringSet create() {
return JavaScriptObject.createObject().cast();
}
}

+ 280
- 0
src/com/vaadin/terminal/gwt/client/MeasureManager.java View File

@@ -0,0 +1,280 @@
package com.vaadin.terminal.gwt.client;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;

import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ui.VMeasuringOrderedLayout;

public class MeasureManager {

public static final class MeasuredSize {
private int width = -1;
private int height = -1;
private boolean isDirty = true;
private int captionWidth = 0;
private int captionHeight = 0;

public int getHeight() {
return height;
}

public int getWidth() {
return width;
}

public void setHeight(int height) {
if (this.height != height) {
isDirty = true;
}
this.height = height;
}

public void setWidth(int width) {
if (width != this.width) {
isDirty = true;
}
this.width = width;
}

public boolean isDirty() {
return isDirty;
}

public void setDirty(boolean isDirty) {
this.isDirty = isDirty;
}

public void setCaptionHeight(int captionHeight) {
if (captionHeight != this.captionHeight) {
isDirty = true;
}
this.captionHeight = captionHeight;
}

public void setCaptionWidth(int captionWidth) {
if (captionWidth != this.captionWidth) {
isDirty = true;
}
this.captionWidth = captionWidth;
}

public int getCaptionHeight() {
return captionHeight;
}

public int getCaptionWidth() {
return captionWidth;
}
}

private static MeasureManager instance = new MeasureManager();

public static Collection<VPaintableWidget> getChildren(
VPaintableWidget paintable, ApplicationConnection client) {
if (!(paintable instanceof Container)) {
return Collections.emptySet();
}
Widget widget = paintable.getWidgetForPaintable();
Collection<VPaintableWidget> children = new ArrayList<VPaintableWidget>();

addDescendantPaintables(widget, children, client);

return children;
}

private static void addDescendantPaintables(Widget widget,
Collection<VPaintableWidget> paintables,
ApplicationConnection client) {
if (widget instanceof HasWidgets) {
VPaintableMap paintableMap = client.getPaintableMap();
for (Widget child : (HasWidgets) widget) {
VPaintableWidget paintable = paintableMap.getPaintable(child);
if (paintable != null) {
paintables.add(paintable);
} else {
addDescendantPaintables(child, paintables, client);
}
}
}
}

private static VPaintableWidget getParentPaintable(
VPaintableWidget paintable, VPaintableMap paintableMap) {
Widget widget = paintable.getWidgetForPaintable();
while (true) {
widget = widget.getParent();
if (widget == null) {
return null;
}
VPaintableWidget parentPaintable = paintableMap
.getPaintable(widget);
if (parentPaintable != null) {
return parentPaintable;
}
// Else continue with the parent
}
}

public void doLayout(ApplicationConnection client) {
VPaintableMap paintableMap = client.getPaintableMap();
VPaintableWidget[] paintableWidgets = paintableMap
.getRegisteredPaintableWidgets();

int passes = 0;
long start = System.currentTimeMillis();
while (true) {
long passStart = System.currentTimeMillis();
passes++;
long measureStart = System.currentTimeMillis();
FastStringSet changedSet = findChangedWidgets(paintableWidgets,
paintableMap);
JsArrayString changed = changedSet.dump();
long measureEnd = System.currentTimeMillis();

VConsole.log("Measure in " + (measureEnd - measureStart) + " ms");

if (changed.length() == 0) {
VConsole.log("No more changes in pass " + passes);
break;
}

if (passes > 100) {
VConsole.log("Aborting layout");
break;
}

FastStringSet affectedContainers = FastStringSet.create();
for (int i = 0; i < changed.length(); i++) {
VPaintableWidget paintable = (VPaintableWidget) paintableMap
.getPaintable(changed.get(i));
VPaintableWidget parentPaintable = getParentPaintable(
paintable, paintableMap);
if (parentPaintable instanceof Container) {
affectedContainers
.add(paintableMap.getPid(parentPaintable));
}
}

long layoutStart = System.currentTimeMillis();
for (int i = 0; i < changed.length(); i++) {
String pid = changed.get(i);
VPaintableWidget paintable = (VPaintableWidget) paintableMap
.getPaintable(pid);
if (!affectedContainers.contains(pid)) {
Widget widget = paintable.getWidgetForPaintable();
if (widget instanceof RequiresResize) {
// TODO Do nothing here if parent instanceof
// ProvidesRepaint?
((RequiresResize) widget).onResize();
}
}
}

JsArrayString affectedPids = affectedContainers.dump();
for (int i = 0; i < affectedPids.length(); i++) {
// Find all changed children
String containerPid = affectedPids.get(i);
VPaintableWidget container = (VPaintableWidget) paintableMap
.getPaintable(containerPid);
Collection<VPaintableWidget> children = getChildren(container,
client);
HashSet<Widget> changedChildren = new HashSet<Widget>();

for (VPaintableWidget child : children) {
if (changedSet.contains(paintableMap.getPid(child))) {
changedChildren.add(child.getWidgetForPaintable());
}
}

((Container) container).requestLayout(changedChildren);
}

long layoutEnd = System.currentTimeMillis();
VConsole.log(affectedPids.length()
+ " requestLayout invocations in "
+ (layoutEnd - layoutStart) + "ms");

long passEnd = System.currentTimeMillis();
StringBuilder b = new StringBuilder();
b.append(changed.length());
b.append(" changed widgets in pass ");
b.append(passes);
b.append(" in ");
b.append((passEnd - passStart));
b.append(" ms: ");
if (changed.length() < 10) {
for (int i = 0; i < changed.length(); i++) {
if (i != 0) {
b.append(", ");
}
b.append(changed.get(i));
}
}
VConsole.log(b.toString());
}
long end = System.currentTimeMillis();
VConsole.log("Total layout time: " + (end - start) + "ms");
}

private FastStringSet findChangedWidgets(
VPaintableWidget[] paintableWidgets, VPaintableMap paintableMap) {

FastStringSet changed = FastStringSet.create();
for (VPaintableWidget paintableWidget : paintableWidgets) {
Widget widget = paintableWidget.getWidgetForPaintable();
if (paintableWidget instanceof VMeasuringOrderedLayout) {
VMeasuringOrderedLayout hasCaptions = (VMeasuringOrderedLayout) paintableWidget;
Collection<VCaption> childCaptions = hasCaptions
.getChildCaptions();
for (VCaption vCaption : childCaptions) {
VPaintableWidget captionOwner = vCaption.getOwner();
MeasureManager.MeasuredSize measuredSize = paintableMap
.getMeasuredSize(captionOwner);

int captionHeight = vCaption.getOffsetHeight();
int captionWidth = vCaption.getOffsetWidth();
if (captionHeight == 0 || captionWidth == 0) {
// Empty caption is probably detached
if (!Util.isAttachedAndDisplayed(vCaption)) {
// Ignore if it is detached
continue;
}
}
measuredSize.setCaptionHeight(captionHeight);
measuredSize.setCaptionWidth(captionWidth);
if (measuredSize.isDirty()) {
changed.add(paintableMap.getPid(captionOwner));
measuredSize.setDirty(false);
}
}
}

MeasureManager.MeasuredSize measuredSize = paintableMap
.getMeasuredSize(paintableWidget);

measuredSize.setWidth(widget.getOffsetWidth());
measuredSize.setHeight(widget.getOffsetHeight());

if (measuredSize.isDirty()) {
changed.add(paintableMap.getPid(paintableWidget));
measuredSize.setDirty(false);
}
}

return changed;
}

private MeasureManager() {
// Singleton constructor
}

public static MeasureManager get() {
return instance;
}
}

+ 25
- 85
src/com/vaadin/terminal/gwt/client/Util.java View File

@@ -5,11 +5,7 @@
package com.vaadin.terminal.gwt.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.List;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -24,7 +20,6 @@ import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
@@ -65,30 +60,6 @@ public class Util {
return el;
}-*/;

private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400;
private static Set<Widget> latelyChangedWidgets = new HashSet<Widget>();

private static Timer lazySizeChangeTimer = new Timer() {
private boolean lazySizeChangeTimerScheduled = false;

@Override
public void run() {
componentSizeUpdated(latelyChangedWidgets);
latelyChangedWidgets.clear();
lazySizeChangeTimerScheduled = false;
}

@Override
public void schedule(int delayMillis) {
if (lazySizeChangeTimerScheduled) {
cancel();
} else {
lazySizeChangeTimerScheduled = true;
}
super.schedule(delayMillis);
}
};

/**
* This helper method can be called if components size have been changed
* outside rendering phase. It notifies components parent about the size
@@ -106,58 +77,40 @@ public class Util {
* run componentSizeUpdated lazyly
*/
public static void notifyParentOfSizeChange(Widget widget, boolean lazy) {
if (lazy) {
latelyChangedWidgets.add(widget);
lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT);
} else {
Set<Widget> widgets = new HashSet<Widget>();
widgets.add(widget);
Util.componentSizeUpdated(widgets);
ApplicationConnection applicationConnection = findApplicationConnectionFor(widget);
if (applicationConnection != null) {
applicationConnection.doLayout(lazy);
}
}

/**
* Called when the size of one or more widgets have changed during
* rendering. Finds parent container and notifies them of the size change.
*
* @param paintables
*/
public static void componentSizeUpdated(Set<Widget> widgets) {
if (widgets.isEmpty()) {
return;
}
private static boolean findAppConnectionWarningDisplayed = false;

Map<Container, Set<Widget>> childWidgets = new HashMap<Container, Set<Widget>>();
private static ApplicationConnection findApplicationConnectionFor(
Widget widget) {
if (!findAppConnectionWarningDisplayed) {
findAppConnectionWarningDisplayed = true;
VConsole.log("Warning: Using Util.findApplicationConnectionFor which should be eliminated once there is a better way to find the ApplicationConnection for a Paintable");
}

for (Widget widget : widgets) {
if (!widget.isAttached()) {
List<ApplicationConnection> runningApplications = ApplicationConfiguration
.getRunningApplications();
for (ApplicationConnection applicationConnection : runningApplications) {
VPaintableMap paintableMap = applicationConnection
.getPaintableMap();
VPaintableWidget paintable = paintableMap.getPaintable(widget);
if (paintable == null) {
continue;
}

// ApplicationConnection.getConsole().log(
// "Widget " + Util.getSimpleName(widget) + " size updated");
Widget parent = widget.getParent();
while (parent != null && !(parent instanceof Container)) {
parent = parent.getParent();
}
if (parent != null) {
Set<Widget> set = childWidgets.get(parent);
if (set == null) {
set = new HashSet<Widget>();
childWidgets.put((Container) parent, set);
String pid = paintableMap.getPid(paintable);
if (pid != null) {
VPaintable otherPaintable = paintableMap.getPaintable(pid);
if (otherPaintable == paintable) {
return applicationConnection;
}
set.add(widget);
}
}

Set<Widget> parentChanges = new HashSet<Widget>();
for (Container parent : childWidgets.keySet()) {
if (!parent.requestLayout(childWidgets.get(parent))) {
parentChanges.add(parent.getWidgetForPaintable());
}
}

componentSizeUpdated(parentChanges);
return null;
}

public static float parseRelativeSize(String size) {
@@ -614,20 +567,7 @@ public class Util {

public static void updateRelativeChildrenAndSendSizeUpdateEvent(
ApplicationConnection client, HasWidgets container, Widget widget) {
/*
* Relative sized children must be updated first so the component has
* the correct outer dimensions when signaling a size change to the
* parent.
*/
Iterator<Widget> childIterator = container.iterator();
while (childIterator.hasNext()) {
Widget w = childIterator.next();
client.handleComponentRelativeSize(w);
}

HashSet<Widget> widgets = new HashSet<Widget>();
widgets.add(widget);
Util.componentSizeUpdated(widgets);
notifyParentOfSizeChange(widget, false);
}

public static native int getRequiredWidth(

+ 30
- 0
src/com/vaadin/terminal/gwt/client/VPaintableMap.java View File

@@ -3,6 +3,7 @@
*/
package com.vaadin.terminal.gwt.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -16,6 +17,7 @@ import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.Paintable;
import com.vaadin.terminal.gwt.client.MeasureManager.MeasuredSize;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;
public class VPaintableMap {
@@ -208,6 +210,21 @@ public class VPaintableMap {
}
public VPaintableWidget[] getRegisteredPaintableWidgets() {
ArrayList<VPaintableWidget> result = new ArrayList<VPaintableWidget>();
for (VPaintable paintable : getPaintables()) {
if (paintable instanceof VPaintableWidget) {
VPaintableWidget paintableWidget = (VPaintableWidget) paintable;
if (!unregistryBag.contains(getPid(paintable))) {
result.add(paintableWidget);
}
}
}
return result.toArray(new VPaintableWidget[result.size()]);
}
void purgeUnregistryBag(boolean unregisterPaintables) {
if (unregisterPaintables) {
for (String pid : unregistryBag) {
@@ -375,4 +392,17 @@ public class VPaintableMap {
return getPid(w) != null;
}
/**
* FIXME: Should not be here
*/
@Deprecated
public MeasuredSize getMeasuredSize(VPaintableWidget paintable) {
ComponentDetail componentDetail = getComponentDetail(paintable);
if (componentDetail == null) {
return null;
}
return componentDetail.getMeasuredSize();
}
}

+ 2
- 2
src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java View File

@@ -3,12 +3,12 @@
*/
package com.vaadin.terminal.gwt.client.ui;

public class VHorizontalLayout extends VOrderedLayout {
public class VHorizontalLayout extends VMeasuringOrderedLayout {

public static final String CLASSNAME = "v-horizontallayout";

public VHorizontalLayout() {
super(CLASSNAME, ORIENTATION_HORIZONTAL);
super(CLASSNAME, false);
}

}

+ 539
- 0
src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayout.java View File

@@ -0,0 +1,539 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
package com.vaadin.terminal.gwt.client.ui;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.RequiresResize;
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.MeasureManager;
import com.vaadin.terminal.gwt.client.RenderSpace;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VCaption;
import com.vaadin.terminal.gwt.client.VPaintableMap;
import com.vaadin.terminal.gwt.client.VPaintableWidget;
import com.vaadin.terminal.gwt.client.ValueMap;

public class VMeasuringOrderedLayout extends ComplexPanel implements Container,
RequiresResize {

public static final String CLASSNAME = "v-orderedlayout";

private static final int MARGIN_SIZE = 20;

private final boolean isVertical;

private ApplicationConnection client;

private String id;

private RenderSpace space;

private ValueMap expandRatios;

private ValueMap alignments;

private Map<VPaintableWidget, VCaption> captions = new HashMap<VPaintableWidget, VCaption>();

private boolean spacing;

private VMarginInfo activeMarginsInfo;

protected VMeasuringOrderedLayout(String className, boolean isVertical) {
DivElement element = Document.get().createDivElement();
setElement(element);
// TODO These should actually be defined in css
Style style = element.getStyle();
style.setOverflow(Overflow.HIDDEN);
style.setPosition(Position.RELATIVE);

setStyleName(className);
this.isVertical = isVertical;
}

public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
this.client = client;
id = uidl.getId();
if (client.updateComponent(this, uidl, true)) {
return;
}

long start = System.currentTimeMillis();
// long childTime = 0;

HashSet<Widget> previousChildren = new HashSet<Widget>();
for (Widget child : this) {
if (!(child instanceof VCaption)) {
previousChildren.add(child);
}
}
// TODO Support reordering elements!
for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) {
final UIDL childUIDL = (UIDL) it.next();
final VPaintableWidget child = client.getPaintable(childUIDL);
Widget widget = child.getWidgetForPaintable();

if (widget.getParent() != this) {
DivElement wrapper = Document.get().createDivElement();
wrapper.getStyle().setPosition(Position.ABSOLUTE);
getElement().appendChild(wrapper);
add(widget, wrapper);
}

if (!childUIDL.getBooleanAttribute("cached")) {
child.updateFromUIDL(childUIDL, client);
client.getMeasuredSize(child).setDirty(true);
}
// TODO Update alignments and expand ratios

previousChildren.remove(widget);
}

for (Widget widget : previousChildren) {
Element wrapper = getWrapper(widget);
VCaption caption = captions.remove(widget);
if (caption != null) {
remove(caption);
}
remove(widget);
// Remove the wrapper
getElement().removeChild(wrapper);

client.unregisterPaintable((VPaintableWidget) widget);
}

int bitMask = uidl.getIntAttribute("margins");
if (activeMarginsInfo == null
|| activeMarginsInfo.getBitMask() != bitMask) {
activeMarginsInfo = new VMarginInfo(bitMask);
}

spacing = uidl.getBooleanAttribute("spacing");
expandRatios = uidl.getMapAttribute("expandRatios");
alignments = uidl.getMapAttribute("alignments");
client.getMeasuredSize(this).setDirty(true);
}

private static Element getWrapper(Widget widget) {
return widget.getElement().getParentElement();
}

private void add(Widget widget, DivElement wrapper) {
add(widget, (com.google.gwt.user.client.Element) wrapper.cast());
}

public void onResize() {
requestLayout(Collections.<Widget> emptySet());
}

private static boolean isUndefinedInDirection(Widget widget,
boolean isVertical) {
String dimension = getDimensionInDirection(widget, isVertical);
return dimension == null || dimension.length() == 0;
}

private static boolean isRelativeInDirection(Widget widget,
boolean isVertical) {
String dimension = getDimensionInDirection(widget, isVertical);
return dimension != null && dimension.endsWith("%");
}

private static String getDimensionInDirection(Widget widget,
boolean vertical) {
com.google.gwt.user.client.Element element = widget.getElement();
Style style = element.getStyle();
if (vertical) {
return style.getHeight();
} else {
return style.getWidth();
}
}

public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
throw new UnsupportedOperationException();
}

public boolean hasChildComponent(Widget component) {
return component.getParent() == this;
}

public void updateCaption(VPaintableWidget component, UIDL uidl) {
if (VCaption.isNeeded(uidl)) {
VCaption caption = captions.get(component);
if (caption == null) {
caption = new VCaption(component, client);

Widget widget = (Widget) component;
Element wrapper = getWrapper(widget);

// Logical attach.
getChildren().add(caption);

// Physical attach.
DOM.insertBefore(
(com.google.gwt.user.client.Element) wrapper.cast(),
caption.getElement(), widget.getElement());

// Adopt.
adopt(caption);
captions.put(component, caption);
}
caption.updateCaption(uidl);
} else {
VCaption removedCaption = captions.remove(component);
if (removedCaption != null) {
remove(removedCaption);
MeasureManager.MeasuredSize measuredSize = client
.getMeasuredSize(component);
measuredSize.setCaptionHeight(0);
measuredSize.setCaptionWidth(0);
}
}

}

private void layoutPrimaryDirection() {
Collection<VPaintableWidget> children = MeasureManager.getChildren(
this, client);

// First pass - get total expand ratio and allocated size
int totalAllocated = 0;
double totalExpand = 0;
for (VPaintableWidget child : children) {
Widget widget = child.getWidgetForPaintable();

totalExpand += getExpandRatio(child);

int captionAllocation;
if (isVertical) {
captionAllocation = getCaptionHeight(child);
getWrapper(widget).getStyle().setPaddingTop(captionAllocation,
Unit.PX);
} else {
captionAllocation = 0;
}

if (!isRelativeInDirection(widget, isVertical)) {
totalAllocated += getMeasuredInDirection(child, isVertical)
+ captionAllocation;
}
}
int startMargin = getStartMarginInDirection(isVertical);
int totalMargins = startMargin + getEndMarginInDirection(isVertical);

totalAllocated += totalMargins
+ (getSpacingInDirection(isVertical) * (children.size() - 1));

Style ownStyle = getElement().getStyle();
double ownSize;
if (isUndefinedInDirection(this, isVertical)) {
ownSize = totalAllocated;
ownStyle.setProperty(getMinPropertyName(isVertical),
totalAllocated, Unit.PX);
} else {
ownSize = getMeasuredInDirection(this, isVertical);
ownStyle.clearProperty(getMinPropertyName(isVertical));
}

double unallocatedSpace = Math.max(0, ownSize - totalAllocated);

double currentLocation = startMargin;
for (VPaintableWidget child : children) {
Widget widget = child.getWidgetForPaintable();
Element wrapper = getWrapper(widget);
Style wrapperStyle = wrapper.getStyle();

double childExpandRatio;
if (totalExpand == 0) {
childExpandRatio = 1d / children.size();
} else {
childExpandRatio = getExpandRatio(child) / totalExpand;
}

double extraPixels = unallocatedSpace * childExpandRatio;

boolean relative = isRelativeInDirection(widget, isVertical);

double size = getMeasuredInDirection(child, isVertical);
int captionHeight = getCaptionHeight(child);

if (isVertical) {
size += captionHeight;
} else if (!relative) {
size = Math.max(size, getCaptionWidth(child));
}

double allocatedSpace = extraPixels;
if (!relative) {
allocatedSpace += size;
}

int alignment = getAlignmentInDirection(getAlignment(child),
isVertical);

if (relative) {
double captionReservation = isVertical ? captionHeight : 0;
wrapperStyle.setProperty(getSizeProperty(isVertical),
allocatedSpace - captionReservation, Unit.PX);
} else {
wrapperStyle.clearProperty(getSizeProperty(isVertical));
}

double startPosition = currentLocation;
if (alignment == 0) {
// Centered
startPosition += (allocatedSpace - size) / 2;
} else if (alignment == 1) {
// Right or bottom
startPosition += allocatedSpace - size;
}

wrapperStyle.setProperty(getStartProperty(isVertical),
startPosition, Unit.PX);

currentLocation += allocatedSpace
+ getSpacingInDirection(isVertical);
}
}

private int getEndMarginInDirection(boolean isVertical) {
if (isVertical) {
return activeMarginsInfo.hasBottom() ? MARGIN_SIZE : 0;
} else {
return activeMarginsInfo.hasRight() ? MARGIN_SIZE : 0;
}
}

private int getStartMarginInDirection(boolean isVertical) {
if (isVertical) {
return activeMarginsInfo.hasTop() ? MARGIN_SIZE : 0;
} else {
return activeMarginsInfo.hasLeft() ? MARGIN_SIZE : 0;
}
}

private void layoutSecondaryDirection() {
Collection<VPaintableWidget> children = MeasureManager.getChildren(
this, client);

int maxSize = 0;
for (VPaintableWidget child : children) {
Widget widget = child.getWidgetForPaintable();

int captionAllocation;
if (!isVertical) {
captionAllocation = getCaptionHeight(child);
getWrapper(widget).getStyle().setPaddingTop(captionAllocation,
Unit.PX);
} else {
captionAllocation = 0;
}

if (!isRelativeInDirection(widget, !isVertical)) {
int childSize = getMeasuredInDirection(child, !isVertical)
+ captionAllocation;
maxSize = Math.max(maxSize, childSize);
}
}

int startMargin = getStartMarginInDirection(!isVertical);
int totalMargins = startMargin + getEndMarginInDirection(!isVertical);

double availableSpace;
Style ownStyle = getElement().getStyle();

if (isUndefinedInDirection(this, !isVertical)) {
ownStyle.setProperty(getMinPropertyName(!isVertical), maxSize
+ totalMargins, Unit.PX);
availableSpace = maxSize;
} else {
ownStyle.clearProperty(getMinPropertyName(!isVertical));
availableSpace = getMeasuredInDirection(this, !isVertical)
- totalMargins;
}

for (VPaintableWidget child : children) {
Widget widget = child.getWidgetForPaintable();
Element wrapper = getWrapper(widget);
Style wrapperStyle = wrapper.getStyle();

boolean relative = isRelativeInDirection(widget, !isVertical);

int captionHeight = getCaptionHeight(child);

double allocatedSize = getMeasuredInDirection(child, !isVertical);
if (!isVertical) {
allocatedSize += captionHeight;
} else if (!relative) {
allocatedSize = Math.max(allocatedSize, getCaptionWidth(child));
}

int alignment = getAlignmentInDirection(getAlignment(child),
!isVertical);

double startPosition = startMargin;
if (alignment == 0) {
startPosition += (availableSpace - allocatedSize) / 2;
// Centered
} else if (alignment == 1) {
// Right or bottom
startPosition += (availableSpace - allocatedSize);
}

wrapperStyle.setProperty(getStartProperty(!isVertical),
startPosition, Unit.PX);

if (relative) {
double captionReservation = !isVertical ? captionHeight : 0;
wrapperStyle.setProperty(getSizeProperty(!isVertical),
availableSpace - captionReservation, Unit.PX);
} else {
wrapperStyle.clearProperty(getSizeProperty(!isVertical));
}
}
}

public boolean requestLayout(Set<Widget> changed) {
layoutPrimaryDirection();
layoutSecondaryDirection();

// Doesn't matter right now...
return true;
}

private static int getAlignmentInDirection(AlignmentInfo alignment,
boolean isVertical) {
if (alignment == null) {
return -1;
}
if (isVertical) {
if (alignment.isTop()) {
return -1;
} else if (alignment.isBottom()) {
return 1;
} else {
return 0;
}
} else {
if (alignment.isLeft()) {
return -1;
} else if (alignment.isRight()) {
return 1;
} else {
return 0;
}
}
}

private int getSpacingInDirection(boolean isVertical) {
if (spacing) {
return 20;
} else {
return 0;
}
}

private int getCaptionWidth(VPaintableWidget child) {
MeasureManager.MeasuredSize measuredSize = client
.getMeasuredSize(child);
return measuredSize.getCaptionWidth();
}

private int getCaptionHeight(VPaintableWidget child) {
MeasureManager.MeasuredSize measuredSize = client
.getMeasuredSize(child);
int captionHeight = measuredSize.getCaptionHeight();

VCaption caption = captions.get(child);
if (caption != null) {
caption.getElement().getStyle()
.setMarginTop(-captionHeight, Unit.PX);
}
return captionHeight;
}

private AlignmentInfo getAlignment(VPaintableWidget child) {
String pid = VPaintableMap.get(client).getPid(child);
if (alignments.containsKey(pid)) {
return new AlignmentInfo(alignments.getInt(pid));
} else {
return AlignmentInfo.TOP_LEFT;
}
}

private double getExpandRatio(VPaintableWidget child) {
String pid = VPaintableMap.get(client).getPid(child);
if (expandRatios.containsKey(pid)) {
return expandRatios.getRawNumber(pid);
} else {
return 0;
}
}

private static String getSizeProperty(boolean isVertical) {
return isVertical ? "height" : "width";
}

private static String getStartProperty(boolean isVertical) {
return isVertical ? "top" : "left";
}

private static String getMinPropertyName(boolean isVertical) {
return isVertical ? "minHeight" : "minWidth";
}

private int getMeasuredInDirection(VPaintableWidget paintable,
boolean isVertical) {
MeasureManager.MeasuredSize measuredSize = client
.getMeasuredSize(paintable);
if (isVertical) {
return measuredSize.getHeight();
} else {
return measuredSize.getWidth();
}
}

public Collection<VCaption> getChildCaptions() {
return captions.values();
}

public RenderSpace getAllocatedSpace(Widget child) {
// Concept borrowed from CSS layout
if (space == null) {
space = new RenderSpace(-1, -1) {
@Override
public int getWidth() {
return getOffsetWidth();
}

@Override
public int getHeight() {
return getOffsetHeight();
}
};
}
return space;
}

public Widget getWidgetForPaintable() {
return this;
}
}

+ 0
- 976
src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java View File

@@ -1,976 +0,0 @@
/*
@VaadinApache2LicenseForJavaFiles@
*/
package com.vaadin.terminal.gwt.client.ui;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.DomEvent.Type;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
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.BrowserInfo;
import com.vaadin.terminal.gwt.client.EventId;
import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize;
import com.vaadin.terminal.gwt.client.RenderInformation.Size;
import com.vaadin.terminal.gwt.client.RenderSpace;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VPaintableMap;
import com.vaadin.terminal.gwt.client.VPaintableWidget;
import com.vaadin.terminal.gwt.client.ValueMap;
import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout;
import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer;

public class VOrderedLayout extends CellBasedLayout {

public static final String CLASSNAME = "v-orderedlayout";

private int orientation;

// Can be removed once OrderedLayout is removed
private boolean allowOrientationUpdate = false;

/**
* Size of the layout excluding any margins.
*/
private Size activeLayoutSize = new Size(0, 0);

private boolean isRendering = false;

private String width = "";

private boolean sizeHasChangedDuringRendering = false;

private ValueMap expandRatios;

private double expandRatioSum;

private double defaultExpandRatio;

private ValueMap alignments;

private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
this, EventId.LAYOUT_CLICK) {

@Override
protected VPaintableWidget getChildComponent(Element element) {
return getComponent(element);
}

@Override
protected <H extends EventHandler> HandlerRegistration registerHandler(
H handler, Type<H> type) {
return addDomHandler(handler, type);
}
};

public VOrderedLayout() {
this(CLASSNAME, ORIENTATION_VERTICAL);
allowOrientationUpdate = true;
}

protected VOrderedLayout(String className, int orientation) {
setStyleName(className);
this.orientation = orientation;

STYLENAME_SPACING = className + "-spacing";
STYLENAME_MARGIN_TOP = className + "-margin-top";
STYLENAME_MARGIN_RIGHT = className + "-margin-right";
STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom";
STYLENAME_MARGIN_LEFT = className + "-margin-left";
}

@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
isRendering = true;
super.updateFromUIDL(uidl, client);

// Only non-cached, visible UIDL:s can introduce changes
if (uidl.getBooleanAttribute("cached")
|| uidl.getBooleanAttribute("invisible")) {
isRendering = false;
return;
}

clickEventHandler.handleEventHandlerRegistration(client);

if (allowOrientationUpdate) {
handleOrientationUpdate(uidl);
}

// IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL");

ArrayList<Widget> uidlWidgets = new ArrayList<Widget>(
uidl.getChildCount());
ArrayList<ChildComponentContainer> relativeSizeComponents = new ArrayList<ChildComponentContainer>();
ArrayList<UIDL> relativeSizeComponentUIDL = new ArrayList<UIDL>();

int pos = 0;
for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) {
final UIDL childUIDL = (UIDL) it.next();
final VPaintableWidget childPaintable = client
.getPaintable(childUIDL);
Widget widget = childPaintable.getWidgetForPaintable();

// Create container for component
ChildComponentContainer childComponentContainer = getComponentContainer(widget);

if (childComponentContainer == null) {
// This is a new component
childComponentContainer = createChildContainer(childPaintable);
} else {
/*
* The widget may be null if the same paintable has been
* rendered in a different component container while this has
* been invisible. Ensure the childComponentContainer has the
* widget attached. See e.g. #5372
*/
childComponentContainer.setPaintable(childPaintable);
}

addOrMoveChild(childComponentContainer, pos++);

/*
* Components which are to be expanded in the same orientation as
* the layout are rendered later when it is clear how much space
* they can use
*/
if (!Util.isCached(childUIDL)) {
FloatSize relativeSize = Util.parseRelativeSize(childUIDL);
childComponentContainer.setRelativeSize(relativeSize);
}

if (childComponentContainer.isComponentRelativeSized(orientation)) {
relativeSizeComponents.add(childComponentContainer);
relativeSizeComponentUIDL.add(childUIDL);
} else {
if (isDynamicWidth()) {
childComponentContainer.renderChild(childUIDL, client, -1);
} else {
childComponentContainer.renderChild(childUIDL, client,
activeLayoutSize.getWidth());
}
if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) {
// notify cached relative sized component about size
// chance
client.handleComponentRelativeSize(childComponentContainer
.getWidget());
}
}

uidlWidgets.add(widget);

}

// w.mark("Rendering of "
// + (uidlWidgets.size() - relativeSizeComponents.size())
// + " absolute size components done");

/*
* Remove any children after pos. These are the ones that previously
* were in the layout but have now been removed
*/
removeChildrenAfter(pos);

// w.mark("Old children removed");

/* Fetch alignments and expand ratio from UIDL */
updateAlignmentsAndExpandRatios(uidl, uidlWidgets);
// w.mark("Alignments and expand ratios updated");

/* Fetch widget sizes from rendered components */
updateWidgetSizes();
// w.mark("Widget sizes updated");

recalculateLayout();
// w.mark("Layout size calculated (" + activeLayoutSize +
// ") offsetSize: "
// + getOffsetWidth() + "," + getOffsetHeight());

/* Render relative size components */
for (int i = 0; i < relativeSizeComponents.size(); i++) {
ChildComponentContainer childComponentContainer = relativeSizeComponents
.get(i);
UIDL childUIDL = relativeSizeComponentUIDL.get(i);

if (isDynamicWidth()) {
childComponentContainer.renderChild(childUIDL, client, -1);
} else {
childComponentContainer.renderChild(childUIDL, client,
activeLayoutSize.getWidth());
}

if (Util.isCached(childUIDL)) {
/*
* We must update the size of the relative sized component if
* the expand ratio or something else in the layout changes
* which affects the size of a relative sized component
*/
client.handleComponentRelativeSize(childComponentContainer
.getWidget());
}

// childComponentContainer.updateWidgetSize();
}

// w.mark("Rendering of " + (relativeSizeComponents.size())
// + " relative size components done");

/* Fetch widget sizes for relative size components */
for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {

/* Update widget size from DOM */
childComponentContainer.updateWidgetSize();
}

// w.mark("Widget sizes updated");

/*
* Components with relative size in main direction may affect the layout
* size in the other direction
*/
if ((isHorizontal() && isDynamicHeight())
|| (isVertical() && isDynamicWidth())) {
layoutSizeMightHaveChanged();
}
// w.mark("Layout dimensions updated");

/* Update component spacing */
updateContainerMargins();

/*
* Update component sizes for components with relative size in non-main
* direction
*/
if (updateRelativeSizesInNonMainDirection()) {
// Sizes updated - might affect the other dimension so we need to
// recheck the widget sizes and recalculate layout dimensions
updateWidgetSizes();
layoutSizeMightHaveChanged();
}
calculateAlignments();
// w.mark("recalculateComponentSizesAndAlignments done");

setRootSize();

if (BrowserInfo.get().isIE()) {
/*
* This should fix the issue with padding not always taken into
* account for the containers leading to no spacing between
* elements.
*/
root.getStyle().setProperty("zoom", "1");
}

// w.mark("runDescendentsLayout done");
isRendering = false;
sizeHasChangedDuringRendering = false;
}

private void layoutSizeMightHaveChanged() {
Size oldSize = new Size(activeLayoutSize.getWidth(),
activeLayoutSize.getHeight());
calculateLayoutDimensions();

/*
* If layout dimension changes we must also update container sizes
*/
if (!oldSize.equals(activeLayoutSize)) {
calculateContainerSize();
}
}

private void updateWidgetSizes() {
for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {

/*
* Update widget size from DOM
*/
childComponentContainer.updateWidgetSize();
}
}

private void recalculateLayout() {

/* Calculate space for relative size components */
int spaceForExpansion = calculateLayoutDimensions();

if (!widgetToComponentContainer.isEmpty()) {
/* Divide expansion space between component containers */
expandComponentContainers(spaceForExpansion);

/* Update container sizes */
calculateContainerSize();
}

}

private void expandComponentContainers(int spaceForExpansion) {
int remaining = spaceForExpansion;
for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {
remaining -= childComponentContainer.expand(orientation,
spaceForExpansion);
}

if (remaining > 0) {

// Some left-over pixels due to rounding errors

// Add one pixel to each container until there are no pixels left
// FIXME extra pixels should be divided among expanded widgets if
// such a widgets exists

Iterator<Widget> widgetIterator = iterator();
while (widgetIterator.hasNext() && remaining-- > 0) {
ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
.next();
childComponentContainer.expandExtra(orientation, 1);
}
}

}

private void handleOrientationUpdate(UIDL uidl) {
int newOrientation = ORIENTATION_VERTICAL;
if ("horizontal".equals(uidl.getStringAttribute("orientation"))) {
newOrientation = ORIENTATION_HORIZONTAL;
}

if (orientation != newOrientation) {
orientation = newOrientation;

for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {
childComponentContainer.setOrientation(orientation);
}
}

}

/**
* Updated components with relative height in horizontal layouts and
* components with relative width in vertical layouts. This is only needed
* if the height (horizontal layout) or width (vertical layout) has not been
* specified.
*/
private boolean updateRelativeSizesInNonMainDirection() {
int updateDirection = 1 - orientation;
if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth())
|| (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) {
return false;
}

boolean updated = false;
for (ChildComponentContainer componentContainer : widgetToComponentContainer
.values()) {
if (componentContainer.isComponentRelativeSized(updateDirection)) {
client.handleComponentRelativeSize(componentContainer
.getWidget());
}

updated = true;
}

return updated;
}

private int calculateLayoutDimensions() {
int summedWidgetWidth = 0;
int summedWidgetHeight = 0;

int maxWidgetWidth = 0;
int maxWidgetHeight = 0;

// Calculate layout dimensions from component dimensions
for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {

int widgetHeight = 0;
int widgetWidth = 0;
if (childComponentContainer.isComponentRelativeSized(orientation)) {
if (orientation == ORIENTATION_HORIZONTAL) {
widgetHeight = getWidgetHeight(childComponentContainer);
} else {
widgetWidth = getWidgetWidth(childComponentContainer);
}
} else {
widgetWidth = getWidgetWidth(childComponentContainer);
widgetHeight = getWidgetHeight(childComponentContainer);
}

summedWidgetWidth += widgetWidth;
summedWidgetHeight += widgetHeight;

maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight);
maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth);
}

if (isHorizontal()) {
summedWidgetWidth += activeSpacing.hSpacing
* (widgetToComponentContainer.size() - 1);
} else {
summedWidgetHeight += activeSpacing.vSpacing
* (widgetToComponentContainer.size() - 1);
}

Size layoutSize = updateLayoutDimensions(summedWidgetWidth,
summedWidgetHeight, maxWidgetWidth, maxWidgetHeight);

int remainingSpace;
if (isHorizontal()) {
remainingSpace = layoutSize.getWidth() - summedWidgetWidth;
} else {
remainingSpace = layoutSize.getHeight() - summedWidgetHeight;
}
if (remainingSpace < 0) {
remainingSpace = 0;
}

// ApplicationConnection.getConsole().log(
// "Layout size: " + activeLayoutSize);
return remainingSpace;
}

private int getWidgetHeight(ChildComponentContainer childComponentContainer) {
Size s = childComponentContainer.getWidgetSize();
return s.getHeight()
+ childComponentContainer.getCaptionHeightAboveComponent();
}

private int getWidgetWidth(ChildComponentContainer childComponentContainer) {
Size s = childComponentContainer.getWidgetSize();
int widgetWidth = s.getWidth()
+ childComponentContainer.getCaptionWidthAfterComponent();

/*
* If the component does not have a specified size in the main direction
* the caption may determine the space used by the component
*/
if (!childComponentContainer.widgetHasSizeSpecified(orientation)) {
int captionWidth = childComponentContainer
.getCaptionRequiredWidth();

if (captionWidth > widgetWidth) {
widgetWidth = captionWidth;
}
}

return widgetWidth;
}

private void calculateAlignments() {
int w = 0;
int h = 0;

if (isHorizontal()) {
// HORIZONTAL
h = activeLayoutSize.getHeight();
if (!isDynamicWidth()) {
w = -1;
}

} else {
// VERTICAL
w = activeLayoutSize.getWidth();
if (!isDynamicHeight()) {
h = -1;
}
}

for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
.values()) {
childComponentContainer.updateAlignments(w, h);
}

}

private void calculateContainerSize() {

/*
* Container size here means the size the container gets from the
* component. The expansion size is not include in this but taken
* separately into account.
*/
int height = 0, width = 0;
Iterator<Widget> widgetIterator = iterator();
if (isHorizontal()) {
height = activeLayoutSize.getHeight();
int availableWidth = activeLayoutSize.getWidth();
boolean first = true;
while (widgetIterator.hasNext()) {
ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
.next();
if (!childComponentContainer
.isComponentRelativeSized(ORIENTATION_HORIZONTAL)) {
/*
* Only components with non-relative size in the main
* direction has a container size
*/
width = childComponentContainer.getWidgetSize().getWidth()
+ childComponentContainer
.getCaptionWidthAfterComponent();

/*
* If the component does not have a specified size in the
* main direction the caption may determine the space used
* by the component
*/
if (!childComponentContainer
.widgetHasSizeSpecified(orientation)) {
int captionWidth = childComponentContainer
.getCaptionRequiredWidth();
// ApplicationConnection.getConsole().log(
// "Component width: " + width
// + ", caption width: " + captionWidth);
if (captionWidth > width) {
width = captionWidth;
}
}
} else {
width = 0;
}

if (!isDynamicWidth()) {
if (availableWidth == 0) {
/*
* Let the overflowing components overflow. IE has
* problems with zero sizes.
*/
// width = 0;
// height = 0;
} else if (width > availableWidth) {
width = availableWidth;

if (!first) {
width -= activeSpacing.hSpacing;
}
availableWidth = 0;
} else {
availableWidth -= width;
if (!first) {
availableWidth -= activeSpacing.hSpacing;
}
}

first = false;
}

childComponentContainer.setContainerSize(width, height);
}
} else {
width = activeLayoutSize.getWidth();
while (widgetIterator.hasNext()) {
ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator
.next();

if (!childComponentContainer
.isComponentRelativeSized(ORIENTATION_VERTICAL)) {
/*
* Only components with non-relative size in the main
* direction has a container size
*/
height = childComponentContainer.getWidgetSize()
.getHeight()
+ childComponentContainer
.getCaptionHeightAboveComponent();
} else {
height = 0;
}

childComponentContainer.setContainerSize(width, height);
}

}

}

private Size updateLayoutDimensions(int totalComponentWidth,
int totalComponentHeight, int maxComponentWidth,
int maxComponentHeight) {

/* Only need to calculate dynamic dimensions */
if (!isDynamicHeight() && !isDynamicWidth()) {
return activeLayoutSize;
}

int activeLayoutWidth = 0;
int activeLayoutHeight = 0;

// Update layout dimensions
if (isHorizontal()) {
// Horizontal
if (isDynamicWidth()) {
activeLayoutWidth = totalComponentWidth;
}

if (isDynamicHeight()) {
activeLayoutHeight = maxComponentHeight;
}

} else {
// Vertical
if (isDynamicWidth()) {
activeLayoutWidth = maxComponentWidth;
}

if (isDynamicHeight()) {
activeLayoutHeight = totalComponentHeight;
}
}

if (isDynamicWidth()) {
setActiveLayoutWidth(activeLayoutWidth);
setOuterLayoutWidth(activeLayoutSize.getWidth());
}

if (isDynamicHeight()) {
setActiveLayoutHeight(activeLayoutHeight);
setOuterLayoutHeight(activeLayoutSize.getHeight());
}

return activeLayoutSize;
}

private void setActiveLayoutWidth(int activeLayoutWidth) {
if (activeLayoutWidth < 0) {
activeLayoutWidth = 0;
}
activeLayoutSize.setWidth(activeLayoutWidth);
}

private void setActiveLayoutHeight(int activeLayoutHeight) {
if (activeLayoutHeight < 0) {
activeLayoutHeight = 0;
}
activeLayoutSize.setHeight(activeLayoutHeight);

}

private void setOuterLayoutWidth(int activeLayoutWidth) {
// Don't call setWidth to avoid triggering all kinds of recalculations
// Also don't call super.setWidth to avoid messing with the
// dynamicWidth property
int newPixelWidth = (activeLayoutWidth + activeMargins.getHorizontal());
getElement().getStyle().setWidth(newPixelWidth, Unit.PX);
}

private void setOuterLayoutHeight(int activeLayoutHeight) {
// Don't call setHeight to avoid triggering all kinds of recalculations
// Also don't call super.setHeight to avoid messing with the
// dynamicHeight property
int newPixelHeight = (activeLayoutHeight + activeMargins.getVertical());
getElement().getStyle().setHeight(newPixelHeight, Unit.PX);
}

/**
* Updates the spacing between components. Needs to be done only when
* components are added/removed.
*/
private void updateContainerMargins() {
ChildComponentContainer firstChildComponent = getFirstChildComponentContainer();
if (firstChildComponent != null) {
firstChildComponent.setMarginLeft(0);
firstChildComponent.setMarginTop(0);

for (ChildComponentContainer childComponent : widgetToComponentContainer
.values()) {
if (childComponent == firstChildComponent) {
continue;
}

if (isHorizontal()) {
childComponent.setMarginLeft(activeSpacing.hSpacing);
} else {
childComponent.setMarginTop(activeSpacing.vSpacing);
}
}
}
}

private boolean isHorizontal() {
return orientation == ORIENTATION_HORIZONTAL;
}

private boolean isVertical() {
return orientation == ORIENTATION_VERTICAL;
}

private ChildComponentContainer createChildContainer(VPaintableWidget child) {

// Create a container DIV for the child
ChildComponentContainer childComponent = new ChildComponentContainer(
child, orientation);

return childComponent;

}

public RenderSpace getAllocatedSpace(Widget child) {
int width = 0;
int height = 0;
ChildComponentContainer childComponentContainer = getComponentContainer(child);
// WIDTH CALCULATION
if (isVertical()) {
width = activeLayoutSize.getWidth();
width -= childComponentContainer.getCaptionWidthAfterComponent();
} else if (!isDynamicWidth()) {
// HORIZONTAL
width = childComponentContainer.getContSize().getWidth();
width -= childComponentContainer.getCaptionWidthAfterComponent();
}

// HEIGHT CALCULATION
if (isHorizontal()) {
height = activeLayoutSize.getHeight();
height -= childComponentContainer.getCaptionHeightAboveComponent();
} else if (!isDynamicHeight()) {
// VERTICAL
height = childComponentContainer.getContSize().getHeight();
height -= childComponentContainer.getCaptionHeightAboveComponent();
}

// ApplicationConnection.getConsole().log(
// "allocatedSpace for " + Util.getSimpleName(child) + ": "
// + width + "," + height);
RenderSpace space = new RenderSpace(width, height);
return space;
}

private void recalculateLayoutAndComponentSizes() {
recalculateLayout();

if (!(isDynamicHeight() && isDynamicWidth())) {
/* First update relative sized components */
for (ChildComponentContainer componentContainer : widgetToComponentContainer
.values()) {
client.handleComponentRelativeSize(componentContainer
.getWidget());

// Update widget size from DOM
componentContainer.updateWidgetSize();
}
}

if (isDynamicHeight()) {
/*
* Height is not necessarily correct anymore as the height of
* components might have changed if the width has changed.
*/

/*
* Get the new widget sizes from DOM and calculate new container
* sizes
*/
updateWidgetSizes();

/* Update layout dimensions based on widget sizes */
recalculateLayout();
}

updateRelativeSizesInNonMainDirection();
calculateAlignments();

setRootSize();
}

private void setRootSize() {
root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth());
root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight());
}

public boolean requestLayout(Set<Widget> children) {
for (Widget p : children) {
/* Update widget size from DOM */
ChildComponentContainer componentContainer = getComponentContainer(p);
// This should no longer be needed (after #2563)
// if (isDynamicWidth()) {
// componentContainer.setUnlimitedContainerWidth();
// } else {
// componentContainer.setLimitedContainerWidth(activeLayoutSize
// .getWidth());
// }

componentContainer.updateWidgetSize();

/*
* If this is the result of an caption icon onload event the caption
* size may have changed
*/
componentContainer.updateCaptionSize();
}

Size sizeBefore = new Size(activeLayoutSize.getWidth(),
activeLayoutSize.getHeight());

recalculateLayoutAndComponentSizes();
boolean sameSize = (sizeBefore.equals(activeLayoutSize));
if (!sameSize) {
/* Must inform child components about possible size updates */
client.runDescendentsLayout(this);
}

/* Automatically propagated upwards if the size has changed */

return sameSize;
}

@Override
public void setHeight(String height) {
Size sizeBefore = new Size(activeLayoutSize.getWidth(),
activeLayoutSize.getHeight());

super.setHeight(height);

if (height != null && !height.equals("")) {
setActiveLayoutHeight(getOffsetHeight()
- activeMargins.getVertical());
}

if (isRendering) {
sizeHasChangedDuringRendering = true;
} else {
recalculateLayoutAndComponentSizes();
boolean sameSize = (sizeBefore.equals(activeLayoutSize));
if (!sameSize) {
/* Must inform child components about possible size updates */
client.runDescendentsLayout(this);
}
}
}

@Override
public void setWidth(String width) {
if (this.width.equals(width) || !isVisible()) {
return;
}
Size sizeBefore = new Size(activeLayoutSize.getWidth(),
activeLayoutSize.getHeight());

super.setWidth(width);
this.width = width;
if (width != null && !width.equals("")) {
setActiveLayoutWidth(getOffsetWidth()
- activeMargins.getHorizontal());
}

if (isRendering) {
sizeHasChangedDuringRendering = true;
} else {
recalculateLayoutAndComponentSizes();
boolean sameSize = (sizeBefore.equals(activeLayoutSize));
if (!sameSize) {
/* Must inform child components about possible size updates */
client.runDescendentsLayout(this);
}
/*
* If the height changes as a consequence of this we must inform the
* parent also
*/
if (isDynamicHeight()
&& sizeBefore.getHeight() != activeLayoutSize.getHeight()) {
Util.notifyParentOfSizeChange(this, false);
}

}
}

protected void updateAlignmentsAndExpandRatios(UIDL uidl,
ArrayList<Widget> renderedWidgets) {

/*
*/
alignments = uidl.getMapAttribute("alignments");

/*
* UIDL contains a map of paintable ids to expand ratios
*/

expandRatios = uidl.getMapAttribute("expandRatios");
expandRatioSum = -1.0;

for (int i = 0; i < renderedWidgets.size(); i++) {
Widget widget = renderedWidgets.get(i);
String pid = VPaintableMap.get(client).getPid(widget);

ChildComponentContainer container = getComponentContainer(widget);

// Calculate alignment info
container.setAlignment(getAlignment(pid));

// Update expand ratio
container.setNormalizedExpandRatio(getExpandRatio(pid));
}
}

private AlignmentInfo getAlignment(String pid) {
if (alignments.containsKey(pid)) {
return new AlignmentInfo(alignments.getInt(pid));
} else {
return AlignmentInfo.TOP_LEFT;
}
}

private double getExpandRatio(String pid) {
if (expandRatioSum < 0) {
expandRatioSum = 0;
JsArrayString keyArray = expandRatios.getKeyArray();
int length = keyArray.length();
for (int i = 0; i < length; i++) {
expandRatioSum += expandRatios.getRawNumber(keyArray.get(i));
}
if (expandRatioSum == 0) {
// by default split equally among components
defaultExpandRatio = 1.0 / widgetToComponentContainer.size();
} else {
defaultExpandRatio = 0;
}
}
if (expandRatios.containsKey(pid)) {
return expandRatios.getRawNumber(pid) / expandRatioSum;
} else {
return defaultExpandRatio;
}
}

public void updateCaption(VPaintableWidget paintable, UIDL uidl) {
Widget widget = paintable.getWidgetForPaintable();
ChildComponentContainer componentContainer = getComponentContainer(widget);
componentContainer.updateCaption(uidl, client);
if (!isRendering) {
/*
* This was a component-only update and the possible size change
* must be propagated to the layout
*/
client.captionSizeUpdated(widget);
}
}

/**
* Returns the deepest nested child component which contains "element". The
* child component is also returned if "element" is part of its caption.
*
* @param element
* An element that is a nested sub element of the root element in
* this layout
* @return The Paintable which the element is a part of. Null if the element
* belongs to the layout and not to a child.
*/
private VPaintableWidget getComponent(Element element) {
return Util.getPaintableForElement(client, this, element);
}

public Widget getWidgetForPaintable() {
return this;
}

}

+ 2
- 2
src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java View File

@@ -3,12 +3,12 @@
*/
package com.vaadin.terminal.gwt.client.ui;

public class VVerticalLayout extends VOrderedLayout {
public class VVerticalLayout extends VMeasuringOrderedLayout {

public static final String CLASSNAME = "v-verticallayout";

public VVerticalLayout() {
super(CLASSNAME, ORIENTATION_VERTICAL);
super(CLASSNAME, true);
}

}

Loading…
Cancel
Save