PDA

View Full Version : [CLOSED] Height/width conversions from percentages to pixels



dmallory
29 Apr 2009, 8:31 AM
My team has encountered the following issue in 2.0-M1, including a build from svn as of last weekend.

On various combinations of layouts, seemingly when a CardLayout is involved, percentage height/width values are converted to raw pixels in the final HTML. For example, 100% becomes 100px, 95% becomes 95px, etc. This causes significant problems for applications with heavy height/width 100% usage. Workarounds require deriving height/weight more explicitly from parents or reworking the component hierarchy to sidestep the problematic combinations.

For example, the following code:


ContentPanel searchContentPanel= new ContentPanel();
searchContentPanel.setId("broken-width-example-contentpanel-id");
searchContentPanel.setLayout(new RowLayout(Orientation.VERTICAL));
searchContentPanel.setWidth("100%");
searchContentPanel.setHeight("100%");
searchContentPanel.setHeaderVisible(true);
searchContentPanel.setHeading("Search");
searchContentPanel.setBorders(false);
searchContentPanel.setBodyBorder(false);
searchContentPanel.setFrame(true);
searchContentPanel.add(new LabelField("Search Goes Here"));
LayoutContainer layoutContainer = new LayoutContainer();
layoutContainer.setId("broken-width-example-layout-id");
layoutContainer.setWidth("100%");
layoutContainer.setHeight("100%");
layoutContainer.setLayout(cardLayout);
layoutContainer.add(searchContentPanel);

produces the following HTML:



<div id="broken-width-example-layout-id" style="width: 100%; height: 100%;">
<div id="broken-width-example-contentpanel-id" class="x-panel-noborder x-panel" style="border-width: 0px; width: 100px;">
<div class="x-panel-tl">
<div class="x-panel-tr">
<div class="x-panel-tc">
<div id="x-auto-222" class="x-small-editor x-panel-header x-unselectable" unselectable="on">
<div id="x-auto-220" class="x-icon-btn x-nodrag x-panel-inline-icon x-hide-display" style="cursor: default; float: left;"/>
<div id="x-auto-221" class="x-panel-toolbar" style="overflow: visible;">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr/>
</tbody>
</table>
</div>
<span id="x-auto-222-label" class="x-panel-header-text">Search</span>
</div>
</div>
</div>
</div>
<div class="x-panel-bwrap">
<div class="x-panel-ml">
<div class="x-panel-mr">
<div class="x-panel-mc">
<div class="x-panel-body x-panel-body-noborder" style="overflow: hidden; width: 88px; height: 65px;">
<div id="x-auto-223" class="x-form-label">Search Goes Here</div>
</div>
</div>
</div>
</div>
<div class="x-panel-bl x-panel-nofooter">
<div class="x-panel-br">
<div class="x-panel-bc">
<div class="x-panel-footer"/>
</div>
</div>
</div>
</div>
</div>
</div>


Note that the width is 100px rather than 100% in the second div. We encountered this on upgrade on a project that had otherwise rendered correctly using GXT 1.2.3.

sven
29 Apr 2009, 8:36 AM
Using % sizes in Components is something you hsould not do, especially not if you use layouts to manage sizes. You should also not do that with 1.2

dmallory
29 Apr 2009, 8:42 AM
Thanks for looking at this Sven. Is this documented anywhere? Percentage-sized divs are a fairly common occurrence in HTML and obviously supported by browsers. It also appears to work as I'd expect on other components, and worked this way on the example code above in 1.2.3. If this is not recommended, what is the GXT best practice for filling the maximum space available? We've experimented with auto height/width and found differences in that behavior vs. 100% as well.

sven
29 Apr 2009, 8:43 AM
This works with basic components. But not with rich one. There is no event that gets fired by the browser when the width/height changes. And this can change very often if you use %.

darrellmeyer
30 Apr 2009, 4:31 AM
CardLayout, which is a FitLayout, works by sizing the child component to be the same size as the parent container. Any sizes given to the child component are ignored (except for auto height and width). By setting the size of the child component in pixels, the child component is notified when it is resized. Many components, need to be sized in pixels to render properly, such as Grid and TabPanel, as they must "adjust" to their new size.

It is possible to change this behavior as needed. For example, if you wish to have the child component sized using 100% height and width consider the following code:


public class Test implements EntryPoint {

class TestLayout extends CardLayout {
@Override
protected void setItemSize(Component item, Size size) {
item.setSize("100%", "100%");
}
}

public void onModuleLoad() {
LayoutContainer container = new LayoutContainer();
container.setStyleAttribute("border", "1px solid red");
container.setWidth("100%");
container.setHeight("100%");

TestLayout layout = new TestLayout();
container.setLayout(layout);

LayoutContainer inner = new LayoutContainer();
inner.setStyleAttribute("border", "1px solid blue");
container.add(inner);

layout.setActiveItem(inner);

RootPanel.get().add(container);
}
}In this case, our layout subclass overrides setItemSize and simply sets the size using percentages. This will only be effective when the child component handles percent based sizing.


Note that the width is 100px rather than 100% in the second div. We encountered this on upgrade on a project that had otherwise rendered correctly using GXT 1.2.3.There should not be any difference in behavior between 1.2.3 and 2.0. If you believe that is not the case, please include complete test code, similar to the code I just posted.

