PDA

View Full Version : [3.2+] SplitVBoxLayout



Nesta
10 Apr 2010, 10:31 AM
This Extension will only work with ExtJs 3.2 or greater!


Extention of the Ext.layout.VBoxLayout with the possibility to add SplitBars between the boxes.

An example can be found here: deleted by my server admin ..
- The examples uses a simple BorderLayout with a columnfit layout as center.
- The 2 columns use splitvbox layout to make the panel resizeable
- the columnfit layout was written by animal and makes the columns resizeable


A Ext.Panel using SplitVBoxLayout to render it's child items would look like


var parent = new Ext.Panel({
layout: "splitvbox",
renderTo: "parent",
layoutConfig:{
split: true,
margin: 5,
align: "stretch"
},
items:[top, middle, bottom]
});
Ext.ux.layout.SplitVBoxLayout:



/**
*
* Ext.layout.VBoxLayout extension that adds SplitBars between items to make them resizeable
* by the user.
*
* Use:
* layout: "splitvbox",
* layoutConfig:{
* split: true, // show Splitbar, defualts to yes
* margin: 5, // Height of the Splitbar, defaults to 5
* minHeight: 50 // minHeight of Components, defaults to 50
* }
*
**/
Ext.ns('Ext.ux.layout');

Ext.ux.layout.SplitVBoxLayout = Ext.extend(Ext.layout.VBoxLayout, {
split: true,
margin: 5,
minHeight: 50,
monitorResize: true,

// private
onLayout : function(container, target){

// add margin between elements if margin was defined using layoutConfig

var items = container.items.items;
if(this.margin!=0){
for(var i=1; i<items.length;i++){
container.items.items[i].margins = {left:0,top:this.margin, right:0,bottom:0};
}
}
// get e.g. calculate the boxes array from the VBoxLayout. getBoxes doesn't exist..
Ext.ux.layout.SplitVBoxLayout.superclass.onLayout.call(this, container, target);
if(!this.boxes)
this.boxes = Ext.ux.layout.SplitVBoxLayout.superclass.calculateChildBoxes(items,this.container.getHeight()).boxes;

// update the boxes with the size of the contianed item. other values defined in boxes[i] are not changed
for(var i=0; i<items.length;i++){
items[i].flex=items[i].getHeight();
this.boxes[i].dirtySize = false;
this.boxes[i].top = items[i].y;
this.boxes[i].left = items[i].x;
this.boxes[i].width = items[i].getWidth();
this.boxes[i].height = items[i].getHeight();
}

// we are done if split is false
if(!this.split)
return;

// create Splitbars
if (!this.splitBars)
this.splitBars = [];

for(var i=0; i<(items.length-1);i++){
if (this.splitBars[i]) {
// SpliVboxLayout parent element or browser was resized -> move the Splitbars
this.splitBars[i].el.setStyle("top", this.boxes[(i+1)].top-(this.margin/2));
this.splitBars[i].el.setStyle("width",this.boxes[(i+1)].width);
}else{
// create Splitboxes and add beforeapply listener to resize the items
var currentItem = items[(i+1)];
currentItem.minSize = this.minHeight;
currentItem.boxMinHeight = this.minHeight;
this.splitBars[i] = new Ext.SplitBar(this.innerCt.createChild({
cls: 'x-layout-split x-layout-split-west',
style: {
top: (currentItem.y-(this.margin/2))+'px',
left: '0px',
height: this.margin+'px',
width: currentItem.getWidth()+'px'
}
}),currentItem.el, Ext.SplitBar.VERTICAL);
this.splitBars[i].index = i;
this.splitBars[i].addListener('beforeapply', this.onBoxResize, this);
} // else
} // for
},

// helper function that allows to update a box by passing an object
// private
updateOneBox : function(index,newBox){
this.boxes[index].dirtySize = true;
this.boxes[index].width = newBox.width;
this.boxes[index].height = newBox.height;
this.boxes[index].top = newBox.top;
this.boxes[index].left = newBox.left;
},

// calculate the new box and item sizes
// private
onBoxResize : function(sb,height){
if (sb.dragSpecs.startSize) {
var delta = (height-sb.dragSpecs.startSize);

var lowerBox = this.boxes[(sb.index+1)];
var upperBox = this.boxes[(sb.index)]

var lowerBottom = lowerBox.top + lowerBox.height;
var upperH = upperBox.height-delta;
var lowerH = height;

// lowerBox.height can't be < this.minHeight
if(lowerH<this.minHeight){
var expandLower = this.minHeight - lowerH;
upperH = upperH - expandLower;
lowerH = this.minHeight;
}

// upperBox.height can't be < this.minHeight
if(upperH < this.minHeight){
lowerH = (lowerH - (delta - upperBox.height + this.minHeight));
upperH = this.minHeight;
}

// new upper y of the lowerBox is upperBox +height
lowerBox.top = (upperBox.top + upperH + this.margin);

// update the lowerBox (this.boxes)
this.updateOneBox((sb.index+1),{
dirtySize: true,
width: lowerBox.width,
height: lowerH,
top: lowerBox.top,
left: lowerBox.left});

// Panel.setSize() adds the panel header to the item. -> item size will be wrong and
// absolutely **** if the panel has no header (title is not defined)
if(this.boxes[(sb.index+1)].component.header)
this.boxes[(sb.index+1)].component.body.setHeight(lowerH - this.boxes[(sb.index+1)].component.header.getHeight());
// update the upperBox (this.boxes)
this.updateOneBox(sb.index,{
dirtySize: true,
width: upperBox.width,
height: upperH,
top: upperBox.top,
left: upperBox.left});

// update the flex values of the items -> VBoxLayout will keep the size ratio of our boxes on resize
this.container.items.items[(sb.index+1)].flex = lowerH;
this.container.items.items[(sb.index)].flex = upperH;
sb.el.setStyle('top', lowerBox.top-(this.margin/2)); // move the SplitBar
this.updateChildBoxes(this.boxes); // Let VBoxLayout update the boxes and items
}
return false;
}
});
Ext.Container.LAYOUTS['splitvbox'] = Ext.ux.layout.SplitVBoxLayout;
Keep in mind, that the Ext BorderLayout can render 3 Boxes (North, center, south) and enables resizing of them.


