PDA

View Full Version : [FIXED][3.0svn5735] Form in card panel not rendering correctlly until resized



BlueCamel
6 Dec 2009, 5:55 PM
This is against ext-trunk SVN 5735. The browser is FF 3.5.5 on Linux. I still need to test with other browsers.

I have an app that uses a table layout on one page of a card layout. In side the table layout there is a form. Prior to 5735 this rendered okay. After 5735 the buttons on the form panel are not visible until after resize.

Simple test case based on my app:


Ext.onReady(function(){
var page = new Ext.Viewport({
layout: 'card',
id: 'card-panel',
activeItem: 0,
items: [{
xtype: 'panel',
id: 'card-0',
layout: 'table',
layoutConfig: { columns: 1 },
items: [{
xtype: 'box',
html: '<p>Example Form</p><br />'
},{
xtype: 'form',
border: false,
frame: false,
items: [{
xtype: 'textfield',
name: 'field1',
fieldLabel: 'Field One'
}],
buttons: [{
text: 'submit'
},{
text: 'reset'
}]
}]
}]
});
});

BlueCamel
6 Dec 2009, 6:33 PM
Also failing on Safari with OS X SL. I think I saw some fairly basic changes going into SVN recently for the hbox/vbox fixes. Could they have something to do with this?

evant
6 Dec 2009, 9:11 PM
Confirmed, looking into it, thanks.

Animal
7 Dec 2009, 1:02 AM
I see it.

CardLayout is laying out hidden items (They are all hidden until one is set as the activeitem)

It has deferred render: false, so is using forceLayout in its layout.

But that is not being propagated down. The fix:



Ext.override(Ext.layout.ContainerLayout, {
onResize: function(){
var ct = this.container,
b = ct.bufferResize;

if (ct.collapsed){
return;
}

// Not having an ownerCt negates the buffering: floating and top level
// Containers (Viewport, Window, ToolTip, Menu) need to lay out ASAP.
if (b && ct.ownerCt) {
// If we do NOT already have a layout pending from an ancestor, schedule one.
// If there is a layout pending, we do nothing here.
// buffering to be deprecated soon
if (!ct.hasLayoutPending()){
if(!this.resizeTask){
this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
this.resizeBuffer = Ext.isNumber(b) ? b : 50;
}
ct.layoutPending = true;
this.resizeTask.delay(this.resizeBuffer);
}
}else{
ct.doLayout(false, this.forceLayout);
}
}
});

BlueCamel
7 Dec 2009, 6:12 AM
This works for me on the test case. However, I can't seem to make it work for the application. I'm still looking as to see why.

BlueCamel
7 Dec 2009, 6:31 AM
Here is a new test case that still fails with the proposed over-ride. I run this and then do a Ext.getCmp('form-panel').layout.setActiveItem(1) in firebug to switch panels.

EDIT: That is to say, the same problem as above happens in FF 3.5.5 on Linux. After switching from the default card-0 to card-1, form panel has the buttons chopped off because of what looks like a height issue. To resolve the problem I have resize the window after switching to card-1. If I change the card-panel activeItem config to start out with card-1 (where the form is) then it draws correctly.

This was tested with the proposed override in place.



var form = {
xtype: 'form',
border: false,
frame: false,
items: [{
xtype: 'textfield',
name: 'field1',
fieldLabel: 'Field One'
}],
buttons: [{
text: 'submit'
},{
text: 'reset'
}]
}

var page = new Ext.Viewport({
layout: 'border',
items: [{
xtype: 'box',
region: 'north',
html: 'north panel'
},{
xtype: 'panel',
region: 'center',
layout: 'card',
id: 'card-panel',
activeItem: 0,
items: [{
xtype: 'panel',
id: 'card-0',
html: 'Hello from Card 0'
},{
xtype: 'panel',
id: 'card-1',
layout: 'table',
layoutConfig: { columns: 1 },
items: [{
xtype: 'box',
html: 'Hello from Card 1<br />',
},form]
},{
xtype: 'panel',
id: 'card-2',
html: 'Hello from Card 2'
}]
}]
});

BlueCamel
7 Dec 2009, 6:55 AM
Poking at this more .... if I remove the override and retest:

1) if the activeItem is the form then it renders fine
2) if the activeItem is card-0 and I switch to the form on card-1 it does not render.

That's interesting because I get the same results on this second test case with the override in place. However, this override defiantly fixes the previous test case. My conclusion is that this second test case is the same (or similar) issue but at a different point in the code.

In short, the override works for the previous test case and should be included in 3.1. However, there is a second test case that it doesn't fix yet.

Animal
7 Dec 2009, 7:03 AM
Of course it works if you use hideMode: 'offsets' for card-1

Which you should do anyway.

CardLayout is asking a display:none Container to lay itself out. The new implementation of canLayout stops it trying because there is no space in which to lay itself out.

I'll keep looking at this though.

Animal
7 Dec 2009, 7:17 AM
Try this override:



