From f4e19c674b02b41fa1c96e14e96ca4e87caff2a0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Fri, 10 Feb 2012 11:29:40 +0200 Subject: [PATCH] Update CssLayout to use MeasureManager (#8313) --- WebContent/VAADIN/themes/base/panel/panel.css | 2 + .../VAADIN/themes/reindeer/panel/panel.css | 1 + .../terminal/gwt/client/MeasureManager.java | 26 +- .../gwt/client/ui/LayoutPhaseListener.java | 7 + .../gwt/client/ui/ResizeRequired.java | 7 + .../vaadin/terminal/gwt/client/ui/VPanel.java | 244 +----------------- .../gwt/client/ui/VPanelPaintable.java | 116 ++++++--- .../components/panel/BasicPanelTest.java | 88 +++++++ 8 files changed, 212 insertions(+), 279 deletions(-) create mode 100644 src/com/vaadin/terminal/gwt/client/ui/LayoutPhaseListener.java create mode 100644 src/com/vaadin/terminal/gwt/client/ui/ResizeRequired.java create mode 100644 tests/testbench/com/vaadin/tests/components/panel/BasicPanelTest.java diff --git a/WebContent/VAADIN/themes/base/panel/panel.css b/WebContent/VAADIN/themes/base/panel/panel.css index 126d3da91c..5ef0d91e49 100644 --- a/WebContent/VAADIN/themes/base/panel/panel.css +++ b/WebContent/VAADIN/themes/base/panel/panel.css @@ -30,6 +30,8 @@ } .v-panel-content { overflow: auto; + box-sizing: border-box; + -moz-box-sizing: border-box; } .v-panel-deco { } \ No newline at end of file diff --git a/WebContent/VAADIN/themes/reindeer/panel/panel.css b/WebContent/VAADIN/themes/reindeer/panel/panel.css index ac0074a9e0..08f130271d 100644 --- a/WebContent/VAADIN/themes/reindeer/panel/panel.css +++ b/WebContent/VAADIN/themes/reindeer/panel/panel.css @@ -35,6 +35,7 @@ .v-panel-content > div { background: #fff; min-height: 100%; + overflow: visible; /* min-height causes problems with borders and paddings if overflow is e.g. hidden */ } .blue .v-panel-deco { border-color: #92a3ac; diff --git a/src/com/vaadin/terminal/gwt/client/MeasureManager.java b/src/com/vaadin/terminal/gwt/client/MeasureManager.java index 0bffc7c032..26f922ca5f 100644 --- a/src/com/vaadin/terminal/gwt/client/MeasureManager.java +++ b/src/com/vaadin/terminal/gwt/client/MeasureManager.java @@ -1,8 +1,8 @@ package com.vaadin.terminal.gwt.client; import com.google.gwt.core.client.JsArrayString; -import com.google.gwt.user.client.ui.RequiresResize; -import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ui.LayoutPhaseListener; +import com.vaadin.terminal.gwt.client.ui.ResizeRequired; public class MeasureManager { @@ -11,6 +11,12 @@ public class MeasureManager { VPaintableWidget[] paintableWidgets = paintableMap .getRegisteredPaintableWidgets(); + for (VPaintableWidget vPaintableWidget : paintableWidgets) { + if (vPaintableWidget instanceof LayoutPhaseListener) { + ((LayoutPhaseListener) vPaintableWidget).beforeLayout(); + } + } + int passes = 0; long start = System.currentTimeMillis(); while (true) { @@ -49,15 +55,12 @@ public class MeasureManager { 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(); - } else if (paintable instanceof CalculatingLayout) { + if (paintable instanceof CalculatingLayout) { CalculatingLayout calculating = (CalculatingLayout) paintable; calculating.updateHorizontalSizes(); calculating.updateVerticalSizes(); + } else if (paintable instanceof ResizeRequired) { + ((ResizeRequired) paintable).onResize(); } } } @@ -96,6 +99,13 @@ public class MeasureManager { } VConsole.log(b.toString()); } + + for (VPaintableWidget vPaintableWidget : paintableWidgets) { + if (vPaintableWidget instanceof LayoutPhaseListener) { + ((LayoutPhaseListener) vPaintableWidget).afterLayout(); + } + } + long end = System.currentTimeMillis(); VConsole.log("Total layout time: " + (end - start) + "ms"); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/LayoutPhaseListener.java b/src/com/vaadin/terminal/gwt/client/ui/LayoutPhaseListener.java new file mode 100644 index 0000000000..3ab7b8a9ea --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/LayoutPhaseListener.java @@ -0,0 +1,7 @@ +package com.vaadin.terminal.gwt.client.ui; + +public interface LayoutPhaseListener { + public void beforeLayout(); + + public void afterLayout(); +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ResizeRequired.java b/src/com/vaadin/terminal/gwt/client/ui/ResizeRequired.java new file mode 100644 index 0000000000..a03fff0210 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/ResizeRequired.java @@ -0,0 +1,7 @@ +package com.vaadin.terminal.gwt.client.ui; + +import com.vaadin.terminal.gwt.client.VPaintableWidget; + +public interface ResizeRequired extends VPaintableWidget { + public void onResize(); +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java index 82b362a385..45ded13d00 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java @@ -4,8 +4,6 @@ package com.vaadin.terminal.gwt.client.ui; -import java.util.Set; - import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.event.dom.client.TouchStartEvent; @@ -14,21 +12,14 @@ 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.ui.SimplePanel; -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.Container; import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.RenderInformation; -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.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; -public class VPanel extends SimplePanel implements Container, - ShortcutActionHandlerOwner, Focusable { +public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner, + Focusable { public static final String CLASSNAME = "v-panel"; @@ -48,36 +39,16 @@ public class VPanel extends SimplePanel implements Container, private Element errorIndicatorElement; - private String height; - VPaintableWidget layout; ShortcutActionHandler shortcutHandler; private String width = ""; - private Element geckoCaptionMeter; - int scrollTop; int scrollLeft; - private RenderInformation renderInformation = new RenderInformation(); - - private int borderPaddingHorizontal = -1; - - private int borderPaddingVertical = -1; - - private int captionPaddingHorizontal = -1; - - private int captionMarginLeft = -1; - - boolean rendering; - - private int contentMarginLeft = -1; - - private String previousStyleName; - private TouchScrollDelegate touchScrollDelegate; public VPanel() { @@ -106,7 +77,6 @@ public class VPanel extends SimplePanel implements Container, setStyleName(CLASSNAME); DOM.sinkEvents(getElement(), Event.ONKEYDOWN); DOM.sinkEvents(contentNode, Event.ONSCROLL | Event.TOUCHEVENTS); - contentNode.getStyle().setProperty("position", "relative"); getElement().getStyle().setProperty("overflow", "hidden"); addHandler(new TouchStartHandler() { public void onTouchStart(TouchStartEvent event) { @@ -148,15 +118,6 @@ public class VPanel extends SimplePanel implements Container, DOM.setInnerHTML(captionText, text); } - @Override - public void setStyleName(String style) { - if (!style.equals(previousStyleName)) { - super.setStyleName(style); - detectContainerBorders(); - previousStyleName = style; - } - } - void handleError(UIDL uidl) { if (uidl.hasAttribute("error")) { if (errorIndicatorElement == null) { @@ -190,54 +151,6 @@ public class VPanel extends SimplePanel implements Container, } } - public void runHacks(boolean runGeckoFix) { - if ((BrowserInfo.get().isIE()) && (width == null || width.equals(""))) { - /* - * IE (what version??) needs width to be specified for the root DIV - * so we calculate that from the sizes of the caption and layout - */ - int captionWidth = captionText.getOffsetWidth() - + getCaptionMarginLeft() + getCaptionPaddingHorizontal(); - int layoutWidth = layout.getWidgetForPaintable().getOffsetWidth() - + getContainerBorderWidth(); - int width = layoutWidth; - if (captionWidth > width) { - width = captionWidth; - } - - super.setWidth(width + "px"); - } - - if (runGeckoFix && BrowserInfo.get().isGecko()) { - // workaround for #1764 - if (width == null || width.equals("")) { - if (geckoCaptionMeter == null) { - geckoCaptionMeter = DOM.createDiv(); - DOM.appendChild(captionNode, geckoCaptionMeter); - } - int captionWidth = DOM.getElementPropertyInt(captionText, - "offsetWidth"); - int availWidth = DOM.getElementPropertyInt(geckoCaptionMeter, - "offsetWidth"); - if (captionWidth == availWidth) { - /* - * Caption width defines panel width -> Gecko based browsers - * somehow fails to float things right, without the - * "noncode" below - */ - setWidth(getOffsetWidth() + "px"); - } else { - DOM.setStyleAttribute(captionNode, "width", ""); - } - } - } - - client.runDescendentsLayout(this); - - Util.runWebkitOverflowAutoFix(contentNode); - - } - @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); @@ -275,159 +188,6 @@ public class VPanel extends SimplePanel implements Container, } - @Override - public void setHeight(String height) { - this.height = height; - super.setHeight(height); - if (height != null && !"".equals(height)) { - final int targetHeight = getOffsetHeight(); - int containerHeight = targetHeight - - captionNode.getParentElement().getOffsetHeight() - - bottomDecoration.getOffsetHeight() - - getContainerBorderHeight(); - if (containerHeight < 0) { - containerHeight = 0; - } - DOM.setStyleAttribute(contentNode, "height", containerHeight + "px"); - } else { - DOM.setStyleAttribute(contentNode, "height", ""); - } - if (!rendering) { - runHacks(true); - } - } - - private int getCaptionMarginLeft() { - if (captionMarginLeft < 0) { - detectContainerBorders(); - } - return captionMarginLeft; - } - - private int getContentMarginLeft() { - if (contentMarginLeft < 0) { - detectContainerBorders(); - } - return contentMarginLeft; - } - - private int getCaptionPaddingHorizontal() { - if (captionPaddingHorizontal < 0) { - detectContainerBorders(); - } - return captionPaddingHorizontal; - } - - private int getContainerBorderHeight() { - if (borderPaddingVertical < 0) { - detectContainerBorders(); - } - return borderPaddingVertical; - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - this.width = width; - super.setWidth(width); - if (!rendering) { - runHacks(true); - - if (height.equals("")) { - // Width change may affect height - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this, - this); - } - - } - } - - private int getContainerBorderWidth() { - if (borderPaddingHorizontal < 0) { - detectContainerBorders(); - } - return borderPaddingHorizontal; - } - - private void detectContainerBorders() { - DOM.setStyleAttribute(contentNode, "overflow", "hidden"); - - borderPaddingHorizontal = Util.measureHorizontalBorder(contentNode); - borderPaddingVertical = Util.measureVerticalBorder(contentNode); - - DOM.setStyleAttribute(contentNode, "overflow", "auto"); - - captionPaddingHorizontal = Util.measureHorizontalPaddingAndBorder( - captionNode, 26); - - captionMarginLeft = Util.measureMarginLeft(captionNode); - contentMarginLeft = Util.measureMarginLeft(contentNode); - - } - - public boolean hasChildComponent(Widget component) { - if (component != null && component == layout) { - return true; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - // TODO This is untested as no layouts require this - if (oldComponent != layout.getWidgetForPaintable()) { - return; - } - - setWidget(newComponent); - layout = VPaintableMap.get(client).getPaintable(newComponent); - } - - public RenderSpace getAllocatedSpace(Widget child) { - int w = 0; - int h = 0; - - if (width != null && !width.equals("")) { - w = getOffsetWidth() - getContainerBorderWidth(); - if (w < 0) { - w = 0; - } - } - - if (height != null && !height.equals("")) { - h = contentNode.getOffsetHeight() - getContainerBorderHeight(); - if (h < 0) { - h = 0; - } - } - - return new RenderSpace(w, h, true); - } - - public boolean requestLayout(Set children) { - // content size change might cause change to its available space - // (scrollbars) - client.handleComponentRelativeSize(layout.getWidgetForPaintable()); - if (height != null && height != "" && width != null && width != "") { - /* - * If the height and width has been specified the child components - * cannot make the size of the layout change - */ - return true; - } - runHacks(false); - return !renderInformation.updateSize(getElement()); - } - - @Override - protected void onAttach() { - super.onAttach(); - detectContainerBorders(); - } - public ShortcutActionHandler getShortcutActionHandler() { return shortcutHandler; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanelPaintable.java b/src/com/vaadin/terminal/gwt/client/ui/VPanelPaintable.java index 9c34a68528..eade1d7d0e 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPanelPaintable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanelPaintable.java @@ -4,18 +4,24 @@ package com.vaadin.terminal.gwt.client.ui; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.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.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.MeasuredSize; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.VPaintableWidget; -public class VPanelPaintable extends VAbstractPaintableWidgetContainer { +public class VPanelPaintable extends VAbstractPaintableWidgetContainer + implements ResizeRequired, LayoutPhaseListener { public static final String CLICK_EVENT_IDENTIFIER = "click"; + private Integer uidlScrollTop; + private ClickEventHandler clickEventHandler = new ClickEventHandler(this, CLICK_EVENT_IDENTIFIER) { @@ -26,14 +32,24 @@ public class VPanelPaintable extends VAbstractPaintableWidgetContainer { } }; + private Integer uidlScrollLeft; + + public VPanelPaintable() { + VPanel panel = getWidgetForPaintable(); + MeasuredSize measuredSize = getMeasuredSize(); + + measuredSize.registerDependency(panel.captionNode); + measuredSize.registerDependency(panel.bottomDecoration); + measuredSize.registerDependency(panel.contentNode); + } + @Override protected boolean delegateCaptionHandling() { return false; - }; + } @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - getWidgetForPaintable().rendering = true; if (isRealUpdate(uidl)) { // Handle caption displaying and style names, prior generics. @@ -86,7 +102,6 @@ public class VPanelPaintable extends VAbstractPaintableWidgetContainer { super.updateFromUIDL(uidl, client); if (!isRealUpdate(uidl)) { - getWidgetForPaintable().rendering = false; return; } @@ -130,42 +145,23 @@ public class VPanelPaintable extends VAbstractPaintableWidgetContainer { if (uidl.hasVariable("scrollTop") && uidl.getIntVariable("scrollTop") != getWidgetForPaintable().scrollTop) { - getWidgetForPaintable().scrollTop = uidl - .getIntVariable("scrollTop"); - getWidgetForPaintable().contentNode - .setScrollTop(getWidgetForPaintable().scrollTop); - // re-read the actual scrollTop in case invalid value was set - // (scrollTop != 0 when no scrollbar exists, other values would be - // caught by scroll listener), see #3784 - getWidgetForPaintable().scrollTop = getWidgetForPaintable().contentNode - .getScrollTop(); + // Sizes are not yet up to date, so changing the scroll position + // is deferred to after the layout phase + uidlScrollTop = new Integer(uidl.getIntVariable("scrollTop")); } if (uidl.hasVariable("scrollLeft") && uidl.getIntVariable("scrollLeft") != getWidgetForPaintable().scrollLeft) { - getWidgetForPaintable().scrollLeft = uidl - .getIntVariable("scrollLeft"); - getWidgetForPaintable().contentNode - .setScrollLeft(getWidgetForPaintable().scrollLeft); - // re-read the actual scrollTop in case invalid value was set - // (scrollTop != 0 when no scrollbar exists, other values would be - // caught by scroll listener), see #3784 - getWidgetForPaintable().scrollLeft = getWidgetForPaintable().contentNode - .getScrollLeft(); + // Sizes are not yet up to date, so changing the scroll position + // is deferred to after the layout phase + uidlScrollLeft = new Integer(uidl.getIntVariable("scrollLeft")); } - // Must be run after scrollTop is set as Webkit overflow fix re-sets the - // scrollTop - getWidgetForPaintable().runHacks(false); - // And apply tab index if (uidl.hasVariable("tabindex")) { getWidgetForPaintable().contentNode.setTabIndex(uidl .getIntVariable("tabindex")); } - - getWidgetForPaintable().rendering = false; - } public void updateCaption(VPaintableWidget component, UIDL uidl) { @@ -182,4 +178,66 @@ public class VPanelPaintable extends VAbstractPaintableWidgetContainer { return GWT.create(VPanel.class); } + public void onResize() { + updateSizes(); + } + + void updateSizes() { + MeasuredSize measuredSize = getMeasuredSize(); + VPanel panel = getWidgetForPaintable(); + + Style contentStyle = panel.contentNode.getStyle(); + if (isUndefinedHeight()) { + contentStyle.clearHeight(); + } else { + contentStyle.setHeight(100, Unit.PCT); + } + + if (isUndefinedWidth()) { + contentStyle.clearWidth(); + } else { + contentStyle.setWidth(100, Unit.PCT); + } + + int top = measuredSize.getDependencyOuterHeight(panel.captionNode); + int bottom = measuredSize + .getDependencyOuterHeight(panel.bottomDecoration); + + Style style = panel.getElement().getStyle(); + panel.captionNode.getStyle().setMarginTop(-top, Unit.PX); + panel.bottomDecoration.getStyle().setMarginBottom(-bottom, Unit.PX); + style.setPaddingTop(top, Unit.PX); + style.setPaddingBottom(bottom, Unit.PX); + + // Update scroll positions + panel.contentNode.setScrollTop(panel.scrollTop); + panel.contentNode.setScrollLeft(panel.scrollLeft); + // Read actual value back to ensure update logic is correct + panel.scrollTop = panel.contentNode.getScrollTop(); + panel.scrollLeft = panel.contentNode.getScrollLeft(); + } + + public void beforeLayout() { + // Nothing to do + } + + public void afterLayout() { + VPanel panel = getWidgetForPaintable(); + if (uidlScrollTop != null) { + panel.contentNode.setScrollTop(uidlScrollTop.intValue()); + // Read actual value back to ensure update logic is correct + // TODO Does this trigger reflows? + panel.scrollTop = panel.contentNode.getScrollTop(); + uidlScrollTop = null; + } + + if (uidlScrollLeft != null) { + panel.contentNode.setScrollLeft(uidlScrollLeft.intValue()); + // Read actual value back to ensure update logic is correct + // TODO Does this trigger reflows? + panel.scrollLeft = panel.contentNode.getScrollLeft(); + uidlScrollLeft = null; + } + } + } diff --git a/tests/testbench/com/vaadin/tests/components/panel/BasicPanelTest.java b/tests/testbench/com/vaadin/tests/components/panel/BasicPanelTest.java new file mode 100644 index 0000000000..d23e317572 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/panel/BasicPanelTest.java @@ -0,0 +1,88 @@ +package com.vaadin.tests.components.panel; + +import java.util.Map; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.themes.Reindeer; + +public class BasicPanelTest extends TestBase { + private final Label scrollPosition = new Label(); + private final Panel panel = new Panel("Panel caption") { + @Override + public void changeVariables(Object source, Map variables) { + super.changeVariables(source, variables); + updateLabelText(); + } + }; + + @Override + protected void setup() { + getLayout().setWidth("600px"); + getLayout().setHeight("100%"); + + HorizontalLayout actions = new HorizontalLayout(); + actions.setSpacing(true); + + actions.addComponent(scrollPosition); + actions.addComponent(new Button("Sync")); + + final CheckBox heightSelection = new CheckBox("Undefined height"); + heightSelection.setImmediate(true); + heightSelection.addListener(new ValueChangeListener() { + public void valueChange(ValueChangeEvent event) { + if (heightSelection.getValue() == Boolean.TRUE) { + panel.setHeight(null); + } else { + panel.setHeight("100%"); + } + } + }); + actions.addComponent(heightSelection); + + panel.setWidth("200px"); + panel.setHeight("100%"); + panel.setStyleName(Reindeer.PANEL_LIGHT); + + panel.getContent().setCaption("Content caption"); + + TextArea textArea = new TextArea("TextArea caption"); + textArea.setWidth("300px"); + textArea.setHeight("500px"); + panel.addComponent(textArea); + + getLayout().addComponent(actions); + getLayout().addComponent(panel); + getLayout().setExpandRatio(panel, 1); + + panel.setScrollable(true); + panel.setScrollTop(50); + panel.setScrollLeft(50); + panel.setImmediate(true); + + updateLabelText(); + } + + private void updateLabelText() { + scrollPosition.setValue("Scrolled to " + panel.getScrollTop()); + } + + @Override + protected String getDescription() { + return "Simple test for basic panel functionality"; + } + + @Override + protected Integer getTicketNumber() { + // TODO Auto-generated method stub + return null; + } + +} -- 2.39.5