[gwt-contrib] RFC: Fixed width column support in CellTable

882 views
Skip to first unread message

John LaBanca

unread,
Jan 5, 2011, 5:09:45 PM1/5/11
to Google Web Toolkit Contributors, Chris Ramsdale, Stephanie Brubaker, p...@google.com, Ray Ryan, Daniel Rice (דניאל רייס)‎
    Summary
    Currently, CellTable resizes naturally based on its content, which usually isn't the desired behavior. Columns change widths on each page based on the new contents, which looks bad when paging through a table. When clicking on a Cell to go into edit mode, the Column will resize to fit the editable input. We need to give users more control over the column width. Both of these issues can be seen in the Showcase sample (try paging and clicking on somebody's first name).
    Technical Details
    There is one cross-browser approach that allows users to specify a width for some or all columns and still lets the table resize naturally in a predictable way:
    • Set the table-layout to fixed
    • Set the table width to 100% (optional)
    • Set the width of some or all columns using the col tag

    The following native behaviors are consistent across all browsers. In all cases, the default behavior works very well.
    • If the widths of all columns are set, the width becomes a weight and the columns are resized proportionally
    • If the widths of some columns are set, those columns are fixed and the remaining width is divided evenly over the other columns
      • This allows one or more columns to take the remaining width
      • This also means that the width is divided consistently, not based on the content
    • If the table's width is too narrow to fit the specified column widths, the table will overflow
      • The width in the col tag is basically the hard minimum width
      • Columns that do not have widths are invisible if their is no room for them

    We also want to support a table with a width other than 100% (ex. no width specified or a fixed pixel size). However, if the table width is not specific and table-layout is fixed, columns are 0px wide (invisible) by default, which is not a good default use case. In practice, we must do at least one of the following for columns to be visible:
    • Set the table width to 100% (or some other size)
    • Set the width of all columns


    Proposal

    Column Widths
    I propose that we add an interface ColumnWidths that returns the desired width of the specified column. The default implementation will have a map of Column objects to the desired width. CellTable will have a method to setColumnWidths(ColumnWidths). This works for both fixed layout and non-fixed layout (normal) CellTables.

    interface ColumnWidths {
    /**
    * Get the desired width of the specified column.
    *
    * @param index the index of the column
    * @param column the Column
    * @return the width as a string (ex. 200px)
    */
    String getColumnWidth(int index, Column<?,?> column);
    }

    class DefaultColumnWidths {
    void clearColumnWidth(Column column) {
    map.remove(column);
    }

    String getColumnWidth(int index, Column<?,?> column) {
    return map.contains(column) ? map.get(column) : null;
    }

    void setColumnWidth(Column column, int width, Unit unit) {
    map.put(column, width + unit.getType());
    }
    }

    class CellTable {
    ...
    // Update the column widths.
    void refreshColumnWidths();

    // Set the column widths.
    void setColumnWidths(ColumnWidths);
    ...
    }

    Users can use the DefaultColumnWidths implementation, or they can implement their own version. For example, a user defined ContentColumnWidths could base the column width on the content in the table. This approach ties the column width to the view, not to the Column itself, so users can use different widths for mobile and desktop views.


    Fixed Layout

    In addition, we need a way to set the table layout to fixed instead of the default of auto. The main difference is that with "auto" layout, a column will never truncate contents; it will expand as needed, even exceeding the specified width. With "fixed" layout, the content will be truncated. Also, if the table width's is set with auto layout, the column sizes are based on the content and are unpredictable. Auto layout is useful when the table data is static (or at least does not page). Fixed layout is preferred when paging, or when you don't want the column width to change automatically every time the content changes.

    Fixed: truncate cells, divide extra table width evently over columns (predictable)
    Auto: never truncate cells, divide extra table width based on cell contents (unpredictable)

    Most users will want a fixed table layout so the specified column widths are taken as more than suggestions, but they may not know that because its an obscure DOM style property. I propose that we add overrides to setColumnWidths() that take a boolean to indicate fixed/auto table layout and a table width parameter. The default method will set the table layout to fixed.

    NOTE: If we just add a simple boolean setter to toggle between fixed/auto layout, users may not discover it and wonder why their ColumnWidths don't work as expected.

    class CellTable {
    ...

    // Set the column widths and set table layout to fixed, but don't change the table width.
    void setColumnWidths(ColumnWidths);

    // Set the column widths and table layout, but don't change the table width.
    void setColumnWidths(ColumnWidths, boolean isFixed);

    // Set the column widths, table layout, and table width
    void setColumnWidths(ColumnWidths, boolean isFixed, String width);

    // Set the column widths, table layout, and table width
    void setColumnWidths(ColumnWidths, boolean isFixed, int width, Unit unit);
    ...
    } 

    Scrolling

    The next feature to add to CellTable is a scrolling content area with a fixed header and footer. This proposal works because we can separate the header/data/footer tables into three seperate tables and still line them up reliably.


    Unsupported Features

    Maximum Size
    We will not support a maximum size on columns because not all browsers support the max-width style attribute.

    Mix Fixed and Proportional Distribution
    In the proposal above, you can fix the width of some columns and distribute the remaining width equally over the remaining columns, but you cannot distribute the remaining width proportionally. This isn't natively supported and would require active layout, which isn't worth the performance cost. Users could implement a version of ColumnWidths that supported this by providing new ColumnWidths every time the table is resized.

    User Resizeable Columns
    Under the above proposal, we will not support user resizable columns where the user can click and pull a column header to change its width. The calculations that PagingScrollTable use to support this requires the user to specify the width in pixels (the above proposal allows relative sizes) and requires expensive active layout that causes performance issues. It should still be possible for users to build on top of the above proposal, but it isn't a feature we plan to add to CellTable.