Ext.override(Ext.Container, {
doLayout: function(shallow, force){
var rendered = this.rendered,
forceLayout = force || this.forceLayout,
cs, i, len, c;

this.layoutDone = true;
if(!this.canLayout() || this.collapsed){
this.deferLayout = this.deferLayout || !shallow;
if(!forceLayout){
return;
}
shallow = shallow && !this.deferLayout;
} else {
delete this.deferLayout;
}

cs = (shallow !== true && this.items) ? this.items.items : [];

// Inhibit child Containers from relaying on resize since we are about to to explicitly call doLayout on them all!
for(i = 0, len = cs.length; i < len; i++){
if ((c = cs[i]).layout) {
c.suspendLayoutResize = true;
}
}

// Tell the layout manager to ensure all child items are rendered, and sized according to their rules.
// Will not cause the child items to relayout.
if(rendered && this.layout){

// If we are forcing layouts regardless, propagate this setting to child layout managers
var f = this.layout.forceLayout;
this.layout.forceLayout = forceLayout;
this.layout.layout();
this.layout.forceLayout = f;

// If we have instructed the child layout to force a layout, that will have been propagated because
// ContainerLayout.configureItem calls doLayout if its forceLayout flag is set, so we are done.
if (forceLayout) return;
}

// Explicitly lay out all child items
for(i = 0; i < len; i++){
if((c = cs[i]).doLayout){
c.doLayout(false, forceLayout);
}
}
if(rendered){
this.onLayout(shallow, forceLayout);
}
// Initial layout completed
this.hasLayout = true;
delete this.forceLayout;

// Re-enable child layouts relaying on resize.
for(i = 0; i < len; i++){
if ((c = cs[i]).layout) {
delete c.suspendLayoutResize;
}
}
}
});

Animal
7 Dec 2009, 7:25 AM
That's too complex! There's a better way...

Animal
7 Dec 2009, 7:28 AM
The total override needed is



Ext.override(Ext.layout.ContainerLayout, {
onResize: function(){
var ct = this.container,
b = ct.bufferResize;

if (ct.collapsed){
return;
}

// Not having an ownerCt negates the buffering: floating and top level
// Containers (Viewport, Window, ToolTip, Menu) need to lay out ASAP.
if (b && ct.ownerCt) {
// If we do NOT already have a layout pending from an ancestor, schedule one.
// If there is a layout pending, we do nothing here.
// buffering to be deprecated soon
if (!ct.hasLayoutPending()){
if(!this.resizeTask){
this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
this.resizeBuffer = Ext.isNumber(b) ? b : 50;
}
ct.layoutPending = true;
this.resizeTask.delay(this.resizeBuffer);
}
}else{
ct.doLayout(false, this.forceLayout);
}
},

configureItem: function(c, position){
if(this.extraCls){
var t = c.getPositionEl ? c.getPositionEl() : c;
t.addClass(this.extraCls);
}
// If we are forcing a layout, do so *before* we hide so that laying out is possible
if(c.doLayout && this.forceLayout){
c.doLayout(false, true);
}
if (this.renderHidden && c != this.activeItem) {
c.hide();
}
},
});


The onResize is as before. The configureItem has swapped the doLayout call to before it hides the item!

BlueCamel
7 Dec 2009, 7:41 AM
Of course it works if you use hideMode: 'offsets' for card-1

Which you should do anyway.

CardLayout is asking a display:none Container to lay itself out. The new implementation of canLayout stops it trying because there is no space in which to lay itself out.

I'll keep looking at this though.

I've don't fully grock under what layout conditions hideMode should be changed from the default. I've always kind of looked at it as the default should work and it likely shouldn't be messed with in most cases.

From your comments, I'm infering that the default way of hiding a container is display:none and that by changing hideMode that's controllable. I assume 'offsets' means to do layout in some region that isn't visible on screen. That works in this case as a work around because the new implentation of canLayout will schedule layout for anything not set as display:none.

Sound right?

Animal
7 Dec 2009, 7:44 AM
http://www.extjs.com/deploy/dev/docs/?class=Ext.Component&member=hideMode

If an element is styled display:'none' it has NO DIMENSIONS.

I don't know who said 'display' is preferred. I go for 'offsets' every time!

But that latest override should fix most things. It's been like that for ages, and must have caused LOADS of problems.

Once I saw it, it was obviously wrong. If forcing a layout to occur on render, do it before hiding it so that if it's hideMode: 'display', you won't get problems.

Animal
7 Dec 2009, 7:45 AM
But does the code in #11 work for you? It works for me with your example.

Condor
7 Dec 2009, 7:49 AM
I don't know who said 'display' is preferred. I go for 'offsets' every time!

The reason display is preferred over offsets is that elements hidden with display:none don't need to be drawn by the browser engine, so the result is a faster display.

BlueCamel
7 Dec 2009, 7:49 AM
But does the code in #11 work for you? It works for me with your example.

Yes, I can confirm that the code in #11 works for both test cases and my actual application. Thank you for this!

Jamie Avins
7 Dec 2009, 8:05 AM
Incorporated into svn 5740.

Animal
7 Dec 2009, 8:21 AM
The reason display is preferred over offsets is that elements hidden with display:none don't need to be drawn by the browser engine, so the result is a faster display.


It's only an issue if you are choosing to not defer layout of hidden items.

And if you are forcing layout, then you should use 'offsets'

BlueCamel
7 Dec 2009, 9:14 AM
It's only an issue if you are choosing to not defer layout of hidden items.

And if you are forcing layout, then you should use 'offsets'

How exactly does the developer know (based on docs?) that they are forcing a layout?

Based on this discussion, I'm guessing that forced layout happens any time when using tab or card layouts? That is, the items on the non-visible tab/card panels are forced layout while the visible (activeItem) is a regular layout.

Is this right? Also, I can take this to the regular forums if it would be better than clocking up the BUGs thread.

Animal
7 Dec 2009, 9:20 AM
CardLayout is documented as having deferredRender defaulting to false. Which means it attempts to render all child items, including hidden ones. It forces them to lay out when it initially renders itself.

But hopefully with that switch round of the forced doLayout to be before the hide, this whole issue will just go away!

BlueCamel
7 Dec 2009, 9:23 AM
CardLayout is documented as having deferredRender defaulting to false. Which means it attempts to render all child items, including hidden ones. It forces them to lay out when it initially renders itself.

But hopefully with that switch round of the forced doLayout to be before the hide, this whole issue will just go away!

Ack! Makes sense now.