My favorites | Sign in
Logo
                
Search
for
Updated Jul 14, 2009 by georgopoulos.georgios
Labels: Featured
MixingWithGWT  
Mixing GWT widgets (panels) with GWT Mosaic widgets.

Introduction

GWT 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.

Details

Adding GWT Widgets to GWT Mosaic panels

All 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 1

Adding 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 2

This 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 panels

Like 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 3

This 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).


Sign in to add a comment
Hosted by Google Code