PDA

View Full Version : [OPEN] [CLOSED-309][3.??] HBoxLayout and VBoxLayout don't allow overflow



rspaeth
20 Aug 2009, 9:15 AM
Below is an example where an HBoxLayout doesn't handle overflow:

current problem: when there is overflow on a hbox (or vbox), no scrollbar appears making it not possible to see data that has fallen off the page view.

expected outcome: when there is overflow on a hbox (or vbox), an appropriate scrollbar appears

the example: I have 3 grids inside a panel (classer) which has a 'hbox' layout. I do NOT want the grids to scroll individually (hence why i have scrollOffset: 0 and forceFit:true), but i do want the PARENT panel to scroll. The grids need to have an autoHeight: true so i can fill the page with the grid if needed. With the example below, the panel scrolls vertically with a smaller resolution, but does not scroll horizontally, even though the parent has autoscroll: true. Scrolling should be based on the parent panel overflow.

note: the horizontal scrollbar appears for a split second then disappears on page downsize.




Ext.onReady(function()
{

var reader = new Ext.data.ArrayReader({}, [
{name: 'index'},
{name: 'level'},
{name: 'numraw'},
{name: 'num1'},
{name: 'num0'},
{name: 'w1'},
{name: 'w0'},
{name: 'w'},
{name: 'p1'},
{name: 'woe'},
{name: 'iv'},
{name: 'woeg'},
{name: 'num'},
{name: 'w2'},
{name: 'nmean'},
{name: 'rsqr'},
{name: 'nmeang'}
]);

var def = {
sortable: true
,width: 50
};

var viewConf = {
forceFit:true,
scrollOffset: 0
,autoFill: true
};

var store = new Ext.data.Store(
{
reader: reader,
data: Ext.grid.dummyData
});

var grid1 = new Ext.grid.GridPanel({
id: 'grid1',
ds: store,
cm: new Ext.grid.ColumnModel(
{
defaults: def
,columns:
[
{id:'index', width: 30, sortable: true, dataIndex: 'index'},
{header: "level", width: 80, sortable: true, dataIndex: 'level'},
{header: "Raw", sortable: true, dataIndex: 'numraw'}
]
}),
selModel: new Ext.grid.RowSelectionModel(),
stripeRows: true,
viewConfig: viewConf,
autoHeight: true,
border: true,
bodyBorder: true,
title: 'left side grid',
minColumnWidth: 50
});

var grid2 = new Ext.grid.GridPanel({
id: 'grid2',
ds: store,
cm: new Ext.grid.ColumnModel(
{
defaults: def
,columns:
[
{header: "1", sortable: true, dataIndex: 'num1'},
{header: "0", sortable: true, dataIndex: 'num0'},
{header: "W1", sortable: true, dataIndex: 'w1'},
{header: "W0", sortable: true, dataIndex: 'w0'},
{header: "w", sortable: true, dataIndex: 'w'},
{header: "p1", sortable: true, dataIndex: 'p1'},
{header: "woe", sortable: true, dataIndex: 'woe'},
{header: "iv", sortable: true, dataIndex: 'iv'},
{header: "woeg", width: 100, sortable: true, dataIndex: 'woeg'}
]
}),
stripeRows: true,
viewConfig: viewConf,
autoHeight: true,
disableSelection: true,
border: true,
bodyBorder: true,
title:'middle grid',
minColumnWidth: 50
});

var grid3 = new Ext.grid.GridPanel(
{
id: 'grid3',
ds: store,
cm: new Ext.grid.ColumnModel(
{
defaults: def
,columns:
[
{header: "num", sortable: true, dataIndex: 'num'},
{header: "w2", sortable: true, dataIndex: 'w2'},
{header: "nmean", sortable: true, dataIndex: 'nmean'},
{header: "rsqr", sortable: true, dataIndex: 'rsqr'},
{header: "nmeang", width: 100, sortable: true, dataIndex: 'nmeang'}
]
}),
stripeRows: true,
viewConfig: viewConf,
autoHeight: true,
disableSelection: true,
border: true,
bodyBorder: true,
title:'right side grid',
minColumnWidth: 50
});

var port = new Ext.Viewport(
{
layout: 'border'
,defaults: {autoScroll: true}
,items: [
{
xtype: 'tabpanel'
,region: 'center'
,id: 'tabPanel'
,autoScroll: false
,frame: false
,border: false
,activeTab:0
,items:[
{
xtype: 'panel',
id: 'container',
title: 'the app',
autoScroll:false,
layout: 'border',
frame: false,
items:[
{
xtype: 'panel',
region:'west',
border: true,
width: 200
},
{
xtype: 'panel',
id: 'classer',
region:'center',
title: 'Classer',
autoScroll:true,
layout: 'hbox',
layoutConfig: {
pack: 'start'
},
items:[
grid1
,grid2,
grid3
]
}
]
}]

}]
});



});