dmallory
30 Apr 2009, 9:32 AM
Thank you for the explanation. We've come up with some similar workarounds. However, this did behave very differently in 1.2.3 and as I mentioned is an extremely visible breaking change in 2.0-M1 that is not documented. I have created a simpler, self-contained example using a single layout container, tab panel, and tab item with different background colors to help illustrate.

Module code:


public class Example implements EntryPoint {

public void onModuleLoad() {

// top layout container, red
LayoutContainer layoutContainer = new LayoutContainer();
layoutContainer.setWidth(500);
layoutContainer.setHeight(300);
layoutContainer.setStyleAttribute("backgroundColor", "red");
layoutContainer.setId("red-layout-container");

// tab panel, blue
TabPanel tabPanel = new TabPanel();
tabPanel.setWidth("100%");
tabPanel.setHeight("100%");
tabPanel.setStyleAttribute("backgroundColor", "blue");
tabPanel.setId("blue-tab-panel");

// tab item, green
TabItem tabItem = new TabItem();
tabItem.setWidth("100%");
tabItem.setHeight("100%");
tabItem.setStyleAttribute("backgroundColor", "green");
tabItem.setId("green-tab-item");

tabPanel.add(tabItem);
layoutContainer.add(tabPanel);
RootPanel.get().add(layoutContainer);
}
}

Output generated with GXT 1.2.3, GWT 1.5.3:


<div
style="overflow: hidden; background-color: red; width: 500px; height: 300px;"
id="red-layout-container">
<div id="blue-tab-panel" class="x-tab-panel"
style="background-color: blue; width: 100%; height: 100%;">
<div class="x-tab-panel-header" style="width: 498px;">
<div class="x-tab-strip-wrap">
<ul class="x-tab-strip x-tab-strip-top">
<li id="green-tab-item" class="x-tab-strip-active"><a
onclick="return false;" class="x-tab-strip-close" /><a
onclick="return false;" href="#" class="x-tab-right"><em
class="x-tab-left"><span class="x-tab-strip-inner"><span
class="x-tab-strip-text" /></span></em></a></li>
<li class="x-tab-edge" />
<div class="x-clear" />
</ul>
</div>
<div class="x-tab-strip-spacer" /></div>
<div class="x-tab-panel-body x-tab-panel-body-top"
style="height: 285px; width: 498px;">
<div
style="overflow: hidden; background-color: green; width: 498px; height: 285px;"
id="green-tab-item" /></div>
</div>
</div>

Output generated with GXT 2.0-M1, GWT 1.6.4:


<div id="red-layout-container"
style="background-color: red; width: 500px; height: 300px;">
<div role="tablist" tabindex="0" id="blue-tab-panel" class="x-tab-panel"
style="background-color: blue; width: 100%;"
aria-activedescendant="blue-tab-panel__green-tab-item">
<div class="x-tab-panel-header" style="width: 498px;">
<div role="presentation" class="x-tab-strip-wrap">
<ul class="x-tab-strip x-tab-strip-top">
<li role="tab" id="blue-tab-panel__green-tab-item"
class="x-unselectable x-tab-strip-active" unselectable="on"><a
role="presentation" onclick="return false;" class="x-tab-strip-close" /><a
role="tab" class="x-tab-right"><em class="x-tab-left"><span
class="x-tab-strip-inner"><span class="x-tab-strip-text" /></span></em></a></li>
<li class="x-tab-edge" />
<div class="x-clear" />
</ul>
</div>
<div class="x-tab-strip-spacer" /></div>
<div class="x-tab-panel-body x-tab-panel-body-top"
style="height: 0px; width: 498px;">
<div id="green-tab-item"
style="background-color: green; width: 100%; height: 100%;" /></div>
</div>
</div>

Observe that in 1.2.3 the top div has a height of 100% and tab panel body divs are correctly calculated as 100% height/width of the parent, minus the border and any margins. In 2.0-M1 the top div height is missing and one of the tab panel body divs receives a height of 0px. Below is the visual result.

GXT 1.2.3, GWT 1.5.3:

13356

GXT 2.0-M1, GWT 1.6.4:

13357

As our application used many tab panels and other card layouts to separate content by pages, this resulted in all of those pages which had previously stretched to 100% height becoming compressed and requiring new workarounds. Based on the discussion so far, is the current functionality correct and will it be continued through the 2.0 release? We have begun changing our layouts to accommodate it but I'm concerned that we're implementing something that may break again if it continues to change.

darrellmeyer
1 May 2009, 5:47 AM
I would not expect this code to work as desired.


However, this did behave very differently in 1.2.3 and as I mentioned is an extremely visible breaking change in 2.0-M1 that is not documented.This is not a breaking change. Your code worked in the past because there were extra layout calls in tab panel which was fixed in 1.2.4. So by fixing the library code, we broke your code. But your code is incorrect.

TabPanel needs to be sized either in pixels directly or by a parent layout. Your outer layout container does not use a layout, so a FlowLayout is used which does not size its children.

If you add:


layoutContainer.setLayout(new FitLayout());With this line, the code works as expected in all GXT versions. TabPanel will not render properly when setting true percentage values directly. It must know when its dimensions have changed to update and recalculate dimensions. When using normal HTML percentage values, the component will not know when it's size has changed by the browser and thus will not render property (the wrong height in your code).