IntroductionGWT Mosaic is using a layout system based on absolute positioning. Like Java Swing, GWT Mosaic uses Layout Managers to layout widgets in a container widget called LayoutPanel. Such a layout system relays on a cascading layout process. This is a very important concept for building complex application layouts, it allows nested layout structures, as you can layout a top level container, and the child containers will have a chance to adjust their layouts as well. GWT Panels are not designed to be part of a layout cascading process. This makes mixing of GWT widgets and GWT Mosaic widgets a complicated task. GWT Mosaic provides an interface (HasLayoutManager) that panels have to implement so that they can be part of the layout cascade process. This interface can be used to build widgets that can act as adapters between GWT widgets and GWT Mosaic widgets. For example a Viewport widget is an adapter widget. DetailsAdding GWT Widgets to GWT Mosaic panelsAll GWT Mosaic panels are based on LayoutPanel. LayoutPanel is using a layout manager to determine the location and size of widgets placed into. A layout manager is an object that implements the LayoutManager interface. Although widgets can provide size and alignment hints, a LayoutPanel's layout manager has the final say on the size and position of the widgets within the LayoutPanel. Widgets are automatically laid out when they are attached to the DOM, this makes it easy to get almost all widgets to render without any issues. Example 1Adding a GWT panel, like HorizontalSplitPanel, into a Viewport is trivial. public void onModuleLoad() {
Viewport viewport = new Viewport();
HorizontalSplitPanel hSplit = new HorizontalSplitPanel();
hSplit.setSize("250px", "200px");
hSplit.setSplitPosition("30%");
hSplit.setRightWidget(new HTML("<b>Right</b>"));
hSplit.setLeftWidget(new HTML("<b>Left</b>"));
viewport.getLayoutPanel().add(hSplit);
viewport.attach();
}
Note: hSplit.setSize("250px", "200px") is ignored by Viewport since the default layout manager is FillLayout. But, FillLayout can be forced to respect the widget's size by providing alignment hints. By replacing the following line: viewport.getLayoutPanel().add(hSplit); with: viewport.getLayoutPanel().add(hSplit, new FillLayoutData(FillLayout.ALIGN_CENTER, FillLayout.ALIGN_MIDDLE, true)); the HorizontalSplitPanel is positioned in the center of the page and sized to (250px, 200px).
Example 2This example is using a BoxLayout to place two DisclosurePanel widgets in one row. This case is not trivial since DisclosurePanel changes its size in both directions. The hint FillStyle.HORIZONTAL forces LayoutPanel to ignore any size changes in the horizontal axis, however, LayoutPanel will respct any size changes in the vertical axis. public void onModuleLoad() {
Viewport viewport = new Viewport(new BoxLayout());
LayoutPanel vbox = viewport.getLayoutPanel();
vbox.add(createAdvancedForm(), new BoxLayoutData(FillStyle.HORIZONTAL, true));
vbox.add(createAdvancedForm(), new BoxLayoutData(FillStyle.HORIZONTAL, true));
viewport.attach();
}
private Widget createAdvancedForm() {
// Create a table to layout the form options
FlexTable layout = new FlexTable();
layout.setCellSpacing(6);
layout.setWidth("300px");
FlexCellFormatter cellFormatter = layout.getFlexCellFormatter();
// Add a title to the form
layout.setHTML(0, 0, "Enter Search Criteria");
cellFormatter.setColSpan(0, 0, 2);
cellFormatter.setHorizontalAlignment(0, 0,
HasHorizontalAlignment.ALIGN_CENTER);
// Add some standard form options
layout.setHTML(1, 0, "Name:");
layout.setWidget(1, 1, new TextBox());
layout.setHTML(2, 0, "Description:");
layout.setWidget(2, 1, new TextBox());
// Create some advanced options
HorizontalPanel genderPanel = new HorizontalPanel();
String[] genderOptions = new String[] {"male", "female"};
for (int i = 0; i < genderOptions.length; i++) {
genderPanel.add(new RadioButton("gender", genderOptions[i]));
}
Grid advancedOptions = new Grid(2, 2);
advancedOptions.setCellSpacing(6);
advancedOptions.setHTML(0, 0, "Location:");
advancedOptions.setWidget(0, 1, new TextBox());
advancedOptions.setHTML(1, 0, "Gender:");
advancedOptions.setWidget(1, 1, genderPanel);
// Add advanced options to form in a disclosure panel
DisclosurePanel advancedDisclosure = new DisclosurePanel(
"Advanced Criteria");
advancedDisclosure.setAnimationEnabled(true);
advancedDisclosure.ensureDebugId("cwDisclosurePanel");
advancedDisclosure.setContent(advancedOptions);
layout.setWidget(3, 0, advancedDisclosure);
cellFormatter.setColSpan(3, 0, 2);
return layout;
}
Again this seems trivial. But, by replacing the following line: Viewport viewport = new Viewport(new BoxLayout()); with Viewport viewport = new Viewport(new BoxLayout(Orientation.VERTICAL)); the UI is missaligned:
One possible workaround to fix this issue would be to extend DisclosurePanel and override the setWidth()/setHeight() methods. Unfortunately, DisclosurePanel is declared final, in which case a monitor could be used to periodically check the outer dimensions of a widget and redraws it as necessary. public void onModuleLoad() {
Viewport viewport = new Viewport(new BoxLayout(Orientation.VERTICAL));
LayoutPanel vbox = viewport.getLayoutPanel();
vbox.add(createAdvancedForm(),
new BoxLayoutData(FillStyle.HORIZONTAL, true));
vbox.add(createAdvancedForm(),
new BoxLayoutData(FillStyle.HORIZONTAL, true));
viewport.attach();
}
private Widget createAdvancedForm() {
// Create a table to layout the form options
final FlexTable layout = new FlexTable();
layout.setCellSpacing(6);
layout.setWidth("300px");
FlexCellFormatter cellFormatter = layout.getFlexCellFormatter();
// Add a title to the form
layout.setHTML(0, 0, "Enter Search Criteria");
cellFormatter.setColSpan(0, 0, 2);
cellFormatter.setHorizontalAlignment(0, 0,
HasHorizontalAlignment.ALIGN_CENTER);
// Add some standard form options
layout.setHTML(1, 0, "Name:");
layout.setWidget(1, 1, new TextBox());
layout.setHTML(2, 0, "Description:");
layout.setWidget(2, 1, new TextBox());
// Create some advanced options
HorizontalPanel genderPanel = new HorizontalPanel();
String[] genderOptions = new String[] {"male", "female"};
for (int i = 0; i < genderOptions.length; i++) {
genderPanel.add(new RadioButton("gender", genderOptions[i]));
}
Grid advancedOptions = new Grid(2, 2);
advancedOptions.setCellSpacing(6);
advancedOptions.setHTML(0, 0, "Location:");
advancedOptions.setWidget(0, 1, new TextBox());
advancedOptions.setHTML(1, 0, "Gender:");
advancedOptions.setWidget(1, 1, genderPanel);
// Add advanced options to form in a disclosure panel
DisclosurePanel advancedDisclosure = new DisclosurePanel(
"Advanced Criteria");
advancedDisclosure.setAnimationEnabled(true);
advancedDisclosure.ensureDebugId("cwDisclosurePanel");
advancedDisclosure.setContent(advancedOptions);
layout.setWidget(3, 0, advancedDisclosure);
cellFormatter.setColSpan(3, 0, 2);
ResizableWidgetCollection.get().add(new ResizableWidget() {
public Element getElement() {
return layout.getElement();
}
public boolean isAttached() {
return layout.isAttached();
}
public void onResize(int width, int height) {
WidgetHelper.getParent(layout).layout();
}
});
return layout;
}
Adding GWT Mosaic Widgets to GWT panelsLike panels all GWT Mosaic widgets are based on LayoutPanel. Most GWT Mosaic widgets extend LayoutComposite (a Composite widget initialized with a LayoutPanel). Adding GWT Mosaic widgets to GWT widgets needs some extra glue code or adapter classes. Such an adapter class is Viewport. Viewport is designed to be a top level widget. Viewport is attached to RootPanel, is a listens to window resize events, extends LayoutComposite and is responsible to trigger the layout cascade. Example 3This example adds a DateComboBox to RootPanel, it also demonstrates various ways to initialize the widget's size. public void onModuleLoad() {
DateComboBox cb1 = new DateComboBox();
cb1.setPixelSize(256, 32);
RootPanel.get().add(cb1, 10, 10);
// ----------
DateComboBox cb2 = new DateComboBox() {
@Override
protected void onLoad() {
super.onLoad();
WidgetHelper.setSize(this, getPreferredSize());
}
};
RootPanel.get().add(cb2, 10, 60);
// ----------
DateComboBox cb3 = new DateComboBox() {
@Override
protected void onLoad() {
super.onLoad();
WidgetHelper.setSize(this, getPreferredSize().getWidth(), -1);
}
};
cb3.setHeight("5em");
RootPanel.get().add(cb3, 10, 110);
// ----------
DateComboBox cb4 = new DateComboBox() {
@Override
protected void onLoad() {
super.onLoad();
WidgetHelper.setSize(this, getPreferredSize());
setHeight("5em");
}
};
RootPanel.get().add(cb4, 10, 160);
}
Note: The glue code that makes adding GWT Mosaic widgets to GWT widgets trivial is in LayoutPanel.onLoad() method: @Override
protected void onLoad() {
super.onLoad();
Widget parent = findParent();
if (parent instanceof HasLayoutManager || parent instanceof Viewport) {
return;
}
GWT.log("====================== Parent of '" + this.getClass().getName()
+ "' ('" + parent.getClass().getName()
+ "') is not an instance of HasLayoutManager.", null);
// Set the initial size & layout
DeferredCommand.addCommand(new Command() {
public void execute() {
layout();
}
});
// Add to Resizable Collection
ResizableWidgetCollection.get().add(new ResizableWidget() {
public Element getElement() {
return LayoutPanel.this.getElement();
}
public boolean isAttached() {
return LayoutPanel.this.isAttached();
}
public void onResize(int width, int height) {
LayoutPanel.this.layout();
}
});
}A LayoutPanel if not added to a GWT Mosaic widget will call layout(), in a DeferredCommand, and initialize a monitor to monitor size changes (almost like Viewport).
|