Ext.grid.dummyData = [
[0, '-Inf -< 28', 11, 102, 284, '2.0%', '5.2%', '2.1%', 0.946, -0.936, 0.030, '', 386, '2.1%', -0.229, 0.001],
[1, '28 -< 32', 4, 282, 457, '5.6%', '8.4%', '5.7%', 0.968, -0.395, 0.011, '', 739, '5.7%', -0.080, 0.000],
[2, '32 -< 33', 1, 128, 128, '2.6%', '2.3%', '2.6%', 0.980, 0.088, 0.000, '', 256, '2.6%', -0.047, 0.000],
[3, '33 -< 41', 8, 1050, 1410, '21.0%', '25.8%', 0.027, 0.973, -0.207, 0.010, '', 2460, '21.1%', 0.083, 0.001],
[4, '41 -< 53', 12, 1742, 1809, '34.8%', '33.1%', 0.021, 0.979, 0.050, 0.001, '', 3551, '34.8%', 0.057, 0.001],
[5, '53 -< 58', 5, 548, 502, '11.0%', '9.2%', '10.9%', 0.981, 0.176, 0.003, '', 1050, '10.9%', 0.045, 0.000],
[6, '58 -< 70', 12, 814, 551, '16.3%', '10.1%', '16.1%', 0.986, 0.478, 0.030, '', 1365, '16.1%', -0.084, 0.001],
[7, '70 -< 74', 4, 124, 114, '2.5%', '2.1%', '2.5%', 0.981, 0.172, 0.001, '', 238, '2.5%', -0.137, 0.000],
[8, '74 -< 80', 6, 154, 110, '3.1%', '2.0%', '3.1%', 0.985, 0.424, 0.005, '', 264, '3.1%', -0.316, 0.003],
[9, '80 -< Inf', 15, 56, 94, '1.1%', '1.7%', '1.1%', 0.966, -0.430, 0.003, '', 150, '1.1%', -0.463, 0.002],
[10, 'Other', 0, 0, 0, '0.0%', '0.0%', '0.0%', 0.000, 0.000, 0.000, '', 0, '0.0%', 0.000, 0.000]
];

Animal
20 Aug 2009, 9:39 PM
That's not a vbox layout then. vbox fits children into available height.

(and hbox into available width!)

Condor
20 Aug 2009, 11:00 PM
Both HBoxLayout and VBoxLayout have this problem:

Ext.onReady(function(){
new Ext.Panel({
title: 'HBoxLayout',
width: 200,
height: 200,
layout: {
type: 'hbox',
align: 'stretch',
scrollOffset: 19
},
autoScroll: true,
items: [{
title: 'Item 1',
width: 125
},{
title: 'Item 2',
width: 125
}],
renderTo: Ext.getBody()
});
new Ext.Panel({
title: 'VBoxLayout',
width: 200,
height: 200,
layout: {
type: 'vbox',
align: 'stretch',
scrollOffset: 19
},
autoScroll: true,
items: [{
title: 'Item 1',
height: 125
},{
title: 'Item 2',
height: 125
}],
renderTo: Ext.getBody()
});
});

@Animal: Why did you move this thread? Don't you think it's a bug? If you think it isn't, it should at least be a feature request.

Animal
20 Aug 2009, 11:23 PM
An hbox layout fits all child items across its available width, allocating space according to flex hints.

There should never he a horizontal scrollbar. The whole point is that it relies on being either configured or layed out to a fixed width which it then shares out among child items. Otherwise there's no way of performing any any allocation.

And the same goes for vbox in the other dimension.

Doesn't the OP require a table layout?

Condor
20 Aug 2009, 11:32 PM
But what should happen if you don't specify any flex config option, but only fixed widths/heights?
IMHO a scrollbar should be visible if the container is autoScroll:true and the total width/height is larger than the container.

ps. I already advised the OP to use a table layout.

Animal
21 Aug 2009, 1:43 AM
Then it's not a vbox which is a fitting layout but a table which is a stretching layout. It's just choosing the wrong layout.

rspaeth
21 Aug 2009, 9:00 AM
Why are hbox and vbox fitting layouts? It seems to me that this should be documented in the HBoxLayout api. There is nothing indicating that it could not stretch. All it says is "A layout that arranges items horizontally".

Although you can use the table layout as a work around, it still seems like a bug that hbox and vbox do not have a scrollbar when autoScroll:true. I am technically not creating a table. I just want things to layout horizontally as the layout name implies. There is nothing saying that my box has to be within the size of the browser. Other frameworks offer turning on and off scroll policies or adding a box to a scroll view. I think it would only strengthen extjs if this were allowed for hbox and vbox layouts.