Thanks,
John LaBanca
jlab...@google.com

Stephen Haberman

unread,
Jan 5, 2011, 5:54:10 PM1/5/11
to google-web-tool...@googlegroups.com

Sounds really cool.

That being said, you said "RFC"...so... :-)

> This approach ties the column width to the view, not to the Column
> itself, so users can use different widths for mobile and desktop
> views.

Personally, I'd rather just do:

column.setWidth(20, Unit.PX);

Than the ColumnsWidths abstraction.

...besides, in canonical GWT MVP, wouldn't you do something like:

interface XxxView { HasData<T> table(); }

class MobileView {
void foo() {
columnA.setWidth(20, Unit.PX);
}
}

class DesktopView {
void foo() {
columnA.setWidth(200, Unit.PX);
}
}

...are you really going to want to share widths across desktop/web? Or,
even if you do, should that be something your presenter worries about?
I suppose if the widths relative--but even if the widths are relative,
it still seems like that is a code reuse concern between the views, and
not something the presenter should be concerned about.

(Look at that, Column is even an abstract class, so you can add setWidth
without a breaking change. ;-) )

- Stephen

John LaBanca

unread,
Jan 5, 2011, 6:09:29 PM1/5/11
to google-web-tool...@googlegroups.com
Column#setWidth() is simpler, but we need a discoverable way to allow the user to set the table to use fixed width layout and set a reasonable table width.  Otherwise, they'll either get weird behavior because layout:auto doesn't truncate, or columns won't show up because columns are 0 width by default in fixed width layout.  We could JavaDoc it, but I wish it was more discoverable in the API.

Thanks,
John LaBanca
jlab...@google.com


Stephen Haberman

unread,
Jan 5, 2011, 6:24:08 PM1/5/11
to google-web-tool...@googlegroups.com

> We could JavaDoc it, but I wish it was more discoverable in the API.

Hm, would a CellTable overload of setWidth be good enough, e.g.:

table.setWidth(String width, boolean isFixed);

This seems more discoverable than the get/setLayoutFixed toggle that,
I agree as you mentioned, might get lost in the CellTable method list.

- Stephen

John LaBanca

unread,
Jan 5, 2011, 6:33:57 PM1/5/11
to google-web-tool...@googlegroups.com

Thanks,
John LaBanca
jlab...@google.com


On Wed, Jan 5, 2011 at 6:24 PM, Stephen Haberman <ste...@exigencecorp.com> wrote:

>  We could JavaDoc it, but I wish it was more discoverable in the API.

Hm, would a CellTable overload of setWidth be good enough, e.g.:

   table.setWidth(String width, boolean isFixed);
Heh - I just tried that exact thing.  It seems pretty reasonable to me.


