summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--GWT development mode for vaadin.launch24
-rw-r--r--WebContent/VAADIN/themes/base/boxlayout/boxlayout.css169
-rw-r--r--WebContent/VAADIN/themes/base/button/button.css4
-rw-r--r--WebContent/VAADIN/themes/base/paintable/paintable.css7
-rw-r--r--WebContent/VAADIN/themes/reindeer/layouts/layouts.css16
-rw-r--r--WebContent/VAADIN/themes/runo/orderedlayout/orderedlayout.css10
-rw-r--r--WebContent/VAADIN/themes/tests-components/styles.css17
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComponentLocator.java9
-rw-r--r--src/com/vaadin/terminal/gwt/client/LayoutManager.java31
-rw-r--r--src/com/vaadin/terminal/gwt/client/ServerConnector.java9
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java593
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/HorizontalBoxLayoutConnector.java26
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/PreLayoutListener.java8
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VBoxLayout.java705
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VerticalBoxLayoutConnector.java26
-rw-r--r--tests/testbench/com/vaadin/tests/components/orderedlayout/BoxLayoutTest.java437
-rw-r--r--tests/testbench/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java2
-rw-r--r--tests/testbench/com/vaadin/tests/components/orderedlayout/VaadinTunesLayout.java341
-rw-r--r--tests/testbench/com/vaadin/tests/components/table/TextFieldRelativeWidth.java3
-rw-r--r--tests/testbench/com/vaadin/tests/themes/LiferayThemeTest.java37
22 files changed, 2469 insertions, 17 deletions
diff --git a/GWT development mode for vaadin.launch b/GWT development mode for vaadin.launch
new file mode 100644
index 0000000000..5195348bf5
--- /dev/null
+++ b/GWT development mode for vaadin.launch
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/vaadin"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/Users/jouni/.ivy2/cache/com.google.gwt/gwt-dev/jars/gwt-dev-2.4.0.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/Users/jouni/.ivy2/cache/com.google.gwt/gwt-user/jars/gwt-user-2.4.0.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;vaadin&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/vaadin/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/vaadin/tests/testbench&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/vaadin/tests/client-side&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/vaadin/tests/server-side&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-noserver -war WebContent/VAADIN/widgetsets com.vaadin.terminal.gwt.DefaultWidgetSet -startupUrl http://localhost:8080/vaadin -bindAddress 0.0.0.0"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="vaadin"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx512M -XX:MaxPermSize=256M"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="/Users/jouni/Documents/Work/Dev/Vaadin/vaadin"/>
+</launchConfiguration>
diff --git a/WebContent/VAADIN/themes/base/boxlayout/boxlayout.css b/WebContent/VAADIN/themes/base/boxlayout/boxlayout.css
new file mode 100644
index 0000000000..bbfad98582
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/boxlayout/boxlayout.css
@@ -0,0 +1,169 @@
+/*
+TODO
+- separate styles to proper places
+- decide a good class name structure for core layouts (e.g. 'v-layout', 'v-vertical', 'v-grid' etc.)
+- use !important in carefully selected places to prevent accidental layout breakage by custom theming (e.g. alignments should be forced)
+
+*/
+
+.v-boxlayout.v-margin-top {padding-top: 12px;}
+.v-boxlayout.v-margin-right {padding-right: 12px;}
+.v-boxlayout.v-margin-bottom {padding-bottom: 12px;}
+.v-boxlayout.v-margin-left {padding-left: 12px;}
+
+.v-spacing {
+ width: 6px;
+ height: 6px;
+}
+
+.v-boxlayout {
+ display: inline-block;
+}
+
+.v-boxlayout.v-horizontal {
+ white-space: nowrap !important;
+}
+
+.v-boxlayout > .v-expand {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+}
+
+.v-slot,
+.v-spacing {
+ display: inline-block;
+ white-space: normal;
+ vertical-align: top;
+}
+
+/* Clear any floats inside the slot, to prevent unwanted collapsing */
+.v-slot:after {
+ content: "";
+ display: inline-block;
+ clear: both;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+}
+
+.v-vertical > .v-slot,
+.v-vertical > .v-expand > .v-slot {
+ display: block;
+ clear: both;
+}
+
+.v-horizontal > .v-slot,
+.v-horizontal > .v-expand > .v-slot {
+ height: 100%;
+}
+
+.v-vertical > .v-spacing,
+.v-vertical > .v-expand > .v-spacing {
+ width: 0;
+ display: block;
+ clear: both;
+}
+
+.v-horizontal > .v-spacing,
+.v-horizontal > .v-expand > .v-spacing {
+ height: 0;
+}
+
+.v-align-middle:before,
+.v-align-bottom:before,
+.v-expand > .v-align-middle:before,
+.v-expand > .v-align-bottom:before {
+ content: "";
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+ width: 0;
+}
+
+.v-align-middle,
+.v-align-bottom {
+ white-space: nowrap;
+}
+
+.v-align-middle > .v-connector,
+.v-align-bottom > .v-connector {
+ display: inline-block;
+ /* TODO this is a bit tricky, since it will override component defaults in some cases */
+ white-space: normal;
+}
+
+.v-align-middle > .v-connector {
+ vertical-align: middle;
+}
+
+.v-align-bottom > .v-connector {
+ vertical-align: bottom;
+}
+
+.v-align-center {
+ text-align: center;
+}
+
+.v-align-center > .v-connector {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.v-align-right {
+ text-align: right;
+}
+
+.v-align-right > .v-connector {
+ margin-left: auto;
+}
+
+.v-has-caption {
+ display: inline-block;
+}
+
+.v-caption {
+ display: inline-block; /* Force default width to zero */
+ overflow: visible;
+ vertical-align: middle;
+}
+
+.v-caption-on-left,
+.v-caption-on-right {
+ white-space: nowrap;
+}
+
+.v-caption-on-top > .v-caption,
+.v-caption-on-bottom > .v-caption {
+ display: block;
+}
+
+.v-caption-on-left > .v-caption {
+ padding-right: .5em;
+}
+
+.v-caption-on-right > .v-caption {
+ padding-left: .5em;
+}
+
+.v-caption-on-left > .v-connector,
+.v-caption-on-right > .v-connector {
+ display: inline-block;
+ vertical-align: middle;
+ /* TODO this is a bit tricky, since it will override component defaults in some cases */
+ white-space: normal;
+}
+
+.v-has-caption.v-has-width > .v-connector {
+ width: 100% !important;
+}
+
+.v-has-caption.v-has-height > .v-connector {
+ height: 100% !important;
+}
+
+.v-errorindicator {
+ vertical-align: middle;
+} \ No newline at end of file
diff --git a/WebContent/VAADIN/themes/base/button/button.css b/WebContent/VAADIN/themes/base/button/button.css
index 2e14d59a90..8424a3e59f 100644
--- a/WebContent/VAADIN/themes/base/button/button.css
+++ b/WebContent/VAADIN/themes/base/button/button.css
@@ -4,7 +4,7 @@
.v-button {
display: inline-block;
zoom: 1;
- text-align: center;
+ text-align: center !important;
text-decoration: none;
border: 2px outset #ddd;
background: #eee;
@@ -84,7 +84,7 @@
* NativeButton styles (html button element)
* -------------------------------------- */
.v-nativebutton {
- text-align: center;
+ text-align: center !important;
cursor: pointer;
white-space: nowrap;
margin: 0;
diff --git a/WebContent/VAADIN/themes/base/paintable/paintable.css b/WebContent/VAADIN/themes/base/paintable/paintable.css
index 36a3fe3455..9d700fd4ee 100644
--- a/WebContent/VAADIN/themes/base/paintable/paintable.css
+++ b/WebContent/VAADIN/themes/base/paintable/paintable.css
@@ -1,4 +1,7 @@
.v-connector {
- box-sizing: border-box;
+ -webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-} \ No newline at end of file
+ box-sizing: border-box;
+ text-align: left;
+ display: inline-block;
+}
diff --git a/WebContent/VAADIN/themes/reindeer/layouts/layouts.css b/WebContent/VAADIN/themes/reindeer/layouts/layouts.css
index 528d4b9174..650247bade 100644
--- a/WebContent/VAADIN/themes/reindeer/layouts/layouts.css
+++ b/WebContent/VAADIN/themes/reindeer/layouts/layouts.css
@@ -1,21 +1,25 @@
.v-orderedlayout-margin-top,
.v-horizontallayout-margin-top,
-.v-verticallayout-margin-top {
+.v-verticallayout-margin-top,
+.v-boxlayout.v-margin-top {
padding-top: 18px;
}
.v-orderedlayout-margin-right,
.v-horizontallayout-margin-right,
-.v-verticallayout-margin-right {
+.v-verticallayout-margin-right,
+.v-boxlayout.v-margin-right {
padding-right: 18px;
}
.v-orderedlayout-margin-bottom,
.v-horizontallayout-margin-bottom,
-.v-verticallayout-margin-bottom {
+.v-verticallayout-margin-bottom,
+.v-boxlayout.v-margin-bottom {
padding-bottom: 18px;
}
.v-orderedlayout-margin-left,
.v-horizontallayout-margin-left,
-.v-verticallayout-margin-left {
+.v-verticallayout-margin-left,
+.v-boxlayout.v-margin-left {
padding-left: 18px;
}
.v-orderedlayout-spacing-on,
@@ -24,6 +28,10 @@
padding-top: 7px;
padding-left: 6px;
}
+.v-spacing {
+ height: 7px;
+ width: 6px;
+}
/* Different for historical reasons: previously was inherited directly from Base theme */
/* TODO unify these values in version 7 */
.v-gridlayout-margin-top {
diff --git a/WebContent/VAADIN/themes/runo/orderedlayout/orderedlayout.css b/WebContent/VAADIN/themes/runo/orderedlayout/orderedlayout.css
index 306292d843..f70e285729 100644
--- a/WebContent/VAADIN/themes/runo/orderedlayout/orderedlayout.css
+++ b/WebContent/VAADIN/themes/runo/orderedlayout/orderedlayout.css
@@ -1,7 +1,8 @@
.v-orderedlayout-margin-top,
.v-horizontallayout-margin-top,
.v-verticallayout-margin-top,
-.v-csslayout-margin-top {
+.v-csslayout-margin-top,
+.v-boxlayout.v-margin-top {
padding-top: 15px;
}
.v-orderedlayout-margin-right,
@@ -13,7 +14,8 @@
.v-orderedlayout-margin-bottom,
.v-horizontallayout-margin-bottom,
.v-verticallayout-margin-bottom,
-.v-csslayout-margin-bottom {
+.v-csslayout-margin-bottom,
+.v-boxlayout.v-margin-bottom {
padding-bottom: 15px;
}
.v-orderedlayout-margin-left,
@@ -28,6 +30,10 @@
padding-top: 8px;
padding-left: 8px;
}
+.v-spacing {
+ width: 8px;
+ height: 8px;
+}
.v-verticallayout-darker,
.v-horizontallayout-darker,
.v-gridlayout-darker,
diff --git a/WebContent/VAADIN/themes/tests-components/styles.css b/WebContent/VAADIN/themes/tests-components/styles.css
index c38f32f132..45a96b6d16 100644
--- a/WebContent/VAADIN/themes/tests-components/styles.css
+++ b/WebContent/VAADIN/themes/tests-components/styles.css
@@ -36,4 +36,21 @@
.v-table-row-tables-test-cell-style-red-row,
.v-table-cell-content-tables-test-cell-style-red-row {
background: #f00;
+}
+
+.v-boxlayout.test {
+ border: 1px solid #ddd;
+}
+
+.v-boxlayout.test .target {
+ outline: 2px dashed blue;
+}
+
+.fieldset {
+ padding: .5em 1em;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
+ -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
+ border-radius: .5em;
+ background: rgba(0,0,0,.02);
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index f65b4c51e7..518f91df7d 100644
--- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
@@ -16,6 +16,14 @@
name="com.vaadin.terminal.gwt.DefaultWidgetSetBrowserSpecificOverrides" />
<source path="client" />
+
+ <!-- TODO only for development -->
+ <replace-with class="com.vaadin.terminal.gwt.client.ui.VerticalBoxLayoutConnector">
+ <when-type-is class="com.vaadin.terminal.gwt.client.ui.orderedlayout.VerticalLayoutConnector" />
+ </replace-with>
+ <replace-with class="com.vaadin.terminal.gwt.client.ui.HorizontalBoxLayoutConnector">
+ <when-type-is class="com.vaadin.terminal.gwt.client.ui.orderedlayout.HorizontalLayoutConnector" />
+ </replace-with>
<!-- Use own Scheduler implementation to be able to track if commands are
running -->
diff --git a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
index d847d49e6f..a4df6c6cc4 100644
--- a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
+++ b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java
@@ -13,6 +13,7 @@ import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ui.SubPartAware;
+import com.vaadin.terminal.gwt.client.ui.VBoxLayout;
import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout;
import com.vaadin.terminal.gwt.client.ui.orderedlayout.VMeasuringOrderedLayout;
import com.vaadin.terminal.gwt.client.ui.root.VRoot;
@@ -483,6 +484,14 @@ public class ComponentLocator {
// is always 0 which indicates the widget in the active tab
widgetPosition = 0;
}
+ if ("VVerticalLayout".equals(widgetClassName)
+ || "VHorizontalLayout".equals(widgetClassName)) {
+ widgetClassName = "VBoxLayout";
+ }
+ if (w instanceof VBoxLayout
+ && "ChildComponentContainer".equals(widgetClassName)) {
+ widgetClassName = "VBoxLayout$Slot";
+ }
/*
* The new grid and ordered layotus do not contain
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
index b182429902..a40a2e841f 100644
--- a/src/com/vaadin/terminal/gwt/client/LayoutManager.java
+++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java
@@ -18,6 +18,7 @@ import com.google.gwt.user.client.Timer;
import com.vaadin.terminal.gwt.client.MeasuredSize.MeasureResult;
import com.vaadin.terminal.gwt.client.ui.ManagedLayout;
import com.vaadin.terminal.gwt.client.ui.PostLayoutListener;
+import com.vaadin.terminal.gwt.client.ui.PreLayoutListener;
import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout;
import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent;
import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener;
@@ -130,7 +131,7 @@ public class LayoutManager {
return;
}
measuredSize.removeDependent(owner.getConnectorId());
- stopMeasuringIfUnecessary(element);
+ stopMeasuringIfUnnecessary(element);
}
public boolean isLayoutRunning() {
@@ -152,7 +153,7 @@ public class LayoutManager {
}
}
- private void layoutLater() {
+ public void layoutLater() {
if (!layoutPending) {
layoutPending = true;
layoutTimer.schedule(100);
@@ -176,10 +177,21 @@ public class LayoutManager {
private void doLayout() {
VConsole.log("Starting layout phase");
+ Duration totalDuration = new Duration();
+
+ int preLayoutStart = totalDuration.elapsedMillis();
+ for (ComponentConnector connector : connection.getConnectorMap()
+ .getComponentConnectors()) {
+ if (connector instanceof PreLayoutListener) {
+ ((PreLayoutListener) connector).preLayout();
+ }
+ }
+ VConsole.log("Invoke pre layout listeners in "
+ + (totalDuration.elapsedMillis() - preLayoutStart) + " ms");
+
Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>();
int passes = 0;
- Duration totalDuration = new Duration();
for (ManagedLayout layout : needsHorizontalLayout) {
currentDependencyTree.setNeedsHorizontalLayout(layout, true);
@@ -637,6 +649,14 @@ public class LayoutManager {
return getMeasuredSize(element, nullSize).getMarginLeft();
}
+ public int getMarginWidth(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginWidth();
+ }
+
+ public int getMarginHeight(Element element) {
+ return getMeasuredSize(element, nullSize).getMarginHeight();
+ }
+
public void reportOuterHeight(ComponentConnector component, int outerHeight) {
MeasuredSize measuredSize = getMeasuredSize(component);
if (isLayoutRunning()) {
@@ -713,12 +733,12 @@ public class LayoutManager {
listeners.remove(listener);
if (listeners.isEmpty()) {
elementResizeListeners.remove(element);
- stopMeasuringIfUnecessary(element);
+ stopMeasuringIfUnnecessary(element);
}
}
}
- private void stopMeasuringIfUnecessary(Element element) {
+ private void stopMeasuringIfUnnecessary(Element element) {
if (!needsMeasure(element)) {
measuredNonConnectorElements.remove(element);
setMeasuredSize(element, null);
@@ -737,4 +757,5 @@ public class LayoutManager {
public void setEverythingNeedsMeasure() {
everythingNeedsMeasure = true;
}
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ServerConnector.java b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
index 559238b512..f179b29054 100644
--- a/src/com/vaadin/terminal/gwt/client/ServerConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ServerConnector.java
@@ -91,6 +91,15 @@ public interface ServerConnector extends Connector {
public HandlerRegistration addStateChangeHandler(StateChangeHandler handler);
/**
+ * Removes the specified StateChangeHandler from this connector. The handler
+ * will no longer be notified of the state is updated by the server.
+ *
+ * @param handler
+ * The handler that should be removed.
+ */
+ public void removeStateChangeHandler(StateChangeHandler handler);
+
+ /**
* Sends the given event to all registered handlers.
*
* @param event
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java
new file mode 100644
index 0000000000..c58241c608
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java
@@ -0,0 +1,593 @@
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Unit;
+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.ComponentConnector;
+import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.terminal.gwt.client.Paintable;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.ValueMap;
+import com.vaadin.terminal.gwt.client.communication.RpcProxy;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler;
+import com.vaadin.terminal.gwt.client.ui.VBoxLayout.CaptionPosition;
+import com.vaadin.terminal.gwt.client.ui.VBoxLayout.Slot;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent;
+import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutServerRPC;
+import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutState;
+
+public abstract class AbstractBoxLayoutConnector extends
+ AbstractLayoutConnector implements Paintable, PreLayoutListener,
+ PostLayoutListener {
+
+ AbstractOrderedLayoutServerRPC rpc;
+
+ private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
+ this) {
+
+ @Override
+ protected ComponentConnector getChildComponent(Element element) {
+ return Util.getConnectorForElement(getConnection(), getWidget(),
+ element);
+ }
+
+ @Override
+ protected LayoutClickRPC getLayoutClickRPC() {
+ return rpc;
+ };
+
+ };
+
+ @Override
+ public void init() {
+ rpc = RpcProxy.create(AbstractOrderedLayoutServerRPC.class, this);
+ getWidget().setLayoutManager(getLayoutManager());
+ }
+
+ @Override
+ public AbstractOrderedLayoutState getState() {
+ return (AbstractOrderedLayoutState) super.getState();
+ }
+
+ @Override
+ protected Widget createWidget() {
+ return GWT.create(VBoxLayout.class);
+ }
+
+ @Override
+ public VBoxLayout getWidget() {
+ return (VBoxLayout) super.getWidget();
+ }
+
+ /**
+ * For bookkeeping. Used to determine if extra calculations are needed for
+ * horizontal layout.
+ */
+ private HashSet<ComponentConnector> hasVerticalAlignment = new HashSet<ComponentConnector>();
+
+ /**
+ * For bookkeeping. Used to determine if extra calculations are needed for
+ * horizontal layout.
+ */
+ private HashSet<ComponentConnector> hasRelativeHeight = new HashSet<ComponentConnector>();
+
+ /**
+ * For bookkeeping. Used to determine if extra calculations are needed for
+ * horizontal layout.
+ */
+ private HashSet<ComponentConnector> hasExpandRatio = new HashSet<ComponentConnector>();
+
+ /**
+ * For bookkeeping. Used in extra calculations for horizontal layout.
+ */
+ private HashSet<Element> needsMeasure = new HashSet<Element>();
+
+ /**
+ * For bookkeeping. Used in extra calculations for horizontal layout.
+ */
+ private HashMap<Element, Integer> childElementHeight = new HashMap<Element, Integer>();
+
+ /**
+ * For bookkeeping. Used in extra calculations for horizontal layout.
+ */
+ private HashMap<Element, Integer> childCaptionElementHeight = new HashMap<Element, Integer>();
+
+ // For debugging
+ private static int resizeCount = 0;
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ clickEventHandler.handleEventHandlerRegistration();
+
+ VBoxLayout layout = getWidget();
+
+ ValueMap expandRatios = uidl.getMapAttribute("expandRatios");
+ ValueMap alignments = uidl.getMapAttribute("alignments");
+
+ for (ComponentConnector child : getChildren()) {
+ Slot slot = layout.getSlot(child.getWidget());
+ String pid = child.getConnectorId();
+
+ AlignmentInfo alignment;
+ if (alignments.containsKey(pid)) {
+ alignment = new AlignmentInfo(alignments.getInt(pid));
+ if (alignment.isVerticalCenter() || alignment.isBottom()) {
+ hasVerticalAlignment.add(child);
+ } else {
+ hasVerticalAlignment.remove(child);
+ }
+ } else {
+ alignment = AlignmentInfo.TOP_LEFT;
+ hasVerticalAlignment.remove(child);
+ }
+ slot.setAlignment(alignment);
+
+ double expandRatio;
+ // TODO discuss the layout specs, is this what we want: distribute
+ // extra space equally if no expand ratios are specified inside a
+ // layout with specified size
+ if (expandRatios.getKeySet().size() == 0
+ && ((!getWidget().vertical && !isUndefinedHeight()) || !isUndefinedWidth())) {
+ expandRatio = 1;
+ hasExpandRatio.add(child);
+ } else if (expandRatios.containsKey(pid)
+ && expandRatios.getRawNumber(pid) > 0) {
+ expandRatio = expandRatios.getRawNumber(pid);
+ hasExpandRatio.add(child);
+ } else {
+ expandRatio = -1;
+ hasExpandRatio.remove(child);
+ getLayoutManager().addElementResizeListener(
+ child.getWidget().getElement(),
+ childComponentResizeListener);
+ if (slot.hasCaption()) {
+ getLayoutManager()
+ .addElementResizeListener(slot.getCaptionElement(),
+ slotCaptionResizeListener);
+ }
+ }
+ slot.setExpandRatio(expandRatio);
+
+ if (slot.getSpacingElement() != null) {
+ getLayoutManager().addElementResizeListener(
+ slot.getSpacingElement(), spacingResizeListener);
+ }
+
+ }
+
+ if (needsExpand()) {
+ updateExpand();
+ } else {
+ getWidget().clearExpand();
+ }
+
+ }
+
+ public void updateCaption(ComponentConnector child) {
+ Slot slot = getWidget().getSlot(child.getWidget());
+
+ String caption = child.getState().getCaption();
+ String iconUrl = child.getState().getIcon() != null ? child.getState()
+ .getIcon().getURL() : null;
+ List<String> styles = child.getState().getStyles();
+ String error = child.getState().getErrorMessage();
+ boolean required = false;
+ if (child instanceof AbstractFieldConnector) {
+ required = ((AbstractFieldConnector) child).isRequired();
+ }
+ boolean enabled = child.getState().isEnabled();
+ // TODO Description is handled from somewhere else?
+
+ slot.setCaption(caption, iconUrl, styles, error, required, enabled);
+
+ slot.setRelativeWidth(child.isRelativeWidth());
+ slot.setRelativeHeight(child.isRelativeHeight());
+
+ if (slot.hasCaption()) {
+ CaptionPosition pos = slot.getCaptionPosition();
+ getLayoutManager().addElementResizeListener(
+ slot.getCaptionElement(), slotCaptionResizeListener);
+ if (child.isRelativeHeight()
+ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
+ getWidget().updateCaptionOffset(slot.getCaptionElement());
+ } else if (child.isRelativeWidth()
+ && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
+ getWidget().updateCaptionOffset(slot.getCaptionElement());
+ }
+ } else {
+ getLayoutManager().removeElementResizeListener(
+ slot.getCaptionElement(), slotCaptionResizeListener);
+ }
+
+ if (!slot.hasCaption()) {
+ childCaptionElementHeight.remove(child.getWidget().getElement());
+ }
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ super.onConnectorHierarchyChange(event);
+
+ List<ComponentConnector> previousChildren = event.getOldChildren();
+ int currentIndex = 0;
+ VBoxLayout layout = getWidget();
+
+ for (ComponentConnector child : getChildren()) {
+ Widget childWidget = child.getWidget();
+ Slot slot = layout.getSlot(childWidget);
+ if (slot.getParent() != layout) {
+ child.addStateChangeHandler(childStateChangeHandler);
+ }
+ layout.addOrMoveSlot(slot, currentIndex++);
+ }
+
+ for (ComponentConnector child : previousChildren) {
+ if (child.getParent() != this) {
+ Slot slot = layout.getSlot(child.getWidget());
+ hasVerticalAlignment.remove(child);
+ hasRelativeHeight.remove(child);
+ hasExpandRatio.remove(child);
+ needsMeasure.remove(child.getWidget().getElement());
+ childElementHeight.remove(child.getWidget().getElement());
+ childCaptionElementHeight
+ .remove(child.getWidget().getElement());
+ getLayoutManager().removeElementResizeListener(
+ child.getWidget().getElement(),
+ childComponentResizeListener);
+ if (slot.hasCaption()) {
+ getLayoutManager()
+ .removeElementResizeListener(
+ slot.getCaptionElement(),
+ slotCaptionResizeListener);
+ }
+ if (slot.getSpacingElement() != null) {
+ getLayoutManager().removeElementResizeListener(
+ slot.getSpacingElement(), spacingResizeListener);
+ }
+ child.removeStateChangeHandler(childStateChangeHandler);
+ layout.removeSlot(child.getWidget());
+ }
+ }
+
+ if (needsExpand()) {
+ getWidget().updateExpand();
+ } else {
+ getWidget().clearExpand();
+ }
+
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ getWidget().setMargin(new VMarginInfo(getState().getMarginsBitmask()));
+ getWidget().setSpacing(getState().isSpacing());
+
+ if (needsFixedHeight()) {
+ setLayoutHeightListener(true);
+ } else {
+ setLayoutHeightListener(false);
+ }
+
+ }
+
+ StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ ComponentConnector child = (ComponentConnector) stateChangeEvent
+ .getConnector();
+
+ // We need to update the slot size if the component size is changed
+ // to relative
+ Slot slot = getWidget().getSlot(child.getWidget());
+ slot.setRelativeWidth(child.isRelativeWidth());
+ slot.setRelativeHeight(child.isRelativeHeight());
+
+ // For relative sized widgets, we need to set the caption offset
+ if (slot.hasCaption()) {
+ CaptionPosition pos = slot.getCaptionPosition();
+ if (child.isRelativeHeight()
+ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
+ getWidget().updateCaptionOffset(slot.getCaptionElement());
+ } else if (child.isRelativeWidth()
+ && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
+ getWidget().updateCaptionOffset(slot.getCaptionElement());
+ }
+ }
+
+ // TODO 'needsExpand' might return false during the first render,
+ // since updateFromUidl is called last
+
+ // If the slot has caption, we need to listen for it's size changes
+ // in order to update the padding/margin offset for relative sized
+ // components
+ if ((child.isRelativeHeight() || needsFixedHeight())
+ && slot.hasCaption()) {
+ getLayoutManager().addElementResizeListener(
+ slot.getCaptionElement(), slotCaptionResizeListener);
+ } else if (!needsExpand()) {
+ // TODO recheck if removing the listener here breaks anything.
+ // Should be cleaned up.
+ // getLayoutManager().removeElementResizeListener(
+ // slot.getCaptionElement(), slotCaptionResizeListener);
+ }
+
+ if (slot.getSpacingElement() != null && needsExpand()) {
+ // Spacing is on
+ getLayoutManager().addElementResizeListener(
+ slot.getSpacingElement(), spacingResizeListener);
+ } else if (slot.getSpacingElement() != null) {
+ getLayoutManager().addElementResizeListener(
+ slot.getSpacingElement(), spacingResizeListener);
+ }
+
+ if (child.isRelativeHeight()) {
+ hasRelativeHeight.add(child);
+ needsMeasure.remove(child.getWidget().getElement());
+ } else {
+ hasRelativeHeight.remove(child);
+ needsMeasure.add(child.getWidget().getElement());
+ }
+
+ if (needsFixedHeight()) {
+ getLayoutManager().addElementResizeListener(
+ child.getWidget().getElement(),
+ childComponentResizeListener);
+ } else if (!needsExpand()) {
+ getLayoutManager().removeElementResizeListener(
+ child.getWidget().getElement(),
+ childComponentResizeListener);
+ }
+
+ if (needsFixedHeight()) {
+ setLayoutHeightListener(true);
+ } else {
+ setLayoutHeightListener(false);
+ }
+
+ }
+ };
+
+ private boolean needsFixedHeight() {
+ if (!getWidget().vertical
+ && isUndefinedHeight()
+ && (hasRelativeHeight.size() > 0 || hasVerticalAlignment.size() > 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean needsExpand() {
+ boolean canApplyExpand = (getWidget().vertical && !isUndefinedHeight())
+ || !isUndefinedWidth();
+ return hasExpandRatio.size() > 0 && canApplyExpand;
+ }
+
+ public void preLayout() {
+ resizeCount = 0;
+ if (needsFixedHeight()) {
+ getWidget().clearHeight();
+ getLayoutManager().setNeedsMeasure(this);
+ }
+ }
+
+ public void postLayout() {
+ if (needsFixedHeight()) {
+ // Re-measure all elements that are available
+ for (Element el : needsMeasure) {
+ childElementHeight.put(el, getLayoutManager()
+ .getOuterHeight(el));
+
+ // Element captionElement = el.getParentElement()
+ // .getFirstChildElement().cast();
+ // if (captionElement.getClassName().contains("v-caption")) {
+ // childCaptionElementHeight.put(el, getLayoutManager()
+ // .getOuterHeight(captionElement));
+ // }
+ }
+
+ String h = getWidget().getElement().getStyle().getHeight();
+ if (h == null || h.equals("")) {
+ int height = getLayoutManager().getOuterHeight(
+ getWidget().getElement())
+ - getLayoutManager().getMarginHeight(
+ getWidget().getElement());
+ // int height = getMaxHeight();
+ getWidget().getElement().getStyle().setHeight(height, Unit.PX);
+ }
+ }
+ // System.err.println("Element resize listeners fired for " +
+ // resizeCount
+ // + " times");
+ }
+
+ private ElementResizeListener layoutResizeListener = new ElementResizeListener() {
+ public void onElementResize(ElementResizeEvent e) {
+ resizeCount++;
+ updateLayoutHeight();
+ if (needsExpand() && (isUndefinedHeight() || isUndefinedWidth())) {
+ updateExpand();
+ }
+ }
+ };
+
+ private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() {
+ public void onElementResize(ElementResizeEvent e) {
+ resizeCount++;
+
+ Element captionElement = (Element) e.getElement().cast();
+
+ CaptionPosition pos = getWidget().getCaptionPositionFromElement(
+ (Element) captionElement.getParentElement().cast());
+
+ Element widgetElement = captionElement.getParentElement()
+ .getLastChild().cast();
+ if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) {
+ widgetElement = captionElement.getParentElement()
+ .getFirstChildElement().cast();
+ }
+
+ if (captionElement == widgetElement) {
+ // Caption element already detached
+ getLayoutManager().removeElementResizeListener(captionElement,
+ slotCaptionResizeListener);
+ return;
+ }
+
+ String widgetWidth = widgetElement.getStyle().getWidth();
+ String widgetHeight = widgetElement.getStyle().getHeight();
+
+ if (widgetHeight.endsWith("%")
+ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
+ getWidget().updateCaptionOffset(captionElement);
+ } else if (widgetWidth.endsWith("%")
+ && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
+ getWidget().updateCaptionOffset(captionElement);
+ }
+
+ int h = getLayoutManager().getOuterHeight(captionElement)
+ - getLayoutManager().getMarginHeight(captionElement);
+ childCaptionElementHeight.put(widgetElement, h);
+
+ updateLayoutHeight();
+
+ if (needsExpand()) {
+ updateExpand();
+ }
+ }
+ };
+
+ private ElementResizeListener childComponentResizeListener = new ElementResizeListener() {
+ public void onElementResize(ElementResizeEvent e) {
+ resizeCount++;
+ int h = getLayoutManager().getOuterHeight(e.getElement());
+ childElementHeight.put((Element) e.getElement().cast(), h);
+ updateLayoutHeight();
+
+ if (needsExpand()) {
+ updateExpand();
+ }
+ }
+ };
+
+ private ElementResizeListener spacingResizeListener = new ElementResizeListener() {
+ public void onElementResize(ElementResizeEvent e) {
+ resizeCount++;
+ if (needsExpand()) {
+ updateExpand();
+ }
+ }
+ };
+
+ private void updateLayoutHeight() {
+ if (needsFixedHeight() && childElementHeight.size() > 0) {
+ int h = getMaxHeight();
+ h += getLayoutManager().getBorderHeight(getWidget().getElement())
+ + getLayoutManager().getPaddingHeight(
+ getWidget().getElement());
+ getWidget().getElement().getStyle().setHeight(h, Unit.PX);
+ getLayoutManager().setNeedsMeasure(this);
+ }
+ }
+
+ private void updateExpand() {
+ // System.out.println("All sizes: "
+ // + childElementHeight.values().toString() + " - Caption sizes: "
+ // + childCaptionElementHeight.values().toString());
+ getWidget().updateExpand();
+ }
+
+ private int getMaxHeight() {
+ int highestNonRelative = -1;
+ int highestRelative = -1;
+ for (Element el : childElementHeight.keySet()) {
+ // TODO would be more efficient to measure the slot element if both
+ // caption and child widget elements need to be measured. Keeping
+ // track of what to measure is the most difficult part of this
+ // layout.
+ CaptionPosition pos = getWidget().getCaptionPositionFromElement(
+ (Element) el.getParentElement().cast());
+ if (needsMeasure.contains(el)) {
+ int h = childElementHeight.get(el);
+ String sHeight = el.getStyle().getHeight();
+ // Only add the caption size to the height of the slot if
+ // coption position is top or bottom
+ if (childCaptionElementHeight.containsKey(el)
+ && (sHeight == null || !sHeight.endsWith("%"))
+ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
+ h += childCaptionElementHeight.get(el);
+ }
+ if (h > highestNonRelative) {
+ highestNonRelative = h;
+ }
+ } else {
+ int h = childElementHeight.get(el);
+ if (childCaptionElementHeight.containsKey(el)
+ && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
+ h += childCaptionElementHeight.get(el);
+ }
+ if (h > highestRelative) {
+ highestRelative = h;
+ }
+ }
+ }
+ return highestNonRelative > -1 ? highestNonRelative : highestRelative;
+ }
+
+ @Override
+ public void onUnregister() {
+ // Cleanup all ElementResizeListeners
+
+ getLayoutManager().removeElementResizeListener(
+ getWidget().getElement(), layoutResizeListener);
+
+ for (int i = 0; i < getWidget().getWidgetCount(); i++) {
+ // TODO unsafe
+ Slot slot = (Slot) getWidget().getWidget(i);
+
+ if (slot.hasCaption()) {
+ getLayoutManager().removeElementResizeListener(
+ slot.getCaptionElement(), slotCaptionResizeListener);
+ }
+
+ if (slot.getSpacingElement() != null) {
+ getLayoutManager().removeElementResizeListener(
+ slot.getSpacingElement(), spacingResizeListener);
+ }
+
+ getLayoutManager()
+ .removeElementResizeListener(slot.getWidget().getElement(),
+ childComponentResizeListener);
+
+ }
+
+ super.onUnregister();
+ }
+
+ private void setLayoutHeightListener(boolean add) {
+ if (add) {
+ getLayoutManager().addElementResizeListener(
+ getWidget().getElement(), layoutResizeListener);
+ } else {
+ getLayoutManager().removeElementResizeListener(
+ getWidget().getElement(), layoutResizeListener);
+ if (!needsExpand()) {
+ childElementHeight.clear();
+ childCaptionElementHeight.clear();
+ }
+ }
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
index ef74228ae8..e205723e64 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
@@ -163,6 +163,10 @@ public abstract class AbstractConnector implements ServerConnector,
.addHandler(StateChangeEvent.TYPE, handler);
}
+ public void removeStateChangeHandler(StateChangeHandler handler) {
+ ensureHandlerManager().removeHandler(StateChangeEvent.TYPE, handler);
+ }
+
public void onStateChanged(StateChangeEvent stateChangeEvent) {
if (debugLogging) {
VConsole.log("State change event for "
diff --git a/src/com/vaadin/terminal/gwt/client/ui/HorizontalBoxLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/HorizontalBoxLayoutConnector.java
new file mode 100644
index 0000000000..20d9ca9d30
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/HorizontalBoxLayoutConnector.java
@@ -0,0 +1,26 @@
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.HorizontalLayout;
+
+@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
+public class HorizontalBoxLayoutConnector extends AbstractBoxLayoutConnector {
+
+ @Override
+ public void init() {
+ super.init();
+ getWidget().setVertical(false);
+ }
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // TODO remove when Vaadin style name handling is improved so that it
+ // won't override extra client side style names
+ getWidget().setVertical(false);
+ super.updateFromUIDL(uidl, client);
+ getWidget().setVertical(false);
+ }
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/PreLayoutListener.java b/src/com/vaadin/terminal/gwt/client/ui/PreLayoutListener.java
new file mode 100644
index 0000000000..9a087bb46d
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/PreLayoutListener.java
@@ -0,0 +1,8 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.ui;
+
+public interface PreLayoutListener {
+ public void preLayout();
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VBoxLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VBoxLayout.java
new file mode 100644
index 0000000000..540ad58c2b
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/VBoxLayout.java
@@ -0,0 +1,705 @@
+package com.vaadin.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
+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.FlowPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.terminal.gwt.client.LayoutManager;
+
+public class VBoxLayout extends FlowPanel {
+
+ private static final String ALIGN_CLASS_PREFIX = "v-align-";
+
+ protected boolean spacing = false;
+
+ protected boolean vertical = true;
+
+ protected boolean definedHeight = false;
+
+ private Map<Widget, Slot> widgetToSlot = new HashMap<Widget, Slot>();
+
+ private LayoutManager layoutManager;
+
+ public VBoxLayout() {
+ setStylePrimaryName("v-boxlayout");
+ setVertical(true);
+ }
+
+ public void setVertical(boolean isVertical) {
+ vertical = isVertical;
+ if (vertical) {
+ addStyleName("v-vertical");
+ removeStyleName("v-horizontal");
+ } else {
+ addStyleName("v-horizontal");
+ removeStyleName("v-vertical");
+ }
+ }
+
+ public void addOrMoveSlot(Slot slot, int index) {
+ if (slot.getParent() == this) {
+ int currentIndex = getWidgetIndex(slot);
+ if (index == currentIndex) {
+ return;
+ }
+ }
+ insert(slot, index);
+ }
+
+ @Override
+ protected void insert(Widget child, Element container, int beforeIndex,
+ boolean domInsert) {
+ // Validate index; adjust if the widget is already a child of this
+ // panel.
+ beforeIndex = adjustIndex(child, beforeIndex);
+
+ // Detach new child.
+ child.removeFromParent();
+
+ // Logical attach.
+ getChildren().insert(child, beforeIndex);
+
+ // Physical attach.
+ container = expandWrapper != null ? expandWrapper : getElement();
+ if (domInsert) {
+ DOM.insertChild(container, child.getElement(),
+ spacing ? beforeIndex * 2 : beforeIndex);
+ } else {
+ DOM.appendChild(container, child.getElement());
+ }
+
+ // Adopt.
+ adopt(child);
+ }
+
+ public Slot removeSlot(Widget widget) {
+ Slot slot = getSlot(widget);
+ remove(slot);
+ widgetToSlot.remove(widget);
+ return slot;
+ }
+
+ public Slot getSlot(Widget widget) {
+ Slot slot = widgetToSlot.get(widget);
+ if (slot == null) {
+ slot = new Slot(widget);
+ widgetToSlot.put(widget, slot);
+ }
+ return slot;
+ }
+
+ public enum CaptionPosition {
+ TOP, RIGHT, BOTTOM, LEFT
+ }
+
+ protected class Slot extends SimplePanel {
+
+ private Element spacer;
+
+ private Element captionWrap;
+ private Element caption;
+ private Element captionText;
+ private Icon icon;
+ private Element errorIcon;
+ private Element requiredIcon;
+
+ // Caption is placed after component unless there is some part which
+ // moves it above.
+ private CaptionPosition captionPosition = CaptionPosition.RIGHT;
+
+ private AlignmentInfo alignment;
+ private double expandRatio = -1;
+
+ public Slot(Widget widget) {
+ setWidget(widget);
+ setStylePrimaryName("v-slot");
+ }
+
+ public AlignmentInfo getAlignment() {
+ return alignment;
+ }
+
+ public void setAlignment(AlignmentInfo alignment) {
+ this.alignment = alignment;
+
+ if (alignment.isHorizontalCenter()) {
+ addStyleName(ALIGN_CLASS_PREFIX + "center");
+ removeStyleName(ALIGN_CLASS_PREFIX + "right");
+ } else if (alignment.isRight()) {
+ addStyleName(ALIGN_CLASS_PREFIX + "right");
+ removeStyleName(ALIGN_CLASS_PREFIX + "center");
+ } else {
+ removeStyleName(ALIGN_CLASS_PREFIX + "right");
+ removeStyleName(ALIGN_CLASS_PREFIX + "center");
+ }
+ if (alignment.isVerticalCenter()) {
+ addStyleName(ALIGN_CLASS_PREFIX + "middle");
+ removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
+ } else if (alignment.isBottom()) {
+ addStyleName(ALIGN_CLASS_PREFIX + "bottom");
+ removeStyleName(ALIGN_CLASS_PREFIX + "middle");
+ } else {
+ removeStyleName(ALIGN_CLASS_PREFIX + "middle");
+ removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
+ }
+ }
+
+ public void setExpandRatio(double expandRatio) {
+ this.expandRatio = expandRatio;
+ }
+
+ public double getExpandRatio() {
+ return expandRatio;
+ }
+
+ public void setSpacing(boolean spacing) {
+ if (spacing && spacer == null) {
+ spacer = DOM.createDiv();
+ spacer.addClassName("v-spacing");
+ getElement().getParentElement().insertBefore(spacer,
+ getElement());
+ } else if (!spacing && spacer != null) {
+ spacer.removeFromParent();
+ spacer = null;
+ }
+ }
+
+ public Element getSpacingElement() {
+ return spacer;
+ }
+
+ protected int getSpacingSize(boolean vertical) {
+ if (spacer == null) {
+ return 0;
+ }
+
+ if (layoutManager != null) {
+ if (vertical) {
+ return layoutManager.getOuterHeight(spacer);
+ } else {
+ return layoutManager.getOuterWidth(spacer);
+ }
+ }
+ // TODO place for optimization (in expense of theme
+ // flexibility): only measure one of the elements and cache the
+ // value
+ return vertical ? spacer.getOffsetHeight() : spacer
+ .getOffsetWidth();
+ // }
+ }
+
+ public void setCaptionPosition(CaptionPosition captionPosition) {
+ if (caption == null) {
+ return;
+ }
+
+ captionWrap.removeClassName("v-caption-on-"
+ + this.captionPosition.name().toLowerCase());
+
+ this.captionPosition = captionPosition;
+ if (captionPosition == CaptionPosition.BOTTOM
+ || captionPosition == CaptionPosition.RIGHT) {
+ captionWrap.appendChild(caption);
+ } else {
+ captionWrap.insertFirst(caption);
+ }
+
+ captionWrap.addClassName("v-caption-on-"
+ + captionPosition.name().toLowerCase());
+ }
+
+ public CaptionPosition getCaptionPosition() {
+ return captionPosition;
+ }
+
+ // TODO refactor VCaption and use that instead: creates a tight coupling
+ // between this layout and Vaadin, but it's already coupled
+ public void setCaption(String captionText, String iconUrl,
+ List<String> styles, String error, boolean required,
+ boolean enabled) {
+
+ // TODO place for optimization: check if any of these have changed
+ // since last time, and only run those changes
+
+ // Caption wrappers
+ if (captionText != null || iconUrl != null || error != null
+ || required) {
+ if (caption == null) {
+ caption = DOM.createDiv();
+ captionWrap = DOM.createDiv();
+ captionWrap.addClassName("v-connector");
+ captionWrap.addClassName("v-has-caption");
+ getElement().appendChild(captionWrap);
+ captionWrap.appendChild(getWidget().getElement());
+ }
+ } else if (caption != null) {
+ getElement().appendChild(getWidget().getElement());
+ captionWrap.removeFromParent();
+ caption = null;
+ captionWrap = null;
+ }
+
+ // Caption text
+ if (captionText != null) {
+ if (this.captionText == null) {
+ this.captionText = DOM.createSpan();
+ this.captionText.addClassName("v-captiontext");
+ caption.appendChild(this.captionText);
+ }
+ if (captionText.equals("")) {
+ this.captionText.setInnerHTML("&nbsp;");
+ } else {
+ this.captionText.setInnerText(captionText);
+ }
+ } else if (this.captionText != null) {
+ this.captionText.removeFromParent();
+ this.captionText = null;
+ }
+
+ // Icon
+ if (iconUrl != null) {
+ if (icon == null) {
+ icon = new Icon();
+ // icon = DOM.createImg();
+ // icon.setClassName("v-icon");
+ caption.insertFirst(icon.getElement());
+ }
+ // icon.setAttribute("src", iconUrl);
+ icon.setUri(iconUrl);
+ } else if (icon != null) {
+ icon.getElement().removeFromParent();
+ icon = null;
+ }
+
+ // Required
+ if (required) {
+ if (requiredIcon == null) {
+ requiredIcon = DOM.createSpan();
+ // TODO decide something better
+ requiredIcon.setInnerHTML("*");
+ requiredIcon.setClassName("v-required-field-indicator");
+ }
+ caption.appendChild(requiredIcon);
+ } else if (requiredIcon != null) {
+ requiredIcon.removeFromParent();
+ requiredIcon = null;
+ }
+
+ // Error
+ if (error != null) {
+ if (errorIcon == null) {
+ errorIcon = DOM.createSpan();
+ errorIcon.setClassName("v-errorindicator");
+ }
+ caption.appendChild(errorIcon);
+ } else if (errorIcon != null) {
+ errorIcon.removeFromParent();
+ errorIcon = null;
+ }
+
+ if (caption != null) {
+ // Styles
+ caption.setClassName("v-caption");
+
+ if (styles != null) {
+ for (String style : styles) {
+ caption.addClassName("v-caption-" + style);
+ }
+ }
+
+ if (enabled) {
+ caption.removeClassName("v-disabled");
+ } else {
+ caption.addClassName("v-disabled");
+ }
+
+ // Caption position
+ if (captionText != null || iconUrl != null) {
+ setCaptionPosition(CaptionPosition.TOP);
+ } else {
+ setCaptionPosition(CaptionPosition.RIGHT);
+ }
+ }
+
+ // TODO theme flexibility: add extra styles to captionWrap as well?
+
+ }
+
+ public boolean hasCaption() {
+ return caption != null;
+ }
+
+ public Element getCaptionElement() {
+ return caption;
+ }
+
+ public void setRelativeWidth(boolean relativeWidth) {
+ updateRelativeSize(relativeWidth, "width");
+ }
+
+ public void setRelativeHeight(boolean relativeHeight) {
+ updateRelativeSize(relativeHeight, "height");
+ }
+
+ private void updateRelativeSize(boolean isRelativeSize, String direction) {
+ if (isRelativeSize && hasCaption()) {
+ captionWrap.getStyle().setProperty(
+ direction,
+ getWidget().getElement().getStyle()
+ .getProperty(direction));
+ captionWrap.addClassName("v-has-" + direction);
+ } else if (hasCaption()) {
+ captionWrap.getStyle().clearHeight();
+ captionWrap.removeClassName("v-has-" + direction);
+ captionWrap.getStyle().clearPaddingTop();
+ captionWrap.getStyle().clearPaddingRight();
+ captionWrap.getStyle().clearPaddingBottom();
+ captionWrap.getStyle().clearPaddingLeft();
+ caption.getStyle().clearMarginTop();
+ caption.getStyle().clearMarginRight();
+ caption.getStyle().clearMarginBottom();
+ caption.getStyle().clearMarginLeft();
+ }
+ }
+
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ if (DOM.eventGetType(event) == Event.ONLOAD
+ && icon.getElement() == DOM.eventGetTarget(event)) {
+ if (layoutManager != null) {
+ layoutManager.layoutLater();
+ } else {
+ updateCaptionOffset(caption);
+ }
+ }
+ }
+
+ @Override
+ protected void onDetach() {
+ if (spacer != null) {
+ spacer.removeFromParent();
+ }
+ super.onDetach();
+ }
+
+ }
+
+ protected class Icon extends UIObject {
+ public static final String CLASSNAME = "v-icon";
+ private String myUrl;
+
+ public Icon() {
+ setElement(DOM.createImg());
+ DOM.setElementProperty(getElement(), "alt", "");
+ setStyleName(CLASSNAME);
+ }
+
+ public void setUri(String url) {
+ if (!url.equals(myUrl)) {
+ /*
+ * Start sinking onload events, widgets responsibility to react.
+ * We must do this BEFORE we set src as IE fires the event
+ * immediately if the image is found in cache (#2592).
+ */
+ sinkEvents(Event.ONLOAD);
+
+ DOM.setElementProperty(getElement(), "src", url);
+ myUrl = url;
+ }
+ }
+
+ }
+
+ void setLayoutManager(LayoutManager manager) {
+ layoutManager = manager;
+ }
+
+ private static final RegExp captionPositionRegexp = RegExp
+ .compile("v-caption-on-(\\S+)");
+
+ CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
+ // Get caption position from the classname
+ MatchResult matcher = captionPositionRegexp.exec(captionWrap
+ .getClassName());
+ if (matcher == null || matcher.getGroupCount() < 2) {
+ return CaptionPosition.TOP;
+ }
+ String captionClass = matcher.getGroup(1);
+ CaptionPosition captionPosition = CaptionPosition.valueOf(
+ CaptionPosition.class, captionClass.toUpperCase());
+ return captionPosition;
+ }
+
+ void updateCaptionOffset(Element caption) {
+
+ Element captionWrap = caption.getParentElement().cast();
+
+ Style captionWrapStyle = captionWrap.getStyle();
+ captionWrapStyle.clearPaddingTop();
+ captionWrapStyle.clearPaddingRight();
+ captionWrapStyle.clearPaddingBottom();
+ captionWrapStyle.clearPaddingLeft();
+
+ Style captionStyle = caption.getStyle();
+ captionStyle.clearMarginTop();
+ captionStyle.clearMarginRight();
+ captionStyle.clearMarginBottom();
+ captionStyle.clearMarginLeft();
+
+ // Get caption position from the classname
+ CaptionPosition captionPosition = getCaptionPositionFromElement(captionWrap);
+
+ if (captionPosition == CaptionPosition.LEFT
+ || captionPosition == CaptionPosition.RIGHT) {
+ int captionWidth;
+ if (layoutManager != null) {
+ captionWidth = layoutManager.getOuterWidth(caption)
+ - layoutManager.getMarginWidth(caption);
+ } else {
+ captionWidth = caption.getOffsetWidth();
+ }
+ if (captionWidth > 0) {
+ if (captionPosition == CaptionPosition.LEFT) {
+ captionWrapStyle.setPaddingLeft(captionWidth, Unit.PX);
+ captionStyle.setMarginLeft(-captionWidth, Unit.PX);
+ } else {
+ captionWrapStyle.setPaddingRight(captionWidth, Unit.PX);
+ captionStyle.setMarginRight(-captionWidth, Unit.PX);
+ }
+ }
+ }
+ if (captionPosition == CaptionPosition.TOP
+ || captionPosition == CaptionPosition.BOTTOM) {
+ int captionHeight;
+ if (layoutManager != null) {
+ captionHeight = layoutManager.getOuterHeight(caption)
+ - layoutManager.getMarginHeight(caption);
+ } else {
+ captionHeight = caption.getOffsetHeight();
+ }
+ if (captionHeight > 0) {
+ if (captionPosition == CaptionPosition.TOP) {
+ captionWrapStyle.setPaddingTop(captionHeight, Unit.PX);
+ captionStyle.setMarginTop(-captionHeight, Unit.PX);
+ } else {
+ captionWrapStyle.setPaddingBottom(captionHeight, Unit.PX);
+ captionStyle.setMarginBottom(-captionHeight, Unit.PX);
+ }
+ }
+ }
+ }
+
+ private void toggleStyleName(String name, boolean enabled) {
+ if (enabled) {
+ addStyleName(name);
+ } else {
+ removeStyleName(name);
+ }
+ }
+
+ void setMargin(VMarginInfo marginInfo) {
+ toggleStyleName("v-margin-top", marginInfo.hasTop());
+ toggleStyleName("v-margin-right", marginInfo.hasRight());
+ toggleStyleName("v-margin-bottom", marginInfo.hasBottom());
+ toggleStyleName("v-margin-left", marginInfo.hasLeft());
+ }
+
+ protected void setSpacing(boolean spacingEnabled) {
+ spacing = spacingEnabled;
+ for (Slot slot : widgetToSlot.values()) {
+ if (getWidgetIndex(slot) > 0) {
+ slot.setSpacing(spacingEnabled);
+ }
+ }
+ }
+
+ private void recalculateExpands() {
+ double total = 0;
+ for (Slot slot : widgetToSlot.values()) {
+ if (slot.getExpandRatio() > -1) {
+ total += slot.getExpandRatio();
+ } else {
+ if (vertical) {
+ slot.getElement().getStyle().clearHeight();
+ } else {
+ slot.getElement().getStyle().clearWidth();
+ }
+ }
+ }
+ for (Slot slot : widgetToSlot.values()) {
+ if (slot.getExpandRatio() > -1) {
+ if (vertical) {
+ slot.setHeight((100 * (slot.getExpandRatio() / total))
+ + "%");
+ } else {
+ slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%");
+ }
+ }
+ }
+ }
+
+ private Element expandWrapper;
+
+ void clearExpand() {
+ if (expandWrapper != null) {
+ for (; expandWrapper.getChildCount() > 0;) {
+ Element el = expandWrapper.getChild(0).cast();
+ getElement().appendChild(el);
+ if (vertical) {
+ el.getStyle().clearHeight();
+ el.getStyle().clearMarginTop();
+ } else {
+ el.getStyle().clearWidth();
+ el.getStyle().clearMarginLeft();
+ }
+ }
+ expandWrapper.removeFromParent();
+ expandWrapper = null;
+ }
+ }
+
+ public void updateExpand() {
+ boolean isExpanding = false;
+ for (Widget slot : getChildren()) {
+ if (((Slot) slot).getExpandRatio() > -1) {
+ isExpanding = true;
+ } else {
+ if (vertical) {
+ slot.getElement().getStyle().clearHeight();
+ } else {
+ slot.getElement().getStyle().clearWidth();
+ }
+ }
+ slot.getElement().getStyle().clearMarginLeft();
+ slot.getElement().getStyle().clearMarginTop();
+ }
+
+ if (isExpanding) {
+ if (expandWrapper == null) {
+ expandWrapper = DOM.createDiv();
+ expandWrapper.setClassName("v-expand");
+ for (; getElement().getChildCount() > 0;) {
+ Node el = getElement().getChild(0);
+ expandWrapper.appendChild(el);
+ }
+ getElement().appendChild(expandWrapper);
+ }
+
+ int totalSize = 0;
+ for (Widget w : getChildren()) {
+ Slot slot = (Slot) w;
+ if (slot.getExpandRatio() == -1) {
+ if (layoutManager != null) {
+ // TODO check caption position
+ if (vertical) {
+ totalSize += layoutManager.getOuterHeight(slot
+ .getWidget().getElement())
+ - layoutManager.getMarginHeight(slot
+ .getWidget().getElement());
+ if (slot.hasCaption()) {
+ totalSize += layoutManager.getOuterHeight(slot
+ .getCaptionElement())
+ - layoutManager.getMarginHeight(slot
+ .getCaptionElement());
+ }
+ } else {
+ int max = -1;
+ max = layoutManager.getOuterWidth(slot.getWidget()
+ .getElement())
+ - layoutManager.getMarginWidth(slot
+ .getWidget().getElement());
+ if (slot.hasCaption()) {
+ int max2 = layoutManager.getOuterWidth(slot
+ .getCaptionElement())
+ - layoutManager.getMarginWidth(slot
+ .getCaptionElement());
+ max = Math.max(max, max2);
+ }
+ totalSize += max;
+ }
+ } else {
+ totalSize += vertical ? slot.getOffsetHeight() : slot
+ .getOffsetWidth();
+ }
+ }
+ // TODO fails in Opera, always returns 0
+ totalSize += slot.getSpacingSize(vertical);
+ }
+
+ // When we set the margin to the first child, we don't need
+ // overflow:hidden in the layout root element, since the wrapper
+ // would otherwise be placed outside of the layout root element
+ // and block events on elements below it.
+ if (vertical) {
+ expandWrapper.getStyle().setPaddingTop(totalSize, Unit.PX);
+ expandWrapper.getFirstChildElement().getStyle()
+ .setMarginTop(-totalSize, Unit.PX);
+ } else {
+ expandWrapper.getStyle().setPaddingLeft(totalSize, Unit.PX);
+ expandWrapper.getFirstChildElement().getStyle()
+ .setMarginLeft(-totalSize, Unit.PX);
+ }
+
+ recalculateExpands();
+ }
+ }
+
+ public void recalculateLayoutHeight() {
+ // Only needed if a horizontal layout is undefined high, and contains
+ // relative height children or vertical alignments
+ if (vertical || definedHeight) {
+ return;
+ }
+
+ boolean hasRelativeHeightChildren = false;
+ boolean hasVAlign = false;
+
+ for (Widget slot : getChildren()) {
+ Widget widget = ((Slot) slot).getWidget();
+ String h = widget.getElement().getStyle().getHeight();
+ if (h != null && h.indexOf("%") > -1) {
+ hasRelativeHeightChildren = true;
+ }
+ AlignmentInfo a = ((Slot) slot).getAlignment();
+ if (a != null && (a.isVerticalCenter() || a.isBottom())) {
+ hasVAlign = true;
+ }
+ }
+
+ if (hasRelativeHeightChildren || hasVAlign) {
+ int newHeight;
+ if (layoutManager != null) {
+ newHeight = layoutManager.getOuterHeight(getElement())
+ - layoutManager.getMarginHeight(getElement());
+ } else {
+ newHeight = getElement().getOffsetHeight();
+ }
+ VBoxLayout.this.getElement().getStyle()
+ .setHeight(newHeight, Unit.PX);
+ }
+
+ }
+
+ void clearHeight() {
+ getElement().getStyle().clearHeight();
+ }
+
+ @Override
+ public void setHeight(String height) {
+ super.setHeight(height);
+ definedHeight = (height != null && !"".equals(height));
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VerticalBoxLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/VerticalBoxLayoutConnector.java
new file mode 100644
index 0000000000..369afb9e2f
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ui/VerticalBoxLayoutConnector.java
@@ -0,0 +1,26 @@
+package com.vaadin.terminal.gwt.client.ui;
+
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.UIDL;
+import com.vaadin.terminal.gwt.client.ui.Connect.LoadStyle;
+import com.vaadin.ui.VerticalLayout;
+
+@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
+public class VerticalBoxLayoutConnector extends AbstractBoxLayoutConnector {
+
+ @Override
+ public void init() {
+ super.init();
+ getWidget().setVertical(true);
+ }
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ // TODO fix when Vaadin style name handling is improved so that it won't
+ // override extra client side style names
+ getWidget().setVertical(true);
+ super.updateFromUIDL(uidl, client);
+ getWidget().setVertical(true);
+ }
+
+}
diff --git a/tests/testbench/com/vaadin/tests/components/orderedlayout/BoxLayoutTest.java b/tests/testbench/com/vaadin/tests/components/orderedlayout/BoxLayoutTest.java
new file mode 100644
index 0000000000..e52979267a
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/orderedlayout/BoxLayoutTest.java
@@ -0,0 +1,437 @@
+package com.vaadin.tests.components.orderedlayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.event.LayoutEvents.LayoutClickEvent;
+import com.vaadin.event.LayoutEvents.LayoutClickListener;
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.terminal.UserError;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractField;
+import com.vaadin.ui.AbstractOrderedLayout;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Label.ContentMode;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.themes.Reindeer;
+
+@Theme("tests-components")
+public class BoxLayoutTest extends AbstractTestRoot {
+
+ protected AbstractOrderedLayout view;
+
+ protected AbstractOrderedLayout l;
+
+ protected AbstractComponent target;
+
+ protected NativeSelect componentWidth;
+ protected NativeSelect componentHeight;
+ protected NativeSelect componentCaption;
+ protected NativeSelect componentIcon;
+ protected TextField componentDescription;
+ protected CheckBox componentError;
+ protected CheckBox componentRequired;
+
+ protected NativeSelect align;
+ protected CheckBox expand;
+
+ @Override
+ protected void setup(WrappedRequest request) {
+
+ view = new VerticalLayout();
+ view.setSizeFull();
+ view.setMargin(true);
+ view.setSpacing(true);
+
+ view.addComponent(createControls(false));
+ view.addComponent(createTestLayout(false));
+ view.setExpandRatio(view.getComponent(1), 1);
+
+ setContent(view);
+ getApplication().setRootPreserved(true);
+ }
+
+ protected AbstractOrderedLayout createControls(boolean horizontal) {
+ VerticalLayout root = new VerticalLayout();
+ root.setSpacing(true);
+
+ // First row
+ HorizontalLayout header = new HorizontalLayout();
+ header.setSpacing(true);
+ root.addComponent(header);
+
+ Label title = new Label("BoxLayout Test");
+ title.addStyleName(Reindeer.LABEL_H1);
+ header.addComponent(title);
+
+ final CheckBox vertical = new CheckBox("Vertical", !horizontal);
+ vertical.setImmediate(true);
+ vertical.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ view.removeAllComponents();
+
+ view.addComponent(createControls(!vertical.getValue()
+ .booleanValue()));
+ view.addComponent(createTestLayout(!vertical.getValue()
+ .booleanValue()));
+
+ view.setExpandRatio(view.getComponent(1), 1);
+
+ }
+ });
+ header.addComponent(vertical);
+
+ Button addComponent = new Button("Add Component",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ GridLayout grid = new GridLayout(2, 2);
+ Button grow = new Button("Grow Me",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ if (event.getButton().getWidth() == -1) {
+ event.getButton().setHeight("50px");
+ event.getButton().setWidth("200px");
+ } else {
+ event.getButton()
+ .setSizeUndefined();
+ }
+ }
+ });
+ grid.addComponent(new Label("Grid cell 1"));
+ grid.addComponent(new Label("Grid cell 2"));
+ grid.addComponent(grow);
+ grid.addComponent(new Label("Grid cell 4"));
+ // l.addComponent(grid);
+ l.addComponent(new TextField("Some field"));
+ }
+ });
+ header.addComponent(addComponent);
+
+ Button removeComponent = new Button("Remove Component",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ Component last = l.getComponent(l.getComponentCount() - 1);
+ l.removeComponent(last);
+ }
+ });
+ header.addComponent(removeComponent);
+
+ // Second row
+ HorizontalLayout controls = new HorizontalLayout();
+ controls.setSpacing(true);
+ root.addComponent(controls);
+
+ // Layout controls
+ HorizontalLayout layout = new HorizontalLayout();
+ layout.addStyleName("fieldset");
+ layout.setSpacing(true);
+ controls.addComponent(layout);
+ layout.addComponent(new Label("Layout"));
+
+ ArrayList<String> sizes = new ArrayList<String>();
+ sizes.addAll(Arrays.asList("100px", "30em", "100%"));
+
+ final NativeSelect width = new NativeSelect(null, sizes);
+ width.setImmediate(true);
+ width.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (width.getValue() != null) {
+ l.setWidth(width.getValue().toString());
+ } else {
+ l.setWidth(null);
+ }
+ }
+ });
+ layout.addComponent(width);
+ layout.addComponent(new Label("&times;", ContentMode.XHTML));
+ final NativeSelect height = new NativeSelect(null, sizes);
+ height.setImmediate(true);
+ height.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (height.getValue() != null) {
+ l.setHeight(height.getValue().toString());
+ } else {
+ l.setHeight(null);
+ }
+ }
+ });
+ layout.addComponent(height);
+
+ final CheckBox margin = new CheckBox("Margin", false);
+ margin.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ l.setMargin(margin.getValue().booleanValue());
+ }
+ });
+ margin.setImmediate(true);
+ layout.addComponent(margin);
+ layout.addComponent(margin);
+
+ final CheckBox spacing = new CheckBox("Spacing", false);
+ spacing.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ l.setSpacing(spacing.getValue().booleanValue());
+ }
+ });
+ spacing.setImmediate(true);
+ layout.addComponent(spacing);
+
+ // Cell controls
+ HorizontalLayout cell = new HorizontalLayout();
+ cell.addStyleName("fieldset");
+ cell.setSpacing(true);
+ controls.addComponent(cell);
+ cell.addComponent(new Label("Cell"));
+
+ ArrayList<Alignment> alignments = new ArrayList<Alignment>();
+ alignments.addAll(Arrays.asList(Alignment.TOP_LEFT,
+ Alignment.MIDDLE_LEFT, Alignment.BOTTOM_LEFT,
+ Alignment.TOP_CENTER, Alignment.MIDDLE_CENTER,
+ Alignment.BOTTOM_CENTER, Alignment.TOP_RIGHT,
+ Alignment.MIDDLE_RIGHT, Alignment.BOTTOM_RIGHT));
+
+ align = new NativeSelect(null, alignments);
+ for (Alignment a : alignments) {
+ align.setItemCaption(a,
+ a.getVerticalAlignment() + "-" + a.getHorizontalAlignment());
+ }
+ align.setImmediate(true);
+ align.setEnabled(false);
+ align.setNullSelectionAllowed(false);
+ align.select(Alignment.TOP_LEFT);
+ align.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (target == null) {
+ return;
+ }
+ l.setComponentAlignment(target, ((Alignment) align.getValue()));
+ }
+ });
+ cell.addComponent(align);
+
+ expand = new CheckBox("Expand");
+ expand.setImmediate(true);
+ expand.setEnabled(false);
+ expand.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null) {
+ l.setExpandRatio(target, expand.getValue() ? 1 : 0);
+ }
+ }
+ });
+ cell.addComponent(expand);
+
+ // Component controls
+ HorizontalLayout component = new HorizontalLayout();
+ component.addStyleName("fieldset");
+ component.setSpacing(true);
+ root.addComponent(component);
+ component.addComponent(new Label("Component"));
+
+ sizes = new ArrayList<String>();
+ sizes.addAll(Arrays.asList("50px", "200px", "10em", "50%", "100%"));
+
+ componentWidth = new NativeSelect(null, sizes);
+ componentWidth.setImmediate(true);
+ componentWidth.setEnabled(false);
+ componentWidth.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (target == null) {
+ return;
+ }
+ if (componentWidth.getValue() != null) {
+ target.setWidth(componentWidth.getValue().toString());
+ } else {
+ target.setWidth(null);
+ }
+ }
+ });
+ component.addComponent(componentWidth);
+ component.addComponent(new Label("&times;", ContentMode.XHTML));
+
+ componentHeight = new NativeSelect(null, sizes);
+ componentHeight.setImmediate(true);
+ componentHeight.setEnabled(false);
+ componentHeight.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (componentHeight.getValue() != null) {
+ target.setHeight(componentHeight.getValue().toString());
+ } else {
+ target.setHeight(null);
+ }
+ }
+ });
+ component.addComponent(componentHeight);
+
+ componentCaption = new NativeSelect("Caption", Arrays.asList("Short",
+ "Slightly Longer Caption"));
+ componentCaption.setImmediate(true);
+ componentCaption.setEnabled(false);
+ componentCaption.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (componentCaption.getValue() != null) {
+ target.setCaption(componentCaption.getValue().toString());
+ } else {
+ target.setCaption(null);
+ }
+ }
+ });
+ component.addComponent(componentCaption);
+
+ componentIcon = new NativeSelect("Icon", Arrays.asList(
+ "../runo/icons/16/folder.png", "../runo/icons/32/document.png"));
+ componentIcon.setImmediate(true);
+ componentIcon.setEnabled(false);
+ componentIcon.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (componentIcon.getValue() != null) {
+ target.setIcon(new ThemeResource(componentIcon.getValue()
+ .toString()));
+ } else {
+ target.setIcon(null);
+ }
+ }
+ });
+ component.addComponent(componentIcon);
+
+ componentDescription = new TextField("Description");
+ componentDescription.setImmediate(true);
+ componentDescription.setEnabled(false);
+ componentDescription.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ target.setDescription(componentDescription.getValue());
+ }
+ });
+ component.addComponent(componentDescription);
+
+ componentError = new CheckBox("Error");
+ componentError.setImmediate(true);
+ componentError.setEnabled(false);
+ componentError.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null) {
+ target.setComponentError(componentError.getValue() ? new UserError(
+ "Error message") : null);
+ }
+ }
+ });
+ component.addComponent(componentError);
+
+ componentRequired = new CheckBox("Required");
+ componentRequired.setImmediate(true);
+ componentRequired.setEnabled(false);
+ componentRequired.addListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ if (target != null && target instanceof AbstractField) {
+ ((AbstractField<?>) target).setRequired(componentRequired
+ .getValue());
+ }
+ }
+ });
+ component.addComponent(componentRequired);
+
+ return root;
+ }
+
+ protected AbstractOrderedLayout createTestLayout(boolean horizontal) {
+ l = horizontal ? new HorizontalLayout() : new VerticalLayout();
+ l.setSizeUndefined();
+ l.addStyleName("test");
+
+ Label label = new Label("Component 1");
+ l.addComponent(label);
+ l.addComponent(new Button("Component 2"));
+
+ l.addListener(new LayoutClickListener() {
+ public void layoutClick(LayoutClickEvent event) {
+ if (event.getChildComponent() == null
+ || target == event.getChildComponent()) {
+ if (target != null) {
+ target.removeStyleName("target");
+ }
+ target = null;
+ } else if (target != event.getChildComponent()) {
+ if (target != null) {
+ target.removeStyleName("target");
+ }
+ target = (AbstractComponent) event.getChildComponent();
+ target.addStyleName("target");
+ }
+ componentWidth.setEnabled(target != null);
+ componentHeight.setEnabled(target != null);
+ componentCaption.setEnabled(target != null);
+ componentIcon.setEnabled(target != null);
+ componentDescription.setEnabled(target != null);
+ componentError.setEnabled(target != null);
+ componentRequired.setEnabled(target != null
+ && target instanceof AbstractField);
+ align.setEnabled(target != null);
+ expand.setEnabled(target != null);
+ if (target != null) {
+ if (target.getWidth() > -1) {
+ componentWidth.select(new Float(target.getWidth())
+ .intValue()
+ + target.getWidthUnits().getSymbol());
+ } else {
+ componentWidth.select(null);
+ }
+ if (target.getHeight() > -1) {
+ componentHeight.select(new Float(target.getHeight())
+ .intValue()
+ + target.getHeightUnits().getSymbol());
+ } else {
+ componentHeight.select(null);
+ }
+
+ align.select(l.getComponentAlignment(target));
+ expand.setValue(new Boolean(l.getExpandRatio(target) > 0));
+
+ componentCaption.select(target.getCaption());
+ if (target.getIcon() != null) {
+ componentIcon.select(((ThemeResource) target.getIcon())
+ .getResourceId());
+ } else {
+ componentIcon.select(null);
+ }
+ componentDescription.setValue(target.getDescription());
+ componentError.setValue(target.getComponentError() != null);
+ if (target instanceof AbstractField) {
+ componentRequired.setValue(((AbstractField<?>) target)
+ .isRequired());
+ }
+ }
+ }
+ });
+
+ target = null;
+
+ return l;
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/tests/testbench/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java b/tests/testbench/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java
index 172e808070..a4a7098f52 100644
--- a/tests/testbench/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java
+++ b/tests/testbench/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java
@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import com.vaadin.annotations.Theme;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.terminal.WrappedRequest;
@@ -20,6 +21,7 @@ import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.VerticalLayout;
+@Theme("tests-components")
public class OrderedLayoutCases extends AbstractTestRoot {
private static final String[] dimensionValues = { "-1px", "5px", "350px",
"800px", "100%", "50%" };
diff --git a/tests/testbench/com/vaadin/tests/components/orderedlayout/VaadinTunesLayout.java b/tests/testbench/com/vaadin/tests/components/orderedlayout/VaadinTunesLayout.java
new file mode 100644
index 0000000000..973bd63d76
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/orderedlayout/VaadinTunesLayout.java
@@ -0,0 +1,341 @@
+package com.vaadin.tests.components.orderedlayout;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.terminal.Sizeable;
+import com.vaadin.terminal.ThemeResource;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.HorizontalSplitPanel;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.NativeButton;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.Slider;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.VerticalLayout;
+
+@Theme("tests-components")
+public class VaadinTunesLayout extends AbstractTestRoot {
+
+ @Override
+ public void setup(WrappedRequest request) {
+
+ /*
+ * We'll build the whole UI here, since the application will not contain
+ * any logic. Otherwise it would be more practical to separate parts of
+ * the UI into different classes and methods.
+ */
+
+ // Main (browser) window, needed in all Vaadin applications
+ VerticalLayout rootLayout = new VerticalLayout();
+ // final Window root = new Window("VaadinTunes", rootLayout);
+
+ /*
+ * We'll attach the window to the browser view already here, so we won't
+ * forget it later.
+ */
+ setContent(rootLayout);
+
+ // root.showNotification(
+ // "This is an example of how you can do layouts in Vaadin.<br/>It is not a working sound player.",
+ // Notification.TYPE_HUMANIZED_MESSAGE);
+
+ // Our root window contains one VerticalLayout, let's make
+ // sure it's 100% sized, and remove unwanted margins
+ rootLayout.setSizeFull();
+ rootLayout.setMargin(false);
+
+ // Top area, containing playback and volume controls, play status, view
+ // modes and search
+ HorizontalLayout top = new HorizontalLayout();
+ top.setWidth("100%");
+ top.setMargin(false, true, false, true); // Enable horizontal margins
+ top.setSpacing(true);
+
+ // Let's attach that one straight away too
+ rootLayout.addComponent(top);
+
+ // Create the placeholders for all the components in the top area
+ HorizontalLayout playback = new HorizontalLayout();
+ HorizontalLayout volume = new HorizontalLayout();
+ HorizontalLayout status = new HorizontalLayout();
+ HorizontalLayout viewmodes = new HorizontalLayout();
+ ComboBox search = new ComboBox();
+
+ // Add the components and align them properly
+ top.addComponent(playback);
+ top.addComponent(volume);
+ top.addComponent(status);
+ top.addComponent(viewmodes);
+ top.addComponent(search);
+ top.setComponentAlignment(playback, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(volume, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(status, Alignment.MIDDLE_CENTER);
+ top.setComponentAlignment(viewmodes, Alignment.MIDDLE_LEFT);
+ top.setComponentAlignment(search, Alignment.MIDDLE_LEFT);
+
+ /*
+ * We want our status area to expand if the user resizes the root
+ * window, and we want it to accommodate as much space as there is
+ * available. All other components in the top layout should stay fixed
+ * sized, so we don't need to specify any expand ratios for them (they
+ * will automatically revert to zero after the following line).
+ */
+ top.setExpandRatio(status, 1.0F);
+
+ // Playback controls
+ Button prev = new NativeButton("Previous");
+ Button play = new NativeButton("Play/pause");
+ Button next = new NativeButton("Next");
+ playback.addComponent(prev);
+ playback.addComponent(play);
+ playback.addComponent(next);
+ // Set spacing between the buttons
+ playback.setSpacing(true);
+
+ // Volume controls
+ Button mute = new NativeButton("mute");
+ Slider vol = new Slider();
+ vol.setOrientation(Slider.ORIENTATION_HORIZONTAL);
+ vol.setWidth("100px");
+ Button max = new NativeButton("max");
+ volume.addComponent(mute);
+ volume.addComponent(vol);
+ volume.addComponent(max);
+
+ // Status area
+ status.setWidth("80%");
+ status.setSpacing(true);
+
+ Button toggleVisualization = new NativeButton("Mode");
+ Label timeFromStart = new Label("0:00");
+
+ // We'll need another layout to show currently playing track and
+ // progress
+ VerticalLayout trackDetails = new VerticalLayout();
+ trackDetails.setWidth("100%");
+ Label track = new Label("Track Name");
+ Label album = new Label("Album Name - Artist");
+ track.setWidth(null);
+ album.setWidth(null);
+ Slider progress = new Slider();
+ progress.setOrientation(Slider.ORIENTATION_HORIZONTAL);
+ progress.setWidth("100%");
+ trackDetails.addComponent(track);
+ trackDetails.addComponent(album);
+ trackDetails.addComponent(progress);
+ trackDetails.setComponentAlignment(track, Alignment.TOP_CENTER);
+ trackDetails.setComponentAlignment(album, Alignment.TOP_CENTER);
+
+ Label timeToEnd = new Label("-4:46");
+ Button jumpToTrack = new NativeButton("Show");
+
+ // Place all components to the status layout and align them properly
+ status.addComponent(toggleVisualization);
+ status.setComponentAlignment(toggleVisualization, Alignment.MIDDLE_LEFT);
+ status.addComponent(timeFromStart);
+ status.setComponentAlignment(timeFromStart, Alignment.BOTTOM_LEFT);
+ status.addComponent(trackDetails);
+ status.addComponent(timeToEnd);
+ status.setComponentAlignment(timeToEnd, Alignment.BOTTOM_LEFT);
+ status.addComponent(jumpToTrack);
+ status.setComponentAlignment(jumpToTrack, Alignment.MIDDLE_LEFT);
+
+ // Then remember to specify the expand ratio
+ status.setExpandRatio(trackDetails, 1.0F);
+
+ // View mode buttons
+ Button viewAsTable = new NativeButton("Table");
+ Button viewAsGrid = new NativeButton("Grid");
+ Button coverflow = new NativeButton("Coverflow");
+ viewmodes.addComponent(viewAsTable);
+ viewmodes.addComponent(viewAsGrid);
+ viewmodes.addComponent(coverflow);
+
+ /*
+ * That covers the top bar. Now let's move on to the sidebar and track
+ * listing
+ */
+
+ // We'll need one splitpanel to separate the sidebar and track listing
+ HorizontalSplitPanel bottom = new HorizontalSplitPanel();
+ rootLayout.addComponent(bottom);
+
+ // The splitpanel is by default 100% x 100%, but we'll need to adjust
+ // our main window layout to accomodate the height
+ rootLayout.setExpandRatio(bottom, 1.0F);
+
+ // Give the sidebar less space than the listing
+ bottom.setSplitPosition(200, Sizeable.UNITS_PIXELS);
+
+ // Let's add some content to the sidebar
+ // First, we need a layout to but all components in
+ VerticalLayout sidebar = new VerticalLayout();
+ sidebar.setSizeFull();
+ bottom.setFirstComponent(sidebar);
+
+ /*
+ * Then we need some labels and buttons, and an album cover image The
+ * labels and buttons go into their own vertical layout, since we want
+ * the 'sidebar' layout to be expanding (cover image in the bottom).
+ * VerticalLayout is by default 100% wide.
+ */
+ VerticalLayout selections = new VerticalLayout();
+ Label library = new Label("Library");
+ Button music = new NativeButton("Music");
+ music.setWidth("100%");
+
+ Label store = new Label("Store");
+ Button vaadinTunesStore = new NativeButton("VaadinTunes Store");
+ vaadinTunesStore.setWidth("100%");
+ Button purchased = new NativeButton("Purchased");
+ purchased.setWidth("100%");
+
+ Label playlists = new Label("Playlists");
+ Button genius = new NativeButton("Geniues");
+ genius.setWidth("100%");
+ Button recent = new NativeButton("Recently Added");
+ recent.setWidth("100%");
+
+ // Lets add them to the 'selections' layout
+ selections.addComponent(library);
+ selections.addComponent(music);
+ selections.addComponent(store);
+ selections.addComponent(vaadinTunesStore);
+ selections.addComponent(purchased);
+ selections.addComponent(playlists);
+ selections.addComponent(genius);
+ selections.addComponent(recent);
+
+ // Then add the selections to the sidebar, and set it expanding
+ sidebar.addComponent(selections);
+ sidebar.setExpandRatio(selections, 1.0F);
+
+ // Then comes the cover artwork (we'll add the actual image in the
+ // themeing section)
+ Embedded cover = new Embedded("Currently Playing");
+ sidebar.addComponent(cover);
+
+ /*
+ * And lastly, we need the track listing table It should fill the whole
+ * left side of our bottom layout
+ */
+ Table listing = new Table();
+ listing.setSizeFull();
+ listing.setSelectable(true);
+ bottom.setSecondComponent(listing);
+
+ // Add the table headers
+ listing.addContainerProperty("Name", String.class, "");
+ listing.addContainerProperty("Time", String.class, "0:00");
+ listing.addContainerProperty("Artist", String.class, "");
+ listing.addContainerProperty("Album", String.class, "");
+ listing.addContainerProperty("Genre", String.class, "");
+ listing.addContainerProperty("Rating", NativeSelect.class,
+ new NativeSelect());
+
+ // Lets populate the table with random data
+ String[] tracks = new String[] { "Red Flag", "Millstone",
+ "Not The Sun", "Breath", "Here We Are", "Deep Heaven",
+ "Her Voice Resides", "Natural Tan", "End It All", "Kings",
+ "Daylight Slaving", "Mad Man", "Resolve", "Teargas",
+ "African Air", "Passing Bird" };
+ String[] times = new String[] { "4:12", "6:03", "5:43", "4:32", "3:42",
+ "4:45", "2:56", "9:34", "2:10", "3:44", "5:49", "6:30", "5:18",
+ "7:42", "3:13", "2:52" };
+ String[] artists = new String[] { "Billy Talent", "Brand New",
+ "Breaking Benjamin", "Becoming The Archetype",
+ "Bullet For My Valentine", "Chasing Victory", "Chimaira",
+ "Danko Jones", "Deadlock", "Deftones", "From Autumn To Ashes",
+ "Haste The Day", "Four Year Strong", "In Flames", "Kemopetrol",
+ "John Legend" };
+ String[] albums = new String[] { "Once Again", "The Caitiff Choir",
+ "The Devil And God", "Light Grenades", "Dicthonomy",
+ "Back In Black", "Dreamer", "Come Clarity", "Year Zero",
+ "Frames", "Fortress", "Phobia", "The Poison", "Manifesto",
+ "White Pony", "The Big Dirty" };
+ String[] genres = new String[] { "Rock", "Metal", "Hardcore", "Indie",
+ "Pop", "Alternative", "Blues", "Jazz", "Hip Hop",
+ "Electronica", "Punk", "Hard Rock", "Dance", "R'n'B", "Gospel",
+ "Country" };
+ for (int i = 0; i < 1000; i++) {
+ NativeSelect s = new NativeSelect();
+ s.addItem("1 star");
+ s.addItem("2 stars");
+ s.addItem("3 stars");
+ s.addItem("4 stars");
+ s.addItem("5 stars");
+ s.select(i % 5 + " stars");
+ final int index = i % 16;
+ listing.addItem(new Object[] { tracks[index], times[index],
+ artists[index], albums[index], genres[index], s }, i);
+ }
+
+ // We'll align the track time column to right as well
+ listing.setColumnAlignment("Time", Table.ALIGN_RIGHT);
+
+ // TODO the footer
+
+ // Now what's left to do? Themeing of course.
+ // setTheme("vaadintunes");
+
+ /*
+ * Let's give a namespace to our application window. This way, if
+ * someone uses the same theme for different applications, we don't get
+ * unwanted style conflicts.
+ */
+ // root.setStyleName("tTunes");
+
+ top.setStyleName("top");
+ top.setHeight("75px"); // Same as the background image height
+
+ playback.setStyleName("playback");
+ playback.setMargin(false, true, false, false); // Add right-side margin
+ play.setStyleName("play");
+ next.setStyleName("next");
+ prev.setStyleName("prev");
+ playback.setComponentAlignment(prev, Alignment.MIDDLE_LEFT);
+ playback.setComponentAlignment(next, Alignment.MIDDLE_LEFT);
+
+ volume.setStyleName("volume");
+ mute.setStyleName("mute");
+ max.setStyleName("max");
+ vol.setWidth("78px");
+
+ status.setStyleName("status");
+ status.setMargin(true);
+ status.setHeight("46px"); // Height of the background image
+
+ toggleVisualization.setStyleName("toggle-vis");
+ jumpToTrack.setStyleName("jump");
+
+ viewAsTable.setStyleName("viewmode-table");
+ viewAsGrid.setStyleName("viewmode-grid");
+ coverflow.setStyleName("viewmode-coverflow");
+
+ sidebar.setStyleName("sidebar");
+
+ music.setStyleName("selected");
+
+ cover.setSource(new ThemeResource("images/album-cover.jpg"));
+ // Because this is an image, it will retain it's aspect ratio
+ cover.setWidth("100%");
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/tests/testbench/com/vaadin/tests/components/table/TextFieldRelativeWidth.java b/tests/testbench/com/vaadin/tests/components/table/TextFieldRelativeWidth.java
index 865a50c5f7..f3a92d410c 100644
--- a/tests/testbench/com/vaadin/tests/components/table/TextFieldRelativeWidth.java
+++ b/tests/testbench/com/vaadin/tests/components/table/TextFieldRelativeWidth.java
@@ -27,8 +27,7 @@ public class TextFieldRelativeWidth extends TestBase {
public class EditTable extends Table implements Button.ClickListener {
- private Button addButton = new Button("Add new row",
- (Button.ClickListener) this);
+ private Button addButton = new Button("Add new row", this);
private String inputPrompt;
diff --git a/tests/testbench/com/vaadin/tests/themes/LiferayThemeTest.java b/tests/testbench/com/vaadin/tests/themes/LiferayThemeTest.java
new file mode 100644
index 0000000000..fa15d88799
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/themes/LiferayThemeTest.java
@@ -0,0 +1,37 @@
+package com.vaadin.tests.themes;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.themes.LiferayTheme;
+
+@Theme("liferay")
+public class LiferayThemeTest extends AbstractTestRoot {
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ Panel p = new Panel("Panel");
+ addComponent(p);
+ p.addComponent(new Label("Panel content"));
+
+ p = new Panel("Light Panel");
+ p.addStyleName(LiferayTheme.PANEL_LIGHT);
+ addComponent(p);
+ p.addComponent(new Label("Panel content"));
+ }
+
+ @Override
+ protected String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}