Condor
21 Aug 2009, 9:38 AM
I agree. A Hbox/VBoxLayout doesn't need to be a fitting layout if no flex config options are specified.

I recommend this override:

Ext.override(Ext.layout.VBoxLayout, {
onLayout : function(ct, target){
Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target);
var cs = this.getItems(ct), cm, ch, margin,
size = this.getTargetSize(target),
w = size.width - target.getPadding('lr'),
h = size.height - target.getPadding('tb') - this.scrollOffset,
l = this.padding.left, t = this.padding.top,
isStart = this.pack == 'start',
isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
stretchWidth = w - (this.padding.left + this.padding.right),
extraHeight = 0,
maxWidth = 0,
totalFlex = 0,
flexHeight = 0,
usedHeight = 0;
Ext.each(cs, function(c){
cm = c.margins;
totalFlex += c.flex || 0;
ch = c.getHeight();
margin = cm.top + cm.bottom;
extraHeight += ch + margin;
flexHeight += margin + (c.flex ? 0 : ch);
maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right);
});
extraHeight = h - extraHeight - this.padding.top - this.padding.bottom;
var th = flexHeight + this.padding.top + this.padding.bottom;
if(h < th){
h = th;
w -= this.scrollOffset;
stretchWidth -= this.scrollOffset;
}
var innerCtWidth = maxWidth + this.padding.left + this.padding.right;
switch(this.align){
case 'stretch':
this.innerCt.setSize(w, h);
break;
case 'stretchmax':
case 'left':
this.innerCt.setSize(innerCtWidth, h);
break;
case 'center':
this.innerCt.setSize(w = Math.max(w, innerCtWidth), h);
break;
}
var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight),
leftOver = availHeight,
heights = [],
restore = [],
idx = 0,
availableWidth = Math.max(0, w - this.padding.left - this.padding.right);
Ext.each(cs, function(c){
if(isStart && c.flex){
ch = Math.floor(availHeight * (c.flex / totalFlex));
leftOver -= ch;
heights.push(ch);
}
});
if(this.pack == 'center'){
t += extraHeight ? extraHeight / 2 : 0;
}else if(this.pack == 'end'){
t += extraHeight;
}
Ext.each(cs, function(c){
cm = c.margins;
t += cm.top;
c.setPosition(l + cm.left, t);
if(isStart && c.flex){
ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0));
if(isRestore){
restore.push(c.getWidth());
}
c.setSize(availableWidth, ch);
}else{
ch = c.getHeight();
}
t += ch + cm.bottom;
});
idx = 0;
Ext.each(cs, function(c){
cm = c.margins;
if(this.align == 'stretch'){
c.setWidth((stretchWidth - (cm.left + cm.right)).constrain(
c.minWidth || 0, c.maxWidth || 1000000));
}else if(this.align == 'stretchmax'){
c.setWidth((maxWidth - (cm.left + cm.right)).constrain(
c.minWidth || 0, c.maxWidth || 1000000));
}else{
if(this.align == 'center'){
var diff = availableWidth - (c.getWidth() + cm.left + cm.right);
if(diff > 0){
c.setPosition(l + cm.left + (diff/2), c.y);
}
}
if(isStart && c.flex){
c.setWidth(restore[idx++]);
}
}
}, this);
}
});
Ext.override(Ext.layout.HBoxLayout, {
onLayout : function(ct, target){
Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);
var cs = this.getItems(ct), cm, cw, margin,
size = this.getTargetSize(target),
w = size.width - target.getPadding('lr') - this.scrollOffset,
h = size.height - target.getPadding('tb'),
l = this.padding.left, t = this.padding.top,
isStart = this.pack == 'start',
isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
stretchHeight = h - (this.padding.top + this.padding.bottom),
extraWidth = 0,
maxHeight = 0,
totalFlex = 0,
flexWidth = 0,
usedWidth = 0;
Ext.each(cs, function(c){
cm = c.margins;
totalFlex += c.flex || 0;
cw = c.getWidth();
margin = cm.left + cm.right;
extraWidth += cw + margin;
flexWidth += margin + (c.flex ? 0 : cw);
maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);
});
extraWidth = w - extraWidth - this.padding.left - this.padding.right;
var tw = flexWidth + this.padding.left + this.padding.right;
if(w < tw){
w = tw;
h -= this.scrollOffset;
stretchHeight -= this.scrollOffset;
}
var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;
switch(this.align){
case 'stretch':
this.innerCt.setSize(w, h);
break;
case 'stretchmax':
case 'top':
this.innerCt.setSize(w, innerCtHeight);
break;
case 'middle':
this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));
break;
}
var availWidth = Math.max(0, w - this.padding.left - this.padding.right - flexWidth),
leftOver = availWidth,
widths = [],
restore = [],
idx = 0,
availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);
Ext.each(cs, function(c){
if(isStart && c.flex){
cw = Math.floor(availWidth * (c.flex / totalFlex));
leftOver -= cw;
widths.push(cw);
}
});
if(this.pack == 'center'){
l += extraWidth ? extraWidth / 2 : 0;
}else if(this.pack == 'end'){
l += extraWidth;
}
Ext.each(cs, function(c){
cm = c.margins;
l += cm.left;
c.setPosition(l, t + cm.top);
if(isStart && c.flex){
cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));
if(isRestore){
restore.push(c.getHeight());
}
c.setSize(cw, availableHeight);
}else{
cw = c.getWidth();
}
l += cw + cm.right;
});
idx = 0;
Ext.each(cs, function(c){
var cm = c.margins;
if(this.align == 'stretch'){
c.setHeight((stretchHeight - (cm.top + cm.bottom)).constrain(
c.minHeight || 0, c.maxHeight || 1000000));
}else if(this.align == 'stretchmax'){
c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(
c.minHeight || 0, c.maxHeight || 1000000));
}else{
if(this.align == 'middle'){
var diff = availableHeight - (c.getHeight() + cm.top + cm.bottom);
if(diff > 0){
c.setPosition(c.x, t + cm.top + (diff/2));
}
}
if(isStart && c.flex){
c.setHeight(restore[idx++]);
}
}
}, this);
}
});