Notes:
- Don't use collapsible Panel in this Layout. Like the VBoxLayout the boxes will not be updated correctly

Nesta
10 Apr 2010, 10:32 AM
Placeholder for SplitHBoxLayout

[EDIT] found a resizeable HBox Layout written by ry.extjs
http://www.extjs.com/forum/showthread.php?65982-3.0-Ext.layout.HBoxFitSplit.

Nesta
19 Apr 2010, 12:21 AM
Added a example ....

[EDIT] My example was removed by out serveradmin...

hansellh
20 Apr 2010, 2:35 AM
these URLs do not work

Nesta
20 Apr 2010, 3:17 AM
my severadmin changed the permussions...

i have to find a other place for the example sry

imrukhan81@yahoo.co.in
4 May 2010, 2:57 AM
Hi Nesta, I am trying with ExtJS 3.0, but the following error is coming:
Ext.layout.SplitVBoxLayout.superclass.calculateChildBoxes is not a function
PLease help me out.

Animal
4 May 2010, 3:04 AM
You can't use it with 3.0.

Nesta
4 May 2010, 3:08 AM
hi,

i think thats comes with the refactoring of the BoxLayout in 3.2.
I can not find the code of 3.0... but i'm pretty sure, that the calculateChildBoxes function was in the BoxLayout
and not in the VBoxLayout.

Try to extend the BoxLayout directly but i don't think everythink will work as expected


Ext.ux.layout.SplitVBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
...


Please let me know if that helps

[EDIT]Animal do you have permissions to edit the Treadtitle (3.x is a little bit optimistic :) )

imrukhan81@yahoo.co.in
4 May 2010, 3:37 AM
Thank you very much for the reply. Its is not working with the above clue.

How can we use the above layout with collapsible Panel?

Is there any way to use collapsible Panels in Vertical Box, so that it can adjust after minimizing the panel?
I have panel1,panel2,panel3. in which first two are collapsible. How can I adjust the panel3 height after minimizing/maximizing the panel1/panel2.

Thanks a lot in advance.

Nesta
4 May 2010, 3:51 AM
i got the code of Ext 3.0.0.
The entire code of H and V BoxLayout is in the onLayout listener. so it will be really hard to extend it and enable
collapsing Panels in VBox. You should update (at least 3.2) if u want to extend the BoxLayout.

I played around with the collapsible panels in vbox (3.2).
if you don't have resizeable panels it's pretty easy:
- add expand & collapse listeners to all boxes.
- save the height (boxes[i].heigthBeforeCollapse)
- set to old height on expand.

I had this working, but once i started to resize the boxes (while one or more
are collapsed) i decided, that resizing to minHeight is enough :).

