| /* |
| * Copyright 2009 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.layout.client; |
| |
| import static com.google.gwt.dom.client.Style.Unit.CM; |
| import static com.google.gwt.dom.client.Style.Unit.EM; |
| import static com.google.gwt.dom.client.Style.Unit.EX; |
| import static com.google.gwt.dom.client.Style.Unit.IN; |
| import static com.google.gwt.dom.client.Style.Unit.MM; |
| import static com.google.gwt.dom.client.Style.Unit.PC; |
| import static com.google.gwt.dom.client.Style.Unit.PCT; |
| import static com.google.gwt.dom.client.Style.Unit.PT; |
| import static com.google.gwt.dom.client.Style.Unit.PX; |
| |
| 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.Position; |
| import com.google.gwt.dom.client.Style.Unit; |
| import com.google.gwt.junit.DoNotRunWith; |
| import com.google.gwt.junit.Platform; |
| import com.google.gwt.junit.client.GWTTestCase; |
| import com.google.gwt.layout.client.Layout.Alignment; |
| import com.google.gwt.layout.client.Layout.Layer; |
| import com.google.gwt.user.client.Window; |
| |
| /** |
| * Tests for the {@link Layout} class. |
| * |
| * TODO: Note that this test is *not* currently part of any suite. We can't run |
| * it reliably until we have a way to switch on "standards" mode in |
| * GWTTestCases. |
| */ |
| @DoNotRunWith(Platform.HtmlUnit) |
| public class LayoutTest extends GWTTestCase { |
| |
| private static interface LayerInitializer { |
| void setupLayers(Layer l0, Layer l1); |
| } |
| |
| private DivElement parent, child0, child1; |
| private Element wrapper0, wrapper1; |
| private Layout layout; |
| private Layer layer0, layer1; |
| |
| @Override |
| public String getModuleName() { |
| return "com.google.gwt.layout.LayoutTest"; |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_LTRB_PX_CM() { |
| testAnimationTransitions_LTWH_LTRB(PX, CM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_LTRB_PX_EM() { |
| testAnimationTransitions_LTWH_LTRB(PX, EM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_LTRB_PX_EX() { |
| testAnimationTransitions_LTWH_LTRB(PX, EX); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_LTRB_PX_PCT() { |
| testAnimationTransitions_LTWH_LTRB(PX, PCT); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_RBWH_PX_CM() { |
| testAnimationTransitions_LTWH_RBWH(PX, CM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_RBWH_PX_EM() { |
| testAnimationTransitions_LTWH_RBWH(PX, EM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_RBWH_PX_EX() { |
| testAnimationTransitions_LTWH_RBWH(PX, EX); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_LTWH_RBWH_PX_PCT() { |
| testAnimationTransitions_LTWH_RBWH(PX, PCT); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_RBWH_LTRB_PX_CM() { |
| testAnimationTransitions_RBWH_LTRB(PX, CM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_RBWH_LTRB_PX_EM() { |
| testAnimationTransitions_RBWH_LTRB(PX, EM); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_RBWH_LTRB_PX_EX() { |
| testAnimationTransitions_RBWH_LTRB(PX, EX); |
| } |
| |
| /** |
| * Tests animation constraint- and unit-transitions. |
| */ |
| public void testAnimationTransitions_RBWH_LTRB_PX_PCT() { |
| testAnimationTransitions_RBWH_LTRB(PX, PCT); |
| } |
| |
| /** |
| * Tests child alignment within a layer. |
| */ |
| public void testChildAlignment() { |
| layer0.setLeftWidth(0, PX, 128, PX); |
| layer0.setTopHeight(0, PX, 256, PX); |
| |
| layer0.setChildHorizontalPosition(Alignment.STRETCH); |
| layer0.setChildVerticalPosition(Alignment.STRETCH); |
| layout.layout(); |
| assertEquals(0, child0.getOffsetLeft()); |
| assertEquals(0, child0.getOffsetTop()); |
| assertEquals(128, child0.getOffsetWidth()); |
| assertEquals(256, child0.getOffsetHeight()); |
| |
| child0.getStyle().setWidth(64, PX); |
| child0.getStyle().setHeight(128, PX); |
| |
| layer0.setChildHorizontalPosition(Alignment.BEGIN); |
| layer0.setChildVerticalPosition(Alignment.BEGIN); |
| layout.layout(); |
| assertEquals(0, child0.getOffsetLeft()); |
| assertEquals(0, child0.getOffsetTop()); |
| assertEquals(64, child0.getOffsetWidth()); |
| assertEquals(128, child0.getOffsetHeight()); |
| |
| layer0.setChildHorizontalPosition(Alignment.END); |
| layer0.setChildVerticalPosition(Alignment.END); |
| layout.layout(); |
| assertEquals(64, child0.getOffsetLeft()); |
| assertEquals(128, child0.getOffsetTop()); |
| assertEquals(64, child0.getOffsetWidth()); |
| assertEquals(128, child0.getOffsetHeight()); |
| } |
| |
| /** |
| * Test that fillParent() works properly when the outer div is a child of |
| * another div, and that it correctly follows that div's size. |
| */ |
| public void testFillParent() { |
| // We don't use the default elements created in gwtSetUp() because we need |
| // to test the behavior when the layout is contained by an element other |
| // than the <body>. |
| Document doc = Document.get(); |
| DivElement container = doc.createDivElement(); |
| DivElement parent = doc.createDivElement(); |
| DivElement child = doc.createDivElement(); |
| child.setInnerHTML(" "); |
| doc.getBody().appendChild(container); |
| container.appendChild(parent); |
| |
| // The container has to be position:relative so that it serves as an offset |
| // parent. |
| container.getStyle().setPosition(Position.RELATIVE); |
| container.getStyle().setWidth(128, PX); |
| container.getStyle().setHeight(256, PX); |
| |
| Layout layout = new Layout(parent); |
| layout.onAttach(); |
| Layer layer = layout.attachChild(child); |
| layer.setTopBottom(0, PX, 0, PX); |
| layer.setLeftRight(0, PX, 0, PX); |
| |
| layout.fillParent(); |
| layout.layout(); |
| |
| // Test 128x256. |
| assertEquals(128, container.getOffsetWidth()); |
| assertEquals(256, container.getOffsetHeight()); |
| assertEquals(128, parent.getOffsetWidth()); |
| assertEquals(256, parent.getOffsetHeight()); |
| assertEquals(128, child.getOffsetWidth()); |
| assertEquals(256, child.getOffsetHeight()); |
| |
| // Expand to 256x256. The layout should react automatically. |
| container.getStyle().setWidth(256, PX); |
| container.getStyle().setHeight(128, PX); |
| assertEquals(256, container.getOffsetWidth()); |
| assertEquals(256, parent.getOffsetWidth()); |
| assertEquals(256, child.getOffsetWidth()); |
| |
| layout.onDetach(); |
| } |
| |
| /** |
| * Test that fillParent() works properly when the outer div is a child of the |
| * document body. |
| */ |
| public void testFillWindow() { |
| layer0.setTopBottom(0, PX, 0, PX); |
| layer0.setLeftRight(0, PX, 0, PX); |
| layout.layout(); |
| |
| int w = Window.getClientWidth(); |
| int h = Window.getClientHeight(); |
| assertEquals(w, parent.getOffsetWidth()); |
| assertEquals(h, parent.getOffsetHeight()); |
| assertEquals(w, child0.getOffsetWidth()); |
| assertEquals(h, child0.getOffsetHeight()); |
| } |
| |
| /** |
| * Tests that the layout reacts to font-size changes. |
| */ |
| public void testFontSizeChange() { |
| layer0.setLeftWidth(0, PX, 1, EM); |
| layer0.setTopHeight(0, PX, 1, EM); |
| layout.layout(); |
| |
| parent.getStyle().setFontSize(12, PT); |
| int cw = child0.getOffsetWidth(); |
| int ch = child0.getOffsetHeight(); |
| |
| parent.getStyle().setFontSize(24, PT); |
| int nw = child0.getOffsetWidth(); |
| int nh = child0.getOffsetHeight(); |
| assertTrue(nw > cw); |
| assertTrue(nh > ch); |
| |
| parent.getStyle().clearFontSize(); |
| } |
| |
| /** |
| * Ensures that two children laid out using various units in such a way that |
| * they should abut one another actually do so. |
| */ |
| public void testLayoutStructure() { |
| testHorizontalSplit(CM); |
| testHorizontalSplit(EM); |
| testHorizontalSplit(EX); |
| testHorizontalSplit(IN); |
| testHorizontalSplit(MM); |
| testHorizontalSplit(PC); |
| testHorizontalSplit(PT); |
| testHorizontalSplit(PX); |
| |
| testVerticalSplit(CM); |
| testVerticalSplit(EM); |
| testVerticalSplit(EX); |
| testVerticalSplit(IN); |
| testVerticalSplit(MM); |
| testVerticalSplit(PC); |
| testVerticalSplit(PT); |
| testVerticalSplit(PX); |
| } |
| |
| /** |
| * Tests (left-right, left-width, right-width) x (top-bottom, top-height, |
| * bottom-height). Ok, so we don't test the *entire* cross-product, but enough |
| * to be comfortable. |
| */ |
| public void testStaticConstraints() { |
| // left-right, top-bottom |
| layer0.setTopBottom(32, PX, 32, PX); |
| layer0.setLeftRight(32, PX, 32, PX); |
| layout.layout(); |
| |
| int w = parent.getClientWidth(); |
| int h = parent.getClientHeight(); |
| assertEquals(32, wrapper0.getOffsetLeft()); |
| assertEquals(32, wrapper0.getOffsetTop()); |
| assertEquals(w - 64, wrapper0.getOffsetWidth()); |
| assertEquals(h - 64, wrapper0.getOffsetHeight()); |
| |
| // left-width, top-height |
| layer0.setTopHeight(16, PX, 128, PX); |
| layer0.setLeftWidth(16, PX, 128, PX); |
| layout.layout(); |
| |
| assertEquals(16, wrapper0.getOffsetLeft()); |
| assertEquals(16, wrapper0.getOffsetTop()); |
| assertEquals(128, wrapper0.getOffsetWidth()); |
| assertEquals(128, wrapper0.getOffsetHeight()); |
| |
| // right-width, bottom-height |
| layer0.setBottomHeight(16, PX, 128, PX); |
| layer0.setRightWidth(16, PX, 128, PX); |
| layout.layout(); |
| |
| assertEquals(w - (16 + 128), wrapper0.getOffsetLeft()); |
| assertEquals(h - (16 + 128), wrapper0.getOffsetTop()); |
| assertEquals(128, wrapper0.getOffsetWidth()); |
| assertEquals(128, wrapper0.getOffsetHeight()); |
| } |
| |
| /** |
| * Tests all unit types. |
| */ |
| public void testUnits() { |
| // CM |
| layer0.setTopBottom(1, CM, 1, CM); |
| layer0.setLeftRight(1, CM, 1, CM); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // MM |
| layer0.setTopBottom(1, MM, 1, MM); |
| layer0.setLeftRight(1, MM, 1, MM); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // IN |
| layer0.setTopBottom(1, IN, 1, IN); |
| layer0.setLeftRight(1, IN, 1, IN); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // EM |
| layer0.setTopBottom(1, EM, 1, EM); |
| layer0.setLeftRight(1, EM, 1, EM); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // EX |
| layer0.setTopBottom(1, EX, 1, EX); |
| layer0.setLeftRight(1, EX, 1, EX); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // PC |
| layer0.setTopBottom(1, PC, 1, PC); |
| layer0.setLeftRight(1, PC, 1, PC); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // PT |
| layer0.setTopBottom(10, PT, 10, PT); |
| layer0.setLeftRight(10, PT, 10, PT); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| |
| // PCT |
| layer0.setTopBottom(10, PCT, 10, PCT); |
| layer0.setLeftRight(10, PCT, 10, PCT); |
| layout.layout(); |
| assertLeftRightTopBottomUnitsMakeSense(wrapper0); |
| } |
| |
| /** |
| * Tests layout in the presence of decorations on the parent and child |
| * elements. |
| */ |
| public void testWithDecorations() { |
| layer0.setTopBottom(0, PX, 0, PX); |
| layer0.setLeftRight(0, PX, 0, PX); |
| layout.layout(); |
| |
| // Give each of the parent and child 1px margin and 1px border. |
| parent.getStyle().setMargin(1, PX); |
| parent.getStyle().setProperty("border", "1px solid black"); |
| |
| child0.getStyle().setMargin(1, PX); |
| child0.getStyle().setProperty("border", "1px solid black"); |
| layout.layout(); |
| |
| int w = Window.getClientWidth(); |
| int h = Window.getClientHeight(); |
| int pw = parent.getOffsetWidth(); |
| int ph = parent.getOffsetHeight(); |
| |
| // The parent's offsetSize should be 2px smaller than the window's client |
| // area, because of the margin (the border is *included* in the offsetSize). |
| assertEquals(w - 2, pw); |
| assertEquals(h - 2, ph); |
| |
| // The child's offsetSize (actually that of its wrapper element), should be |
| // 2px smaller than the parent, for precisely the same reason. |
| assertEquals(pw - 2, wrapper0.getOffsetWidth()); |
| assertEquals(ph - 2, wrapper0.getOffsetHeight()); |
| } |
| |
| @Override |
| protected void gwtSetUp() throws Exception { |
| Window.enableScrolling(false); |
| |
| Document doc = Document.get(); |
| parent = doc.createDivElement(); |
| child0 = doc.createDivElement(); |
| child1 = doc.createDivElement(); |
| doc.getBody().appendChild(parent); |
| |
| layout = new Layout(parent); |
| layout.onAttach(); |
| layout.fillParent(); |
| |
| layer0 = layout.attachChild(child0); |
| layer1 = layout.attachChild(child1); |
| |
| wrapper0 = child0.getParentElement(); |
| wrapper1 = child1.getParentElement(); |
| } |
| |
| @Override |
| protected void gwtTearDown() throws Exception { |
| Window.enableScrolling(true); |
| Document.get().getBody().removeChild(parent); |
| layout.onDetach(); |
| } |
| |
| private void assertLeftRightTopBottomUnitsMakeSense(Element elem) { |
| // Assume that the element has been laid out to (l, t, r, b) = (1u, 1u, 1u, |
| // 1u). Assert that the element's clientLeft/Top are non-zero, and that the |
| // clientLeft/Top/Width/Height are consistent with the parent's size. |
| int w = parent.getClientWidth(); |
| int h = parent.getClientHeight(); |
| int cl = elem.getOffsetLeft(); |
| int ct = elem.getOffsetTop(); |
| int cw = elem.getOffsetWidth(); |
| int ch = elem.getOffsetHeight(); |
| |
| // Assert that the left-top unit came out at least non-zero size. |
| assertTrue(cl > 0); |
| assertTrue(ct > 0); |
| |
| // Assert that the right-bottom also came out non-zero. We should be able |
| // to assert that it came out the same size as the top-left, but it turns |
| // out that this isn't quite reliable because of rounding errors. |
| assertTrue(w - (cl + cw) > 0); |
| assertTrue(h - (ct + ch) > 0); |
| } |
| |
| // This method may only be called once per test, as it uses delayTestFinish() |
| // internally. |
| private void testAnimationTransitions_LTWH_LTRB(final Unit unit0, |
| final Unit unit1) { |
| testAnimationTransitionsHelper(new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l0.setLeftWidth(0, unit0, 10, unit0); |
| l0.setTopHeight(0, unit0, 10, unit0); |
| l1.setLeftRight(1, unit1, 1, unit1); |
| l1.setTopBottom(1, unit1, 1, unit1); |
| } |
| }, new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l1.setLeftWidth(0, unit0, 10, unit0); |
| l1.setTopHeight(0, unit0, 10, unit0); |
| l0.setLeftRight(1, unit1, 1, unit1); |
| l0.setTopBottom(1, unit1, 1, unit1); |
| } |
| }); |
| } |
| |
| // This method may only be called once per test, as it uses delayTestFinish() |
| // internally. |
| private void testAnimationTransitions_LTWH_RBWH(final Unit unit0, |
| final Unit unit1) { |
| testAnimationTransitionsHelper(new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l0.setLeftWidth(0, unit0, 10, unit0); |
| l0.setTopHeight(0, unit0, 10, unit0); |
| l1.setRightWidth(0, unit1, 10, unit1); |
| l1.setBottomHeight(0, unit1, 10, unit1); |
| } |
| }, new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l1.setLeftWidth(0, unit0, 10, unit0); |
| l1.setTopHeight(0, unit0, 10, unit0); |
| l0.setRightWidth(0, unit1, 10, unit1); |
| l0.setBottomHeight(0, unit1, 10, unit1); |
| } |
| }); |
| } |
| |
| // This method may only be called once per test, as it uses delayTestFinish() |
| // internally. |
| private void testAnimationTransitions_RBWH_LTRB(final Unit unit0, |
| final Unit unit1) { |
| testAnimationTransitionsHelper(new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l0.setRightWidth(0, unit0, 10, unit0); |
| l0.setBottomHeight(0, unit0, 10, unit0); |
| l1.setLeftRight(1, unit1, 1, unit1); |
| l1.setTopBottom(1, unit1, 1, unit1); |
| } |
| }, new LayerInitializer() { |
| public void setupLayers(Layer l0, Layer l1) { |
| l1.setRightWidth(0, unit0, 10, unit0); |
| l1.setBottomHeight(0, unit0, 10, unit0); |
| l0.setLeftRight(1, unit1, 1, unit1); |
| l0.setTopBottom(1, unit1, 1, unit1); |
| } |
| }); |
| } |
| |
| // This method may only be called once per test, as it uses delayTestFinish() |
| // internally. |
| private void testAnimationTransitionsHelper(LayerInitializer before, |
| LayerInitializer after) { |
| before.setupLayers(layer0, layer1); |
| layout.layout(); |
| |
| final int l0 = wrapper0.getOffsetLeft(); |
| final int t0 = wrapper0.getOffsetTop(); |
| final int w0 = wrapper0.getOffsetWidth(); |
| final int h0 = wrapper0.getOffsetHeight(); |
| |
| final int l1 = wrapper1.getOffsetLeft(); |
| final int t1 = wrapper1.getOffsetTop(); |
| final int w1 = wrapper1.getOffsetWidth(); |
| final int h1 = wrapper1.getOffsetHeight(); |
| |
| after.setupLayers(layer0, layer1); |
| delayTestFinish(200); |
| layout.layout(100, new Layout.AnimationCallback() { |
| public void onAnimationComplete() { |
| // Assert that the two layers have swapped positions. |
| assertEquals(l0, wrapper1.getOffsetLeft()); |
| assertEquals(t0, wrapper1.getOffsetTop()); |
| assertEquals(w0, wrapper1.getOffsetWidth()); |
| assertEquals(h0, wrapper1.getOffsetHeight()); |
| |
| assertEquals(l1, wrapper0.getOffsetLeft()); |
| assertEquals(t1, wrapper0.getOffsetTop()); |
| assertEquals(w1, wrapper0.getOffsetWidth()); |
| assertEquals(h1, wrapper0.getOffsetHeight()); |
| |
| finishTest(); |
| } |
| |
| public void onLayout(Layer layer, double progress) { |
| } |
| }); |
| } |
| |
| private void testHorizontalSplit(Unit unit) { |
| // Line them up horizontally, split at 5 units. |
| layer0.setTopBottom(0, PX, 0, PX); |
| layer0.setLeftWidth(0, PX, 5, unit); |
| |
| layer1.setTopBottom(0, PX, 0, PX); |
| layer1.setLeftRight(5, unit, 0, PX); |
| layout.layout(); |
| |
| int child0Right = wrapper0.getOffsetWidth(); |
| int child1Left = wrapper1.getOffsetLeft(); |
| assertEquals(child0Right, child1Left); |
| } |
| |
| private void testVerticalSplit(Unit unit) { |
| // Line them up vertically, split at 5em. |
| layer0.setTopHeight(0, PX, 5, unit); |
| layer0.setLeftRight(0, PX, 0, PX); |
| |
| layer1.setTopBottom(5, unit, 0, PX); |
| layer1.setLeftRight(0, PX, 0, PX); |
| layout.layout(); |
| |
| int child0Bottom = wrapper0.getOffsetHeight(); |
| int child1Top = wrapper1.getOffsetTop(); |
| assertEquals(child0Bottom, child1Top); |
| } |
| } |