fundamental problems with predictive layout

767 views
Skip to first unread message

Magnus

unread,
Sep 1, 2012, 6:09:28 PM9/1/12
to google-we...@googlegroups.com
Hi,

since I use GWT I have a fundamental problem with implementing a predictive layout. I did many resize operations using the onResize method. This method is called whenever a widget is resized. This is fine.
However, it is only possible to react on a resize event if one knows the new sizes!!

Consider a composed widget, with an outer panel and some child widgets. In order to resize the child widgets, I need to know the actual size of the outer panel. But this size is not known in most cases.
In almost every onResize method I was not able to detect the *new* size of the object being resized. Calling getOffset[Width|Height] nearly always results in wrong sizes, old sizes.

One method to deal with this is to schedule the onResize code after the current events are processed. But these retarded resizing codes cascade, when there are composite widgets that must delegate the resize event to their children. And cascading this stuff makes it slow.

So given a composite widget, with a horizontal fixed (left) component, and a variable (right) component: How can you make this resizable?

Thanks
Magnus

Ümit Seren

unread,
Sep 3, 2012, 4:09:44 AM9/3/12
to google-we...@googlegroups.com
How about using nested LayoutPanels? Or if you don't want to use LayoutPanels all the way but need resizing in one of the inner widget you could wrap it in a http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/user/client/ui/ResizeLayoutPanel.html

Andrei

unread,
Sep 3, 2012, 8:08:27 AM9/3/12
to google-we...@googlegroups.com
You can build any layout in GWT that you can build with plain HTML and CSS, i.e. any layout you like. I build very complex UIs in GWT, and I very rarely use resize handlers or getOffsetWidth()/getOffsetHeight() methods, because:

(a) I use a layout panel for page structure, an it resizes by itself as the browser window resizes;

(b) I use fluid HTML/CSS layouts for inside content, relying on the browser to reflow the content as the page size or the content size changes.

There are many good resources on building fluid layouts in HTML/CSS, and all of the suggested techniques can and should be used when using GWT.

Maybe if you can give a specific example of a problem you face, we can suggest a solution.

Magnus

unread,
Sep 3, 2012, 10:16:05 AM9/3/12
to google-we...@googlegroups.com

Am Montag, 3. September 2012 14:08:27 UTC+2 schrieb Andrei:

Maybe if you can give a specific example of a problem you face, we can suggest a solution.

Hello,
 
I actually need a composite panel "TournamentField" that consists of two widgets, a label and a button at the right of the label. I want that the label always consumes most available horizontal space, while the button has a fixed size.
 
[ label with variable width][button]
 