Nesta
11 Jan 2011, 1:20 AM
I encounters problems with 3.3 if you disable the children of a vbox container.
VBoxLayout and my extention are both resizing/ignoring the flex value if you disable children
of a vbox container.
Only solution i found so far is to disable the container (component for which the vbox is defined).
Clearly this only works, if you want to disable all children.

meercat
10 May 2011, 12:53 AM
Thanks for posting this - I've been putting off writing such a layout for some time now, and your's fit the bill nicely.

One small bug fix - when you first create the splitbars you set the styles ("top" etc) and add the "px" suffix to the numeric sizes, but you omit these suffixes whenn you later adjust the sizes - this didn't work for me (under Chrome 11) so I explicitly add the "px" suffixes again when adjust styles as well as some other general tidy ups and tweaks.

And you haven't stated any license or copyright, so I hope you don't mind if I simply give you a named thanks and a link back to this thread in the 'credits' page of my webapp.

Nesta
10 May 2011, 1:19 AM
Never tested the layout in chrome :)
Can you provide some more information about the changes you made or is it just the updateOneBox function?

I have not stated a licence or copyright thats true. But of course its free to use and modify. If you add new features (and want to share them) let my know and i will update this thread.

meercat
10 May 2011, 1:41 AM
Never tested the layout in chrome :)

Chrome is a fantastic dev environment, I switched completely from firefox and firebug and never looked back...


Can you provide some more information about the changes you made or is it just the updateOneBox function?

It's mostly making the code fit the look and feel of the rest of my code (things I pull in from repos I preserve untouched, but things like this I inline) and, for example, using container.items.each() rather than explicit for-loops.

I also reduced it to just the onLayout and onBoxResize methods as the other methods didn't seem to offer much benefit or abstraction (I'm using 3.3.1 - it may be of course that some of these things wouldn't work for 3.2 or earlier).


I have not stated a licence or copyright thats true. But of course its free to use and modify. If you add new features (and want to share them) let my know and i will update this thread.

Thanks: I'll post my version in a separate message below (under the same 'free to use and modify' disclaimer) - I don't think I've added new features as such but if I do so, I'll post further updates.

meercat
10 May 2011, 1:42 AM
duplicate - seems "Preview Post" is actually posting the message...

