Consider a GWT application whose UI has tabs. In one tab there are a few fixed size elements, followed by a DataGrid
.
_______________________________________________
| ______ |
| Tab1 / Tab2 \ Tab3 |
|_____/ \________________________________|
| Fixed size widgets here. || |
| Fixed size widgets here. || |
| ---------------------- || |
| Datagrid headings tab scrollbar -->|| |
| cell cell cell cell || || |
| cell cell cell cell || || |
| cell cell cell cell ||<-- datagrid || |
| cell cell cell cell || scrollbar || |
| cell cell cell cell || || |
| cell cell cell cell || || |
-----------------------------------------------
I want the DataGrid
to fill the remaining height in the window, and to change height if the browser window is resized, but with the constraint that it does not get smaller than a specified minimum height (say, 3 rows, not including headings). In this application, there are usually only about 10 rows in the data grid.
There are two scrollbars. I want them to behave like this:
The datagrid scrollbar allows navigation of the rows in the datagrid. It should not be displayed if all rows in the datagrid can be seen.
The tab scrollbar scrolls the contents of the tab, leaving the tab selectors visible. Usually this is hidden, because the datagrid is contained entirely within the browser window, and the datagrid scrollbar can be used to view the contents. However, the datagrid has a minimum height, and if there is not room for this in the browser window, then the tab scrollbar will be visible.
My question is: how do I achieve this?
I have some sample code. First, the module is loaded like this:
public class Proj implements EntryPoint {
public void onModuleLoad() {
RootLayoutPanel.get().add(new App());
//RootPanel.get("app").add(new App());
}
}
I think I need the RootLayoutPanel
version rather than the RootPanel
, but I'd welcome comments on that.
Now some UiBinder
code:
<!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"
xmlns:d="urn:import:com.google.gwt.user.cellview.client"
>
<ui:style>
.foo {
min-height: 100px;
max-height: 300px;
}
</ui:style>
<g:TabLayoutPanel ui:field="tabLayout" barUnit="PX" barHeight="30" >
<g:tab>
<g:header> Tab One </g:header>
<g:ScrollPanel height="100%"> <!-- Hack? height="100%":-->
<g:FlowPanel>
<g:Label text="Window.getClientHeight():"/>
<g:Label ui:field="label" text="Window.getClientHeight()"/>
<g:Label text="grid.getElement().getAbsoluteTop():"/>
<g:Label ui:field="label2" text="grid.getElement().getAbsoluteTop()"/>
<d:DataGrid styleName='{style.foo}' ui:field="grid"/>
</g:FlowPanel>
</g:ScrollPanel>
</g:tab>
</g:TabLayoutPanel>
</ui:UiBinder>
and finally the definition of class App
:
public class App extends ResizeComposite {
@UiTemplate("App.ui.xml")
interface ThisUiBinder extends UiBinder<Widget, App> {
}
private static ThisUiBinder uiBinder = GWT.create(ThisUiBinder.class);
@UiField
TabLayoutPanel tabLayout;
@UiField
Label label;
@UiField
Label label2;
@UiField (provided=true)
DataGrid grid;
static class Data {
String foo;
public Data(String foo) {
this.foo = foo;
}
}
private static final List<Data> DATA = Arrays.asList(
new Data("A"), new Data("Q"), new Data("w"),
new Data("e"), new Data("r"), new Data("t"),
new Data("y"), new Data("u"), new Data("i"),
new Data("o")
);
private final int MINSIZE = 100; // of grid, in px
public App() {
grid = new DataGrid<Data>();
TextColumn<Data> fooColumn = new TextColumn<Data>() {
@Override
public String getValue(Data object) {
return object.foo;
}
};
grid.addColumn(fooColumn, "foo");
grid.setRowCount(DATA.size(), true);
grid.setRowData(0, DATA);
initWidget(uiBinder.createAndBindUi(this));
//grid.setHeight("100%"); // hack, 100% of what?
//tabLayout.setHeight("100%"); // hack, 100% of what?
onResize(); // Hack!
}
public void onResize() {
label.setText(String.valueOf(Window.getClientHeight()));
// Note that this is 0 on the first call (from our constructor),
// which does not give the desired result!
label2.setText(String.valueOf(grid.getElement().getAbsoluteTop()));
// Works very badly! Jumpy and eratic!
int newsize = Window.getClientHeight() - grid.getElement().getAbsoluteTop();
if (newsize > MINSIZE) {
grid.setHeight(String.valueOf(newsize)) + "px");
}
}
}
The mechanism in onResize() not only smells bad to me (it was implemented as an experiment), but it also works laughably badly.
Is there a neat way to do this purely in CSS? Or does something have to be done in onResize()? One of my motivations for using GWT is to avoid problems caused by variations between browsers, so is pure CSS the right way to go?
I have set height
to 100%
in various places as a hack, to see what difference it makes. I would dearly love to understand properly how this all hangs together - any reading suggestions would be most welcome. I have also tried following the guidelines at http://www.dave-woods.co.uk/100-height-layout-using-css/, e.g. using the CSS:
html, body {
height: 100%;
}
I also found this informative: https://stackoverflow.com/a/11866677/685715. But it didn't fix my problem.