From 628a2edf50c355e6e43e1da2e32224d32651327d Mon Sep 17 00:00:00 2001 From: Mika Murtojarvi Date: Thu, 11 Dec 2014 18:25:40 +0200 Subject: [PATCH] Declarative support for split panel (#7749). Change-Id: I96b7be150c9b6511f9fb701c5ee26afca67f1512 --- .../src/com/vaadin/server/SizeWithUnit.java | 127 +++++++++++++ .../src/com/vaadin/ui/AbstractComponent.java | 59 +----- .../src/com/vaadin/ui/AbstractSplitPanel.java | 139 ++++++++++++++ .../tests/layoutparser/all-components.html | 15 +- .../TestSynchronizeFromDesign.java | 173 ++++++++++++++++++ .../TestSynchronizeToDesign.java | 151 +++++++++++++++ 6 files changed, 606 insertions(+), 58 deletions(-) create mode 100644 server/src/com/vaadin/server/SizeWithUnit.java create mode 100644 server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeFromDesign.java create mode 100644 server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeToDesign.java diff --git a/server/src/com/vaadin/server/SizeWithUnit.java b/server/src/com/vaadin/server/SizeWithUnit.java new file mode 100644 index 0000000000..4c3c51eaec --- /dev/null +++ b/server/src/com/vaadin/server/SizeWithUnit.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.server; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.vaadin.server.Sizeable.Unit; +import com.vaadin.shared.util.SharedUtil; + +/** + * A class for representing a value-unit pair. Also contains utility methods for + * parsing such pairs from a string. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class SizeWithUnit implements Serializable { + private float size; + private Unit unit; + private static final Pattern sizePattern = Pattern + .compile(SharedUtil.SIZE_PATTERN); + + /** + * Constructs a new SizeWithUnit object representing the pair (size, unit). + * + * @param size + * a numeric value + * @param unit + * a unit + */ + public SizeWithUnit(float size, Unit unit) { + this.size = size; + this.unit = unit; + } + + /** + * Returns the numeric value stored in this object. + * + * @return the value of this (value, unit) pair + */ + public float getSize() { + return size; + } + + /** + * Returns the unit stored in this object. + * + * @return the unit of this (value, unit) pair + */ + public Unit getUnit() { + return unit; + } + + /** + * Returns an object whose numeric value and unit are taken from the string + * s. If s does not specify a unit and defaultUnit is not null, defaultUnit + * is used as the unit. If defaultUnit is null and s is a nonempty string + * representing a unitless number, an exception is thrown. Null or empty + * string will produce {-1,Unit#PIXELS}. + * + * @param s + * the string to be parsed + * @param defaultUnit + * The unit to be used if s does not contain any unit. Use null + * for no default unit. + * @return an object containing the parsed value and unit + */ + public static SizeWithUnit parseStringSize(String s, Unit defaultUnit) { + if (s == null) { + return null; + } + s = s.trim(); + if ("".equals(s)) { + return null; + } + float size = 0; + Unit unit = null; + Matcher matcher = sizePattern.matcher(s); + if (matcher.find()) { + size = Float.parseFloat(matcher.group(1)); + if (size < 0) { + size = -1; + unit = Unit.PIXELS; + } else { + String symbol = matcher.group(2); + if ((symbol != null && symbol.length() > 0) + || defaultUnit == null) { + unit = Unit.getUnitFromSymbol(symbol); + } else { + unit = defaultUnit; + } + } + } else { + throw new IllegalArgumentException("Invalid size argument: \"" + s + + "\" (should match " + sizePattern.pattern() + ")"); + } + return new SizeWithUnit(size, unit); + } + + /** + * Returns an object whose numeric value and unit are taken from the string + * s. Null or empty string will produce {-1,Unit#PIXELS}. An exception is + * thrown if s specifies a number without a unit. + * + * @param s + * the string to be parsed + * @return an object containing the parsed value and unit + */ + public static SizeWithUnit parseStringSize(String s) { + return parseStringSize(s, null); + } +} \ No newline at end of file diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 18cd1f5fa4..1cda253b01 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -16,7 +16,6 @@ package com.vaadin.ui; -import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -28,8 +27,6 @@ import java.util.Locale; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Attributes; @@ -46,6 +43,7 @@ import com.vaadin.server.ErrorMessage.ErrorLevel; import com.vaadin.server.Extension; import com.vaadin.server.Resource; import com.vaadin.server.Responsive; +import com.vaadin.server.SizeWithUnit; import com.vaadin.server.Sizeable; import com.vaadin.server.UserError; import com.vaadin.server.VaadinSession; @@ -100,8 +98,6 @@ public abstract class AbstractComponent extends AbstractClientConnector private float height = SIZE_UNDEFINED; private Unit widthUnit = Unit.PIXELS; private Unit heightUnit = Unit.PIXELS; - private static final Pattern sizePattern = Pattern - .compile(SharedUtil.SIZE_PATTERN); /** * Keeps track of the Actions added to this component; the actual @@ -890,7 +886,7 @@ public abstract class AbstractComponent extends AbstractClientConnector */ @Override public void setWidth(String width) { - Size size = parseStringSize(width); + SizeWithUnit size = SizeWithUnit.parseStringSize(width); if (size != null) { setWidth(size.getSize(), size.getUnit()); } else { @@ -905,7 +901,7 @@ public abstract class AbstractComponent extends AbstractClientConnector */ @Override public void setHeight(String height) { - Size size = parseStringSize(height); + SizeWithUnit size = SizeWithUnit.parseStringSize(height); if (size != null) { setHeight(size.getSize(), size.getUnit()); } else { @@ -1250,55 +1246,6 @@ public abstract class AbstractComponent extends AbstractClientConnector } } - /* - * Returns array with size in index 0 unit in index 1. Null or empty string - * will produce {-1,Unit#PIXELS} - */ - private static Size parseStringSize(String s) { - if (s == null) { - return null; - } - s = s.trim(); - if ("".equals(s)) { - return null; - } - float size = 0; - Unit unit = null; - Matcher matcher = sizePattern.matcher(s); - if (matcher.find()) { - size = Float.parseFloat(matcher.group(1)); - if (size < 0) { - size = -1; - unit = Unit.PIXELS; - } else { - String symbol = matcher.group(2); - unit = Unit.getUnitFromSymbol(symbol); - } - } else { - throw new IllegalArgumentException("Invalid size argument: \"" + s - + "\" (should match " + sizePattern.pattern() + ")"); - } - return new Size(size, unit); - } - - private static class Size implements Serializable { - float size; - Unit unit; - - public Size(float size, Unit unit) { - this.size = size; - this.unit = unit; - } - - public float getSize() { - return size; - } - - public Unit getUnit() { - return unit; - } - } - /* * Actions */ diff --git a/server/src/com/vaadin/ui/AbstractSplitPanel.java b/server/src/com/vaadin/ui/AbstractSplitPanel.java index 09f881cf46..1400bcf092 100644 --- a/server/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/server/src/com/vaadin/ui/AbstractSplitPanel.java @@ -18,16 +18,23 @@ package com.vaadin.ui; import java.io.Serializable; import java.lang.reflect.Method; +import java.util.Collection; import java.util.Iterator; +import org.jsoup.nodes.Element; + import com.vaadin.event.ConnectorEventListener; import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.server.SizeWithUnit; import com.vaadin.server.Sizeable; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelRpc; import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState; import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; import com.vaadin.util.ReflectTools; /** @@ -546,4 +553,136 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { private SplitterState getSplitterState(boolean markAsDirty) { return ((AbstractSplitPanelState) super.getState(markAsDirty)).splitterState; } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.AbstractComponent#synchronizeFromDesign(org.jsoup.nodes + * .Element, com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void synchronizeFromDesign(Element design, + DesignContext designContext) { + // handle default attributes + super.synchronizeFromDesign(design, designContext); + // handle custom attributes, use default values if no explicit value + // set + AbstractSplitPanel def = designContext.getDefaultInstance(this + .getClass()); + // There is no setter for reversed, so it will be handled using + // setSplitPosition. + boolean reversed = DesignAttributeHandler.readAttribute("reversed", + design.attributes(), def.getSplitterState().positionReversed, + Boolean.class); + if (design.hasAttr("split-position")) { + SizeWithUnit splitPosition = SizeWithUnit.parseStringSize( + design.attr("split-position"), def.getSplitPositionUnit()); + setSplitPosition(splitPosition.getSize(), splitPosition.getUnit(), + reversed); + } else { // default value for split position + setSplitPosition(def.getSplitPosition(), + def.getSplitPositionUnit(), reversed); + } + if (design.hasAttr("min-split-position")) { + SizeWithUnit minSplitPosition = SizeWithUnit.parseStringSize( + design.attr("min-split-position"), + def.getMinSplitPositionUnit()); + setMinSplitPosition(minSplitPosition.getSize(), + minSplitPosition.getUnit()); + } else { // default value for min-split-position + setMinSplitPosition(def.getMinSplitPosition(), + def.getMinSplitPositionUnit()); + } + if (design.hasAttr("max-split-position")) { + SizeWithUnit maxSplitPosition = SizeWithUnit.parseStringSize( + design.attr("max-split-position"), + def.getMaxSplitPositionUnit()); + setMaxSplitPosition(maxSplitPosition.getSize(), + maxSplitPosition.getUnit()); + } else { // default value for max-split-position + setMaxSplitPosition(def.getMaxSplitPosition(), + def.getMaxSplitPositionUnit()); + } + // remove current children + removeAllComponents(); + // handle children + if (design.children().size() > 2) { + throw new DesignException( + "A split panel can contain at most two components."); + } + for (Element childElement : design.children()) { + Component childComponent = designContext.createChild(childElement); + if (childElement.hasAttr(":second")) { + setSecondComponent(childComponent); + } else { + addComponent(childComponent); + } + } + } + + @Override + protected Collection getCustomAttributes() { + Collection attributes = super.getCustomAttributes(); + // the setters of the properties do not accept strings such as "20px" + attributes.add("split-position"); + attributes.add("min-split-position"); + attributes.add("max-split-position"); + // no explicit setter for reversed + attributes.add("reversed"); + return attributes; + } + + @Override + public void synchronizeToDesign(Element design, DesignContext designContext) { + // handle default attributes (also clears children and attributes) + super.synchronizeToDesign(design, designContext); + // handle custom attributes (write only if a value is not the + // default value) + AbstractSplitPanel def = designContext.getDefaultInstance(this + .getClass()); + if (getSplitPosition() != def.getSplitPosition() + || !def.getSplitPositionUnit().equals(getSplitPositionUnit())) { + String splitPositionString = asString(getSplitPosition()) + + getSplitPositionUnit(); + design.attr("split-position", splitPositionString); + } + if (getMinSplitPosition() != def.getMinSplitPosition() + || !def.getMinSplitPositionUnit().equals( + getMinSplitPositionUnit())) { + design.attr("min-split-position", asString(getMinSplitPosition()) + + getMinSplitPositionUnit()); + } + if (getMaxSplitPosition() != def.getMaxSplitPosition() + || !def.getMaxSplitPositionUnit().equals( + getMaxSplitPositionUnit())) { + design.attr("max-split-position", asString(getMaxSplitPosition()) + + getMaxSplitPositionUnit()); + } + if (getSplitterState().positionReversed) { + design.attr("reversed", ""); + } + // handle child components + Component firstComponent = getFirstComponent(); + Component secondComponent = getSecondComponent(); + if (firstComponent != null) { + Element childElement = designContext.createNode(firstComponent); + design.appendChild(childElement); + } + if (secondComponent != null) { + Element childElement = designContext.createNode(secondComponent); + if (firstComponent == null) { + childElement.attr(":second", ""); + } + design.appendChild(childElement); + } + } + + private String asString(float number) { + int truncated = (int) number; + if (truncated == number) { + return "" + truncated; + } + return "" + number; + } } diff --git a/server/tests/src/com/vaadin/tests/layoutparser/all-components.html b/server/tests/src/com/vaadin/tests/layoutparser/all-components.html index 9b05f29f9c..71aeeeaa41 100644 --- a/server/tests/src/com/vaadin/tests/layoutparser/all-components.html +++ b/server/tests/src/com/vaadin/tests/layoutparser/all-components.html @@ -39,7 +39,7 @@ - + @@ -86,7 +86,18 @@ - + + + First slot + + + Second slot + + + First slot + Second slot + + diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeFromDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeFromDesign.java new file mode 100644 index 0000000000..b34e8fc3f0 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeFromDesign.java @@ -0,0 +1,173 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.abstractsplitpanel; + +import java.lang.reflect.Method; + +import junit.framework.TestCase; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; + +import com.vaadin.server.Sizeable.Unit; +import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState; +import com.vaadin.ui.AbstractSplitPanel; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Table; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.VerticalSplitPanel; +import com.vaadin.ui.declarative.DesignContext; + +/** + * Tests synchronizing the attributes and children of horizontal and vertical + * split panels from a design. + * + * @author Vaadin Ltd + */ +public class TestSynchronizeFromDesign extends TestCase { + DesignContext ctx; + + @Override + protected void setUp() { + ctx = new DesignContext(); + } + + public void testAttributes() throws Exception { + // Create a design with non-default attributes values. + Element design = createDesign(true, false, true, true); + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + sp.synchronizeFromDesign(design, ctx); + // Check that the attributes are correctly parsed. + assertEquals(20.5f, sp.getSplitPosition()); + assertEquals(Unit.PERCENTAGE, sp.getSplitPositionUnit()); + assertEquals(20f, sp.getMinSplitPosition()); + assertEquals(Unit.PERCENTAGE, sp.getMinSplitPositionUnit()); + assertEquals(50f, sp.getMaxSplitPosition()); + assertEquals(Unit.PIXELS, sp.getMaxSplitPositionUnit()); + assertEquals(true, sp.isLocked()); + checkReversed(sp, true); + // check that the properties get the default values if the design + // does not have attributes corresponding to those properties + design = createDesign(true, true, true, true); + sp.synchronizeFromDesign(design, ctx); + HorizontalSplitPanel def = new HorizontalSplitPanel(); + assertEquals(def.getSplitPosition(), sp.getSplitPosition()); + assertEquals(def.getSplitPositionUnit(), sp.getSplitPositionUnit()); + assertEquals(def.getMinSplitPosition(), sp.getMinSplitPosition()); + assertEquals(def.getMinSplitPositionUnit(), + sp.getMinSplitPositionUnit()); + assertEquals(def.getMaxSplitPosition(), sp.getMaxSplitPosition()); + assertEquals(def.getMaxSplitPositionUnit(), + sp.getMaxSplitPositionUnit()); + assertEquals(def.isLocked(), sp.isLocked()); + checkReversed(sp, false); + } + + public void testWithNoChildren() { + Element design = createDesign(true, false, false, false); + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + sp.synchronizeFromDesign(design, ctx); + assertEquals("Unexpected child count for the split panel.", 0, + sp.getComponentCount()); + } + + public void testWithFirstChild() { + Element design = createDesign(false, false, true, false); + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.synchronizeFromDesign(design, ctx); + assertEquals("Unexpected child count for the split panel.", 1, + sp.getComponentCount()); + Object obj = sp.getFirstComponent(); + assertEquals("Wrong component in split panel.", Table.class, + obj.getClass()); + } + + public void testWithSecondChild() { + Element design = createDesign(true, false, false, true); + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + sp.synchronizeFromDesign(design, ctx); + assertEquals("Unexpected child count for the split panel.", 1, + sp.getComponentCount()); + Object obj = sp.getSecondComponent(); + assertEquals("Wrong component in split panel.", VerticalLayout.class, + obj.getClass()); + } + + public void testWithBothChildren() { + Element design = createDesign(false, false, true, true); + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.synchronizeFromDesign(design, ctx); + assertEquals("Unexpected child count for the split panel.", 2, + sp.getComponentCount()); + Object first = sp.getFirstComponent(); + Object second = sp.getSecondComponent(); + assertEquals("Wrong first component in split panel.", Table.class, + first.getClass()); + assertEquals("Wrong second component in split panel.", + VerticalLayout.class, second.getClass()); + } + + /* + * Creates an html tree node structure representing a split panel and its + * contents. The parameters are used for controlling whether the split panel + * is horizontal or vertical, whether attributes are set for the design and + * whether the split panel should have the first and the second child + * component. + */ + private Element createDesign(boolean horizontal, + boolean useDefaultAttributes, boolean hasFirstChild, + boolean hasSecondChild) { + Attributes attributes = new Attributes(); + if (!useDefaultAttributes) { + attributes.put("split-position", "20.5%"); + // The unitless number should correspond to 20% + attributes.put("min-split-position", "20"); + attributes.put("max-split-position", "50px"); + attributes.put("locked", ""); + attributes.put("reversed", ""); + } + String tagName = horizontal ? "v-horizontal-split-panel" + : "v-vertical-split-panel"; + Element element = new Element(Tag.valueOf(tagName), "", attributes); + // Create the children + if (hasFirstChild) { + Element child = new Element(Tag.valueOf("v-table"), ""); + element.appendChild(child); + } + if (hasSecondChild) { + Element child = new Element(Tag.valueOf("v-vertical-layout"), ""); + if (!hasFirstChild) { + child.attr(":second", ""); + } + element.appendChild(child); + } + return element; + } + + /* + * Checks the reversed property of a split panel. + */ + private void checkReversed(AbstractSplitPanel sp, boolean expected) + throws Exception { + Method getter = AbstractSplitPanel.class + .getDeclaredMethod("getSplitterState"); + getter.setAccessible(true); + SplitterState state = (SplitterState) getter.invoke(sp); + assertEquals("Wrong value for split panel property reversed.", + expected, state.positionReversed); + } +} \ No newline at end of file diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeToDesign.java b/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeToDesign.java new file mode 100644 index 0000000000..3e342a0c89 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractsplitpanel/TestSynchronizeToDesign.java @@ -0,0 +1,151 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.abstractsplitpanel; + +import junit.framework.TestCase; + +import org.jsoup.nodes.Element; + +import com.vaadin.server.Sizeable.Unit; +import com.vaadin.ui.Button; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalSplitPanel; +import com.vaadin.ui.declarative.DesignContext; + +/** + * Tests synchronizing the properties and child components of split panels to a + * design. + * + * @author Vaadin Ltd + */ +public class TestSynchronizeToDesign extends TestCase { + private DesignContext ctx; + + @Override + public void setUp() { + ctx = new DesignContext(); + } + + public void testHorizontalWithDefaultValues() { + // no attributes or child elements should appear + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + Element e = ctx.createNode(sp); + assertEquals("Wrong tag name.", "v-horizontal-split-panel", + e.nodeName()); + assertEquals("The split panel should not have attributes.", 0, e + .attributes().size()); + assertEquals("The split panel should not have children.", 0, e + .children().size()); + } + + public void testVerticalWithAttributes() { + // All defined attributes should be output in the tree node. No child + // components are present in this test. + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.setSplitPosition(27f, Unit.PIXELS, true); + sp.setMinSplitPosition(5.5f, Unit.PERCENTAGE); + sp.setMaxSplitPosition(95, Unit.PERCENTAGE); + sp.setLocked(true); + Element e = ctx.createNode(sp); + assertEquals("Wrong tag name.", "v-vertical-split-panel", e.nodeName()); + assertEquals("Unexpected number of attributes.", 5, e.attributes() + .size()); + assertEquals("Wrong split position.", "27px", e.attr("split-position")); + assertEquals("Wrong minimum split position.", "5.5%", + e.attr("min-split-position")); + assertEquals("Wrong maximum split position.", "95%", + e.attr("max-split-position")); + assertTrue("Unexpected value for locked: " + e.attr("locked"), + "true".equals(e.attr("locked")) || "".equals(e.attr("locked"))); + assertTrue( + "Unexpected value for reversed: " + e.attr("reversed"), + "true".equals(e.attr("reversed")) + || "".equals(e.attr("reversed"))); + } + + public void testHorizontalWithFirstChild() { + // The split panel contains only the first child. + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + sp.setSplitPosition(25f); + sp.setFirstComponent(new Button("First slot")); + Element e = ctx.createNode(sp); + assertEquals("Wrong split position.", "25%", e.attr("split-position")); + assertEquals("Wrong number of child elements.", 1, e.children().size()); + Element eb = e.children().get(0); + assertEquals("Wrong tag name of first child element.", "v-button", + eb.nodeName()); + assertEquals("Wrong text in the button element.", "First slot", + eb.html()); + } + + public void testVerticalWithSecondChild() { + // The split panel contains only the second child. + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.setMinSplitPosition(25f, Unit.PIXELS); + sp.setSecondComponent(new Label("Second slot")); + Element e = ctx.createNode(sp); + assertEquals("Wrong minimum split position.", "25px", + e.attr("min-split-position")); + assertEquals("Wrong number of child elements.", 1, e.children().size()); + Element el = e.children().get(0); + assertEquals("Wrong tag name of child element.", "v-label", + el.nodeName()); + assertEquals("Wrong text in the label element.", "Second slot", + el.html()); + assertTrue("Missing attribute :second in the label element.", + el.hasAttr(":second")); + } + + public void testVerticalWithBothChildren() { + // The split panel has both child components. + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.setFirstComponent(new Button("First slot")); + sp.setSecondComponent(new Label("Second slot")); + Element e = ctx.createNode(sp); + assertEquals("Wrong number of child elements.", 2, e.children().size()); + Element eb = e.children().get(0); + assertEquals("Wrong tag name of first child element.", "v-button", + eb.nodeName()); + assertEquals("Wrong text in the button element.", "First slot", + eb.html()); + Element el = e.children().get(1); + assertEquals("Wrong tag name of second child element.", "v-label", + el.nodeName()); + assertEquals("Wrong text in the label element.", "Second slot", + el.html()); + assertFalse( + "There should be no :second attribute when a split panel has both children.", + el.hasAttr(":second")); + } + + public void testReSynchronize() { + // Test that old children and attributes are removed when an element is + // synchronized to a new component. + VerticalSplitPanel sp = new VerticalSplitPanel(); + sp.setMinSplitPosition(5.5f, Unit.PERCENTAGE); + sp.setMaxSplitPosition(95, Unit.PERCENTAGE); + sp.setFirstComponent(new Button("First slot")); + sp.setSecondComponent(new Label("Second slot")); + Element e = ctx.createNode(sp); + sp = new VerticalSplitPanel(); + sp.synchronizeToDesign(e, ctx); + assertTrue("There should be no attributes in the node.", e.attributes() + .size() == 0); + assertTrue("There should be no child elements.", + e.children().size() == 0); + } +} -- 2.39.5