meercat
10 May 2011, 1:47 AM
Duplicate post [ I think maybe I'm posting while Sencha are in mid-deployment of a new version - duplicate post and look-and-feel changes... very strange ... ]

meercat
10 May 2011, 1:50 AM
Some of the changes below may seem to make the code bigger, but when run thru the minifier I use (UglifyJS) things like making extra variables as aliases for properties will make the minified code shorter.


/**
*
* Ext.layout.VBoxLayout extension that adds SplitBars between items to make them resizeable
* by the user.
* Based on http://www.sencha.com/forum/showthread.php?96756-3.2-SplitVBoxLayout with fixes
*
* Use:
* layout: "splitvbox",
* layoutConfig:{
* split: true, // show Splitbar, defualts to yes
* margin: 5, // Height of the Splitbar, defaults to 5
* minHeight: 50 // minHeight of Components, defaults to 50
* }
*
**/
Ext.ns('Ext.ux.layout');
Ext.ux.layout.SplitVBoxLayout = Ext.extend(Ext.layout.VBoxLayout, {
split: true,
margin: 6, // sized to the height of the image for the splitter bar applied via CSS
minHeight: 75,
monitorResize: true,

// private
onLayout : function(container, target){

// add margin between elements if margin was defined using layoutConfig
var items = container.items.items;
if(this.margin!=0){
container.items.each( function(item, index) {
if (index > 0) {
item.margins = {left:0, top:this.margin, right:0, bottom:0};
}
}, this);
}

Ext.ux.layout.SplitVBoxLayout.superclass.onLayout.call(this, container, target);
this.boxes = this.boxes || this.calculateChildBoxes(items,this.container.getHeight()).boxes;

// update the boxes with the size of the contianed item. other values defined in boxes[i] are not changed
container.items.each(function(item,i) {
item.flex = item.getHeight();
this.boxes[i].top = item.y;
this.boxes[i].left = item.x;
this.boxes[i].width = item.getWidth();
this.boxes[i].height = item.getHeight();
}, this);

if (this.split) {
this.splitBars = this.splitBars || [];

for (var i = 0; i < items.length-1; i++) {
if (this.splitBars[i]) {
// SpliVboxLayout parent element or browser was resized -> move the Splitbars
this.splitBars[i].el.setStyle("top", (this.boxes[(i+1)].top-(this.margin/2))+"px");
this.splitBars[i].el.setStyle("width",(this.boxes[(i+1)].width) +"px");
}
else {
// create Splitboxes and add beforeapply listener to resize the items
var currentItem = items[i+1];
currentItem.minSize = this.minHeight;
currentItem.boxMinHeight = this.minHeight;
this.splitBars[i] = new Ext.SplitBar(this.innerCt.createChild({
cls: 'x-layout-split x-layout-split-west',
style: {
top: (currentItem.y-(this.margin/2))+'px',
left: '0px',
height: this.margin+'px',
width: currentItem.getWidth()+'px'
}
}),currentItem.el, Ext.SplitBar.VERTICAL);
this.splitBars[i].index = i;
this.splitBars[i].addListener('beforeapply', this.onBoxResize, this);
}
}
}
},

// calculate the new box and item sizes
// private
onBoxResize : function(sb,height){
if (sb.dragSpecs.startSize) {
var delta = height - sb.dragSpecs.startSize,
minHeight = this.minHeight,
upper = sb.index,
lower = upper+1,
upperBox = this.boxes[upper],
lowerBox = this.boxes[lower],
upperH = upperBox.height - delta,
lowerH = height;

// lowerBox.height can't be < minHeight
if (lowerH < minHeight) {
upperH = upperH - (minHeight - lowerH);
lowerH = minHeight;
}

// upperBox.height can't be < this.minHeight
if (upperH < minHeight) {
lowerH = lowerH - (minHeight - (upperBox.height - delta));
upperH = minHeight;
}

// update the boxes
upperBox.height = upperH;
lowerBox.top = upperBox.top + upperH + this.margin;
lowerBox.height = lowerH;

// Panel.setSize() adds the panel header to the item. -> item size will be wrong and
// absolutely **** if the panel has no header (title is not defined)
if (lowerBox.component.header)
lowerBox.component.body.setHeight(lowerH - lowerBox.component.header.getHeight());

// update the flex values of the items -> VBoxLayout will keep the size ratio of our boxes on resize
this.container.items.items[lower].flex = lowerH;
this.container.items.items[upper].flex = upperH;
sb.el.setStyle('top', lowerBox.top-(this.margin/2)+"px"); // move the SplitBar
this.updateChildBoxes(this.boxes); // Let VBoxLayout update the boxes and items

// This is the fail-safe to reset all the sizes if things play up :)
//this.onLayout( this.container, this.container.getLayoutTarget() );
}
return false;
}
});
Ext.Container.LAYOUTS['splitvbox'] = Ext.ux.layout.SplitVBoxLayout;

spanosyan
22 Feb 2012, 2:48 PM
Hello,

I realize it's been 10 months since any previous activity so I might be shooting in the dark. I recently found this post trying to do the same thing. It mostly works as I would expect, but there is one small problem.

I have a BorderLayout with two VBoxLayout's inside as the center region. I'm applying the SplitVBoxLayout to one of those two. The problem occurs when I resize the panels to a point past the minimum height. Most of the time it will not allow this to happen and will resize the heights accordingly. However, when the layout is allowed to go passed the minHeight, the bottom border for the lower box disappears. I've tried looking into the code to see why that would be, but have had no luck.

I've attached images of the problem.

Thanks!

sp

Nesta
22 Feb 2012, 11:20 PM
Currently i have very little time because of a rather big project that has to be finished soon. But i will try to check this.
If you only have 2 boxes, you can use a nested BorderLayout to make them resizeable. This might be faster as i am not sure when i will find the time to fix this. sry

spanosyan
23 Feb 2012, 7:25 PM
Hi Nesta,

Thank you for replying to my post. I found that the problem occurred as a result of the code segment below. I can't really explain why, but removing the setHeight call fixed the problem. Could it be that in some cases that the height is overestimated with the following approach?




if (lowerBox.component.header)
lowerBox.component.body.setHeight(lowerH - lowerBox.component.header.getHeight());



Anyway, thought I'd let you know so you wouldn't have to worry about it since you're quite busy.

Thanks,

sp

Nesta
24 Feb 2012, 12:55 AM
the code sets "lowerBox.component.body" height directly. The code on the first page of this thread updates the lowerBox using the "this.boxes[(sb.index+)]" this can be the problem.
Or it is the toolbar inside the panel.

I remember that this i have this code to make the layout work with boxes without title/header. if it works for you, it should be save to remove this (as long as you have headers)