This seems more discoverable than the get/setLayoutFixed toggle that,
I agree as you mentioned, might get lost in the CellTable method list.

Thomas Broyer

unread,
Jan 5, 2011, 7:39:50 PM1/5/11
to google-web-tool...@googlegroups.com
Some comments:

 - NIT: DefaultColumnWidths#getColumnWidth implemnt could be simplified to a simple map.get(), given that Map#get returns null if an entry is not present for the given key.

 - at least when using fixed layout, cells should have overflow:hidden (maybe it's the case already, I haven't checked), preferably only made the default in CellTable.BasicStyle (and possibly pointed out in the javadoc for CellTable.Style.cellTableCell for those who won't use the DefaultStyle, and will then have to provide the appropriate 'overflow' value, where overflow:visible is probably not the expected behavior).

 - looking around for more doc on fixed layout (it's been a while that I haven't looked at it), I stumbled on that sentence in the MSDN: "If no width is specified for the table, it renders by default with width=100%." http://msdn.microsoft.com/en-us/library/ms531161(v=vs.85).aspx (not to mention what we all already know: "Setting the property to fixed significantly improves table rendering speed, particularly for longer tables") I don't know how other browsers behave when no width is specified for the table (you said width=0 ?)

 - scrolling would only be possible with fixed layout (at least the way you explain it with independent tables; but there seems to be –realy hackish!– alternatives: http://www.imaputz.com/cssStuff/bigFourVersion.html )

 - how about colgroups? (for styling: e.g. add a border around 2 columns) Those would be used when constructing the <col> for setting their widths. At a minimum, rename your proposed refreshColumnsWidth to refreshColumns, refreshColumnStyles or refreshColumnsMeta (including style names and widths on columns) to allow colgroup support to be added later.

Otherwise, sounds great, and I like Stephen's proposal about the overloaded setWidth instead of overloaded setColumnWidths. You didn't mention it but there might be a need for a getTableLayout (with enum FIXED vs. AUTO) or isFixedLayout or similar (if an enum is used, it should be possible to pass an enum in the overloaded setWidth, and an additional setTableLayout could still be useful, e.g. for use in UiBinder: e.g. <c:CellTable ui:field="table" tableLayout="FIXED" width="100%" />)

Miroslav Pokorny

unread,
Jan 5, 2011, 9:31:20 PM1/5/11
to google-web-tool...@googlegroups.com
Minor suggestion but would not ColumnWidthSource be a better name than ColumnWidths, after all it is a source of column widths given a column. When i see classes w/ plural names, i think of classes w/ static helpers java.util.Collections. Guice is another example of this naming style.

John LaBanca

unread,
Jan 6, 2011, 11:21:55 AM1/6/11
to google-web-tool...@googlegroups.com
After giving this more thought, I think the best option is to get rid of ColumnWidths and roll it into CellTable directly, so you would have:
CellTable.setColumnWidth(Column col, double width, Unit unit)
CellTable.setColumnWidth(Column col, String)

I suggest this for two reasons:
  1. We don't need a CellTable#refreshColumnWidths() method because CellTable always knows when the width changes.  Both the ColumnWidths proposal and Column#setWidth() have the problem that the table isn't notified of the change.  We could add event handlers to ColumnWidths/Column, but thats a bit of overkill.  
  2. I'm not 100% sold on Column#setWidth() because its possible to use the same column in two tables but have different widths.  For example, a source table and a destination table that both contain a summary column.
Thanks,
John LaBanca
jlab...@google.com


On Wed, Jan 5, 2011 at 9:31 PM, Miroslav Pokorny <miroslav...@gmail.com> wrote:
Minor suggestion but would not ColumnWidthSource be a better name than ColumnWidths, after all it is a source of column widths given a column. When i see classes w/ plural names, i think of classes w/ static helpers java.util.Collections. Guice is another example of this naming style.

John LaBanca

unread,
Jan 6, 2011, 5:22:36 PM1/6/11
to google-web-tool...@googlegroups.com
I sent a patch for review that implements this and used it in the CellTable example in Showcase.  It looks much better and the API is straightforward.

Stephen Haberman

unread,
Jan 6, 2011, 5:34:17 PM1/6/11
to google-web-tool...@googlegroups.com

> CellTable.setColumnWidth(Column col, double width, Unit unit)

Yeah, I like this better.

That being said, I still prefer setWidth, because, thinking how
I'd create a table, with col.setWidth, I could do:

table.addColumn(makeFirstColumn());
table.addColumn(makeSecondColumn());
// ... more 1-line addColumn calls, which are easy to read

private Column makeFirstColumn() {
Column c = new Column() { ... };
c.setWidth(...);
return c;
}

Where as with a separate setColumnWidth call, I'll have to:

Column<T, ?> c1 = makeFirstColumn();
table.addColumn(c1);
table.setColumnWidth(c1, 20, PX);

Column<T, ?> c2 = makeSecondColumn();
table.addColumn(c2);
table.setColumnWidth(c2, 20, PX);

private Column makeFirstColumn() {
Column c = new Column() { ... };
return c;
}

Basically, keeping track of the c1, c2 variables obfuscates the
table.addColumn calls. I suppose a helper method would work:

addColumn(table, makeFirstColumn(), 20, PX);

private void addColumn(CellTable<T> t, Column<T, ?> c, int, unit) {
t.addColumn(c);
c.setColumnWidth(int, unit);
}

Eh.

> 1. We don't need a CellTable#refreshColumnWidths() method because


> CellTable always knows when the width changes.

True, this is nice, although there is precedence of having to call refresh
header/footer is you change things post-render.

> 2. I'm not 100% sold on Column#setWidth() because its possible to use the


> same column in two tables but have different widths.

...true, but it seems like a pretty small boundary case.

Nonetheless, I look forward to using the functionality.

Thanks!

- Stephen

Thomas Broyer

unread,
Jan 6, 2011, 6:17:37 PM1/6/11
to google-web-tool...@googlegroups.com


On Thursday, January 6, 2011 11:34:17 PM UTC+1, Stephen Haberman wrote:

> CellTable.setColumnWidth(Column col, double width, Unit unit)

Yeah, I like this better.

That being said, I still prefer setWidth, because, thinking how
I'd create a table, with col.setWidth, I could do:

    table.addColumn(makeFirstColumn());
    table.addColumn(makeSecondColumn());
    // ... more 1-line addColumn calls, which are easy to read

    private Column makeFirstColumn() {
      Column c = new Column() { ... };
      c.setWidth(...);
      return c;
    }

Where as with a separate setColumnWidth call, I'll have to:

    Column<T, ?> c1 = makeFirstColumn();
    table.addColumn(c1);
    table.setColumnWidth(c1, 20, PX);

    Column<T, ?> c2 = makeSecondColumn();
    table.addColumn(c2);
    table.setColumnWidth(c2, 20, PX);

    private Column makeFirstColumn() {
      Column c = new Column() { ... };
      return c;
    }

Basically, keeping track of the c1, c2 variables obfuscates the
table.addColumn calls.


You would probably refactor your code with an addFirstColumn(CellTable) method instead that creates the Column, sets its width and adds it to the CellTable.

vela.i...@gmail.com

unread,
Jan 21, 2011, 12:33:38 PM1/21/11
to google-web-tool...@googlegroups.com
Hi, I need to use this patch, but I'm really losed about how to download and use it.

How must I install this patch in my enviroment?

John LaBanca

unread,
Jan 21, 2011, 12:50:16 PM1/21/11
to google-web-tool...@googlegroups.com
The easiest way to get it is to wait for the GWT 2.2 milestone, which should be released soon.

You can also build GWT from trunk by following the directions at the link below.  You'll need a JDK, svn, and ant installed to build it.

Thanks,
John LaBanca
jlab...@google.com


On Fri, Jan 21, 2011 at 12:33 PM, <vela.i...@gmail.com> wrote:
Hi, I need to use this patch, but I'm really losed about how to download and use it.

How must I install this patch in my enviroment?

vela.i...@gmail.com

unread,
Jan 21, 2011, 12:54:35 PM1/21/11
to google-web-tool...@googlegroups.com
Thanks John, I will try to build from trunk but if I can't do it myself i will be waiting for the GWT 2.2 milestone to be released soon :)

Regards.
Reply all
Reply to author
Forward
0 new messages