rspaeth
25 Aug 2009, 9:51 AM
Nice override Condor. Thanks!

evant
10 Sep 2009, 7:19 PM
This issue appears to be already fixed in SVN.

Can anyone confirm?

Condor
10 Sep 2009, 11:45 PM
No, it's not fixed.

Try the example from post #3 (http://www.extjs.com/forum/showthread.php?p=376883#post376883): IMHO it should show scrollbars.

ps. I adjusted the override for the changes in SVN.

return1.at
14 Dec 2009, 2:37 AM
thanks, this solved my problem.

rspaeth
15 Jan 2010, 2:49 PM
Condor, do you have a new override for extjs 3.1? 3.1 doesn't have the scrollbars for me like your override for 3.0. I tweaked the override you gave to work with the new functions in 3.1, but the overall onlayout is pretty different in 3.1, so i thought i would see if you had a new solution.

Jamie Avins
15 Jan 2010, 3:29 PM
If you aren't using VBox to manage your vertical box (you are forcing it to a certain height), what are you using it for?

rspaeth
15 Jan 2010, 3:49 PM
I am not sure how to answer your question (because i feel like the vbox/hbox do need to manage the contents within it). Condor has a great simple example on post 3 where item 2 in both cases are cut off.

rspaeth
15 Jan 2010, 4:14 PM
As for the referral to table layout, Table layouts are not an option since you cannot remove or insert items (such as panels). What is a good layout where some number of panels can be added or removed (based on user interaction) in a vertical layout (used as a wrapper)? In some cases there can be more panels in the wrapper that can display in a page. In this case scroll bars would need to be available to get to the lower panels.

The same can be said for a horizontal layout.

Jamie Avins
15 Jan 2010, 4:22 PM
I strongly agree that the documentation on these layouts needs to be updated.

The H&V box layouts were designed to handle certain layout needs. They cannot handle everything, making them do so would kill performance as there would need to be multiple layout passes to account for all the possibilities. As it stands putting box and border layouts in a scrolling container is strongly discouraged as it forces a second layout pass to occur. Column and Row layouts were never being rendered properly in IE if they were put into scrolling containers as well, but this is resolved in 3.1.1 (again at the cost of 2 layout passes).

It seems like what many people want is the align possibilities that the Box layouts provide (stretch, etc). and that is certainly something we can provide in a more generic way than twisting the Box layout into something it was not intended to be.

Animal
16 Jan 2010, 5:05 AM
I would suggest that my FloatLayout class can solve some of these layout requirements in 3.1.1

It's basically like HBoxLayout in that it lays out horizontally at absolute x/y positions.

But it does not force a fit. It wraps onto the next line if an element won't fit. It can then align items on the line it has just finished - align top/bottom/middle. It can stretch the cells on the line to %ages of the available width. It can also horizontally space the cells at the left, in the center or to the right.

It does 2 passes if it detects a vertical overflow. As soon as it calculates a component position which it sees will overflow, then it accounts for the scrollbar, recalculates available width, and starts again.

http://www.extjs.com/forum/showthread.php?p=396565#post396565

rspaeth
18 Jan 2010, 8:33 PM
Thanks Animal and Jamie. I look forward to experimenting with the 3.1.1 solutions.

Since the vbox layout was not working out I went ahead and developed an alternative solution for our application (but this solution is app specific). I ended up using an absolute layout as a wrapper to hold the various panels that I need which manages the y position when panels are added and removed. The important thing is that the scrollbars are working as expected.