Below is the code.
(The HorizontalLayoutPanel is just a FlowPanel which sets "float:left;" on its children, as supposed by the article "Moving to standards mode" in the GWT docs.
 
As you can see, I even needed to use a hard coded size for the height of the label, because I do not know the default height...
 
This TournamentField is added to a LayoutPanel.
 
Magnus
 
-----
 
package bcs.client.mod.tournament.cmp;
 
...
 
public class TournamentField extends Composite implements RequiresResize
{
 private HorizontalLayoutPanel     pnl = new HorizontalLayoutPanel ();
 private Label                     lbl_Tournament = new Label ();
 private PushButton                btn_Select = new PushButton (">");
 private static final int XS_BTN = 15;
 private static final int YS = 25;
 
 public TournamentField ()
 {
  super ();
  init ();
 }
 
 private void init ()
 {
  initWidget (pnl);
  pnl.add (lbl_Tournament);
  pnl.add (btn_Select);
  gui.styleBorder(lbl_Tournament,"#FF55DD");
 }
 
 @Override
 public void onResize()
 {
  Size sp = gui.getSize(pnl); // size contains two ints: x and y
  
  btn_Select.setWidth(XS_BTN + "px");
 
  int lxs = sp.x - XS_BTN - 15;
 
  if (lxs < 0)
   lxs = 0;
 
  lbl_Tournament.setWidth(lxs + "px");
  lbl_Tournament.setHeight(sp.y + "px");
 
 }

}

 

Magnus

unread,
Sep 3, 2012, 10:24:49 AM9/3/12
to google-we...@googlegroups.com

Am Montag, 3. September 2012 10:09:45 UTC+2 schrieb Ümit Seren:
How about using nested LayoutPanels? Or if you don't want to use LayoutPanels all the way but need resizing in one of the inner widget you could wrap it in a http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/user/client/ui/ResizeLayoutPanel.html
 
 
Hi,
 
I am using LayoutPanels, and found out the following while debugging within GWT code:
 
When setting the sizes of a widget using the methods of LayoutPanel (setWidgetLeftWidth, and so on), the widget's onResize is not called. So I call it manually right after setting its sizes. But then, the new sizes are not known yet.
The several calls to setWidgetLeftWidth and others just seem to collect information, but the real resizing is done later. (I foudn something with a Scheduler, but I don't know what it does.)
 

So there are two questions:

 

- Do I really have to call the widget's onResize manually, after setting its size using the LayoutPanel's methods?

- How can I schedule this call, so that the widget's onResize is called after its parent has been resized?

 

Magnus

Ümit Seren

unread,
Sep 3, 2012, 10:38:40 AM9/3/12
to google-we...@googlegroups.com
Are you changing the sizes of the layoutPanels dynamically? Because if
you don't it should work out of the box.
If you are changing it dynamically then you might run into this bug
(http://code.google.com/p/google-web-toolkit/issues/detail?id=7188). I
found following code snippet in one of my projects:

layoutPanel.setWidgetSize(this.asWidget(), size);
/*workaround for bug
http://code.google.com/p/google-web-toolkit/issues/detail?id=7188*/
layoutPanel.animate(0,new AnimationCallback() {

@Override
public void onLayout(Layer layer, double progress) {
}

@Override
public void onAnimationComplete() {
layoutPanel.forceLayout();
}
});;


On the other hand if you have simple elements (like in your use case)
you could just rely on CSS settings (float, etc) as Andrei pointed
out, although depending on your actual use case you might have to
calculate the sizes and set them explicitly.
> --
> You received this message because you are subscribed to the Google Groups
> "Google Web Toolkit" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/google-web-toolkit/-/TEK0iLk4aYkJ.
>
> To post to this group, send email to google-we...@googlegroups.com.
> To unsubscribe from this group, send email to
> google-web-tool...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/google-web-toolkit?hl=en.

Jens

unread,
Sep 3, 2012, 10:41:16 AM9/3/12
to google-we...@googlegroups.com
In pure HTML I would do it like:


you can drag the vertical splitter to change the size of the result and the label <div> fills all the space while the button is float:right. No need to use any onResize() JavaScript as the browser takes care of the size. Maybe you can omit the  div that clears the float:right.

You should use as much CSS as you can, so you don't have to worry about resizing your widgets. Let the browser do it. 

-- J.

Kara Rawsonkara

unread,
Sep 1, 2012, 6:23:58 PM9/1/12
to google-we...@googlegroups.com
you have to call force layout to calc sizes in the new space. 

k

Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.

Andrei

unread,
Sep 3, 2012, 5:25:04 PM9/3/12
to google-we...@googlegroups.com
Magnus,

Jens is right: all you need is a simple FlowPanel with a Label and a button inside. Set the button style to "float: right", set its width (which, I assume, is constant), and add a right margin to your label which is wider than the button. That's it. Your generated code will be 10 times smaller (layout panels produce a lot of HTML), and your pages will reflow much faster.

You don't need to worry about the height: a FlowPanel will expand automatically to accommodate its contents. If you want, you can fix its height, or you can use min-height and/or max-height CSS rules. You can set a width of the FlowPanel in pixels or as percentage of its parent's width, or you can let it expand with the content up to a specified maximum. You can allow the content to wrap inside the widget, or you can force it to show ellipsis when the label gets too long, or you can force a scrollbar, etc. There are a lot of options available to you in CSS. I use layout panels only for page structure, and I never use HorizontalPanel or VerticalPanel - they offer nothing that cannot be easily done with FlowPanels and CSS, while they are less flexible as you have discovered.

My advice: when building a layout, forget about widgets. Think how you can do it in pure HTML/CSS, ideally without any <table> elements. Once you do it, the choice of widgets becomes obvious.

Magnus

unread,
Sep 4, 2012, 1:48:59 AM9/4/12
to google-we...@googlegroups.com

Am Montag, 3. September 2012 16:41:16 UTC+2 schrieb Jens:
 
you can drag the vertical splitter to change the size of the result and the label <div> fills all the space while the button is float:right. No need to use any onResize() JavaScript as the browser takes care of the size. Maybe you can omit the  div that clears the float:right.

You should use as much CSS as you can, so you don't have to worry about resizing your widgets. Let the browser do it. 
 
 
Hi Jens,
 
this looks good. I often read about letting the browser do most of the layout is much faster.
 
The problem is to integrate this pure HTML/CSS code (your example) into an existing environment within GWT, i. e. into a LayoutPanel. The place, where the composite widget should be inserted, is a LayoutPanel. I believe that whenever I inserted something into a LayoutPanel it gets invisible because of size 0. This in turn results in setting the sizes explicitely and so on.
 
So how would one do this (your example) within GWT? Which Panels to use for the three DIVs? And how to avoid zero sizes when inserting into the LayoutPanel...
 
Magnus

Magnus

unread,
Sep 4, 2012, 1:58:12 AM9/4/12
to google-we...@googlegroups.com
Hi Andrei,
 
thanks, I'll change the composite widget to be a FlowPanel, but I am sure that it gets 0 sizes when inserted into the LayoutPanel. But it's a precondition that the outer environment is a LayoutPanel, because this is used - as you mentioned - for the page structure.
 
I understand that you recommend doing the overall page layout using LayoutPanels, but doing the small stuff concerning widgets with HTML/CSS? Then there must always be a "border crossing" between the outer layout panels and the inner HTML/CSS widgets. And exactly at this border you have to insert a widget into a layout panel, right? How do you do this? When I do it, the widget gets 0 sizes and I have to hard code the sizes explicitely, resulting in ugly code...
 
Magnus
Message has been deleted

Magnus

unread,
Sep 4, 2012, 2:46:57 AM9/4/12
to google-we...@googlegroups.com

Am Montag, 3. September 2012 10:09:45 UTC+2 schrieb Ümit Seren:
How about using nested LayoutPanels? Or if you don't want to use LayoutPanels all the way but need resizing in one of the inner widget you could wrap it in a http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/user/client/ui/ResizeLayoutPanel.html
 
Hi Seren,
 
I do want to use LayoutPanels, but they don't want to be used by me! :-)
 
Magnus

Magnus

unread,
Sep 4, 2012, 2:48:25 AM9/4/12
to google-we...@googlegroups.com
Hello,
 
I have changed the composite widget to use just HTML/CSS, but the label is not visible (size 0) after adding the composite widgetto a LayoutPanel. In addition I have to hard code the height of the widget, without knowing how much height a button and a lable need in the different browsers...
 
See the simplified code for the composite widget (TournamentField), and also the code for the surrounding layout (OverallLayout) (which is not a real world example but shows how the TournamentField is inserted).
 
I believe that the inner widget would work, if it wasn't placed into a LayoutPanel...
 
Magnus
 
--
 
public class TournamentField extends Composite
{
  private FlowPanel     pnl = new FlowPanel ();

  private Label         lbl_Tournament = new Label ();
  private PushButton    btn_Select = new PushButton (">");
  private static final int XS_BTN = 15;
 
 
 public TournamentField ()
 {
  super ();
  init ();
 }
 
 private void init ()
 {
  initWidget (pnl);
  pnl.add (lbl_Tournament);
  pnl.add (btn_Select);
 
  btn_Select.getElement().getStyle().setFloat(Style.Float.RIGHT);
  btn_Select.setWidth(XS_BTN + "px");
  lbl_Tournament.getElement().getStyle().setMarginRight(XS_BTN + 5,Unit.PX);
 }
}
 
---

public class OverallLayout extends Composite implements RequiresResize
{
 private LayoutPanel     pnl = new LayoutPanel ();
 private TournamentField fld_Tournament = new TournamentField ();
 private static final int YS_FLD = 25;
 
 public OverallLayout ()

 {
  super ();
  init ();
 }
 
 private void init ()
 {
  initWidget (pnl);
  pnl.add (fld_Tournament);
  pnl.setWidgetHorizontalPosition(fld_Tournament, Layout.Alignment.END);
 }
 @Override
 public void onResize()
 {
  Size sp = gui.getSize (pnl); // calls getOffsetWidth/Height
 
  int d = (sp.y - YS_FLD) / 2;
  int xs = some_value;
  pnl.setWidgetRightWidth (fld_Tournament,0,Unit.PX,xs,Unit.PX);
  pnl.setWidgetTopHeight  (fld_Tournament,d,Unit.PX,FLD_YS,Unit.PX);
 }
}
 

Andrei

unread,
Sep 4, 2012, 7:26:28 AM9/4/12
to google-we...@googlegroups.com
When you add a widget to a LayoutPanel, it automatically takes the entire space within that LayoutPanel, unless you use .setWidgetTopHeight or similar methods to specify which part of the LayoutPanel this widget should occupy. If your FlowPanel has size 0, this means that your LayoutPanel has size 0. Where does your LayoutPanel take it size from? Typically it should take it from the root panel. This is the point where you may have a problem. LayoutPanels always have heigh = 0, unless they get their height from a parent widget, or their size is set explicitly in code.

By the way, implements RequiresResize is unnecessary in your code, because you init your Composite as a LayoutPanel, and it already has all the functionality that you need.

Magnus

unread,
Sep 4, 2012, 8:10:15 AM9/4/12
to google-we...@googlegroups.com
Hi Andrei,
 
I can verify that the LayoutPanel is not zero sized! It's about 400px x 60px. It's only the label that gets height 0, not the button.
 
I would be glad I could copy the generated HTML from the Firefox inspection panel, but this is not possible.
 
However, I tried the following:
I added an AbsolutePanel as an intermediate panel to the LayoutPanel, and added my widgets to the AbsolutePanel. The AbsolutePanel takes the whole space wighin the LayoutPanel. I thought this should isolate the widget from the LayoutPanel. But the label remains invisible.
 
Magnus

Magnus

unread,
Sep 4, 2012, 9:11:12 AM9/4/12
to google-we...@googlegroups.com
Hi,
 
I managed to copy the generated HTML code with chrome. The outer div is the AbsolutePanel that acts as the intermediate panel between the LayoutPanel and the widget:
 
<div style="position: relative; overflow: hidden; border: 1px solid rgb(0, 255, 0); " cls="AbsoluteInCell">
 <div style="border: 1px solid rgb(170, 170, 170); width: 407px; height: 55px; ">
  <div class="gwt-Label" style="margin-right: 20px; "></div>
  <div tabindex="0" class="gwt-PushButton gwt-PushButton-up" role="button" style="float: right; width: 15px; " aria-pressed="false">
  <input type="text" tabindex="-1" style="opacity: 0; height: 1px; width: 1px; z-index: -1; overflow: hidden; position: absolute; ">
   <div class="html-face">&gt;
   </div>
  </div>
 </div>
</div>

Magnus

unread,
Sep 6, 2012, 1:49:17 AM9/6/12
to google-we...@googlegroups.com
Hello,

thank you for your help! I have got it running now, but I am not happy with it.

After trying around with integrating the CSS code provided here into the outer LayoutPanel, I soon found myself traversing discussion groups about HTML/CSS. In these groups, people are also talking about browser-specific things, bugs and workarounds. However, I remember that it was the promise of GWT that we never have to worry about browser-specific things again, because the GWT compiler automatically produces the correct code for all browsers, including any workarounds and all that.

There seem to be two different worlds coming together when making GUIs in GWT:
On the one hand. there is the "outer layer", the overall page layout, that you can build using the "GWT layout system" including the set of powerful layout panels.
On the other hand there is the "inner layer", the widgets, controls, buttons, labels, checkboxes and so on.

I believe that the client code for the outer layer is mapped into the clients browser-specific world by the GWT compiler. But the inner layer will reach the client without any mapping. Doesn't this ruin our browser independency?

It was said in this thread, but is this the way to use GWT?

Magnus

Andrei

unread,
Sep 6, 2012, 11:30:55 AM9/6/12
to google-we...@googlegroups.com
Most modern browsers are pretty consistent with CSS2 and even CSS3. There are still some differences, but in a few instances where I ran into them, GWT could not help me. Sometimes you have to specify -moz or -webkit specific rules in your CSS file. In some situations (e.g. vertical-align in Firefox vs Chrome) the result is a few pixels off, but only a very good UI designer would even notice. I bet in most of the discussions that you read on CSS issues, people are talking about older versions of IE and Firefox 3.

There are many great GWT widgets that insulate you from major browser issues, especially LayoutPanel, PopupPanel, DialogBox, DataGrid. But it's too expensive to try and figure out solutions for minor issues, so they still remain.

If fact, I think that trying to be fully cross-browser hurts GWT. GWT's popup panel is a table where every rounded corner is its own cell, even though all modern browsers support border-radius rules. I would love to see GWT drop support for IE

Andrei

unread,
Sep 6, 2012, 11:37:11 AM9/6/12
to google-we...@googlegroups.com
Sorry, I meant to say that I would love GWT to drop support for IE 7, an maybe even IE 8, if it allows to greatly simplify the library, reduce the generated code size, significantly reduce compilation time, and speed up development of new features.

Abraham Lin

unread,
Sep 6, 2012, 11:46:08 AM9/6/12
to google-we...@googlegroups.com
I think you're misunderstanding how GWT works. Everything written using GWT compiles down to HTML, CSS, and other "native" web technologies - there is no difference between panels (LayoutPanels or otherwise) and widgets as far as "mapping" goes.

What seems to be the issue here is that the abstraction provided by GWT can be rather leaky. If you don't understand what the generated code looks like and how it is rendered, then you will likely run into problems. For example, the HTML markup you posted previously contains an empty, (presumably) statically positioned DIV - this is rendered by the browser as an element with zero height. It's not a problem with GWT per se; if anything, the problem is that your expectations don't match those of the framework.

It's also worth noting that while GWT offers fairly reliable abstractions for some things (e.g. basic layout, scripted behaviors), it does not eliminate the need to understand how CSS influences presentation. What it does do is provide mechanisms for cleanly separating browser-specific requirements, so that you or someone else can do the hard work just once and have it available for re-use in the future.

Magnus

unread,
Sep 7, 2012, 1:17:23 AM9/7/12
to google-we...@googlegroups.com
Hi Abraham,

the only empty DIV I see is the label, and it's empty because the label does not contain text.

Do you agree to the statement posted in this thread that the GWT's layout panels should be used for the overall page layout (defining the main areas of a page) and that the small layouts (widgets and so on) should be positioned with CSS?

Maybe you misunderstood my posting: I also said that GWT compiles to HTML and CSS. But it produces different compilations for different browsers. In contrast, everything you put directly into the element attributes will go unchanged into the client.

However, even Google states that in strict mode many panels do not work as expected, e. g. HorizontalPanel, which should be replaced by a FlowPanel where all children should float to the left (which does'nt work in some cases).

The goal was "predictive layout", but it seems to be more like "trial and error", at least in some cases.

Magnus

Magnus

unread,
Sep 7, 2012, 1:31:11 AM9/7/12
to google-we...@googlegroups.com
Hi Andrei!


There are many great GWT widgets that insulate you from major browser issues, especially LayoutPanel, PopupPanel, DialogBox, DataGrid. But it's too expensive to try and figure out solutions for minor issues, so they still remain.


Yes, they do. I built my app with these panels and it works great. I also think that it's ok if you have to try and figure out solutions for minor issues. But I would like to have a general rule how to integrate these minor solutions into the overall layout, e. g. into a surrounding LayoutPanel?

For example, the solution posted by Jens works perfectly in isolation, but when I put it into a LayoutPanel it didn't work at all.

Magnus

Jens

unread,
Sep 7, 2012, 4:46:23 AM9/7/12
to google-we...@googlegroups.com
For example, the solution posted by Jens works perfectly in isolation, but when I put it into a LayoutPanel it didn't work at all.

Really? For me it works. Copy the example UiBinder xml and put it into a RootLayoutPanel:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <ui:style>
        .layer {
            background-color: red;
        }

        .label {
            background-color: orange;
            margin-right: 70px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .button {
            width: 50px;
            float: right;
        }

        .clear {
            clear: right;
        }
    </ui:style>
    <g:LayoutPanel>

        <g:layer top="20" bottom="20" left="20" width="200">
            <g:HTMLPanel styleName="{style.layer}">
                LEFT
            </g:HTMLPanel>
        </g:layer>

        <g:layer top="20" height="60" left="240" right="20">
            <g:HTMLPanel styleName="{style.layer}">
                <div>
                    <g:Button addStyleNames="{style.button}">&gt;</g:Button>
                    <g:Label addStyleNames="{style.label}">Label Label Label</g:Label>
                    <div class="{style.clear}"></div>
                </div>
                <div>
                    <g:Button addStyleNames="{style.button}">&gt;</g:Button>
                    <g:Label addStyleNames="{style.label}">Label Label Label</g:Label>
                    <div class="{style.clear}"></div>
                </div>
            </g:HTMLPanel>
        </g:layer>

        <g:layer top="100" bottom="20" left="240" right="20">
            <g:HTMLPanel styleName="{style.layer}">
                CENTER
            </g:HTMLPanel>
        </g:layer>

    </g:LayoutPanel> 
</ui:UiBinder>  

Abraham Lin

unread,
Sep 7, 2012, 10:29:34 AM9/7/12
to google-we...@googlegroups.com
the only empty DIV I see is the label, and it's empty because the label does not contain text.

Right, and that's why it has zero height as you observed.
 
Do you agree to the statement posted in this thread that the GWT's layout panels should be used for the overall page layout (defining the main areas of a page) and that the small layouts (widgets and so on) should be positioned with CSS?

Generally, yes. Granted, the absolute positioning scheme isn't appropriate for every use case, but if your entire application is GWT-based, then that's the way to go.
 
Maybe you misunderstood my posting: I also said that GWT compiles to HTML and CSS. But it produces different compilations for different browsers. In contrast, everything you put directly into the element attributes will go unchanged into the client.

The different compilations exist to produce mostly-uniform behavior across browsers, but only to a certain extent. It is generally not possible to avoid having to write your own CSS and deal with the browser-specific quirks that come with it.
 
However, even Google states that in strict mode many panels do not work as expected, e. g. HorizontalPanel, which should be replaced by a FlowPanel where all children should float to the left (which does'nt work in some cases).

This is a difference in expectations. In this case, the FlowPanel approach is not a drop-in substitute; it does work if you ensure that the FlowPanel is wide enough to prevent wrapping of the children (and that the children are statically positioned, etc.). For HorizontalPanel specifically, there shouldn't be any reason why it wouldn't work properly in strict mode, though using tables for layout has been frowned upon in the web development world for quite some time now (whether that's justified is a whole different story).
 
The goal was "predictive layout", but it seems to be more like "trial and error", at least in some cases.

While I wouldn't necessarily consider it "trial and error," using CSS effectively certainly has a steep learning curve. Again, the "issue" is that GWT is a leaky abstraction - you still need to know how CSS works (and in some cases, what markup is being generated by GWT). If you have the necessary knowledge CSS knowledge, then the layout *is* predictable.

-Abraham
Reply all
Reply to author
Forward
0 new messages