}
.v-panel-content {
overflow: auto;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
}
.v-panel-deco {
}
\ No newline at end of file
.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;
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 {
VPaintableWidget[] paintableWidgets = paintableMap
.getRegisteredPaintableWidgets();
+ for (VPaintableWidget vPaintableWidget : paintableWidgets) {
+ if (vPaintableWidget instanceof LayoutPhaseListener) {
+ ((LayoutPhaseListener) vPaintableWidget).beforeLayout();
+ }
+ }
+
int passes = 0;
long start = System.currentTimeMillis();
while (true) {
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();
}
}
}
}
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");
}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui;
+
+public interface LayoutPhaseListener {
+ public void beforeLayout();
+
+ public void afterLayout();
+}
--- /dev/null
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.VPaintableWidget;
+
+public interface ResizeRequired extends VPaintableWidget {
+ public void onResize();
+}
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;
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";
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() {
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) {
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) {
}
}
- 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);
}
- @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<Widget> 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;
}
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) {
}
};
+ 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.
super.updateFromUIDL(uidl, client);
if (!isRealUpdate(uidl)) {
- getWidgetForPaintable().rendering = false;
return;
}
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) {
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;
+ }
+ }
+
}
--- /dev/null
+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<String, Object> 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;
+ }
+
+}