Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / grid / ColumnLayout.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.grid.ColumnLayout
17  * @extends Ext.layout.container.HBox
18  * @private
19  *
20  * <p>This class is used only by the grid's HeaderContainer docked child.</p>
21  *
22  * <p>It adds the ability to shrink the vertical size of the inner container element back if a grouped
23  * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.</p>
24  *
25  * <p>Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls
26  * <code>setPadding</code> on the columns so that they lay out correctly.</p>
27  */
28 Ext.define('Ext.grid.ColumnLayout', {
29     extend: 'Ext.layout.container.HBox',
30     alias: 'layout.gridcolumn',
31     type : 'column',
32
33     reserveOffset: false,
34
35     shrinkToFit: false,
36
37     // Height-stretched innerCt must be able to revert back to unstretched height
38     clearInnerCtOnLayout: true,
39
40     beforeLayout: function() {
41         var me = this,
42             i = 0,
43             items = me.getLayoutItems(),
44             len = items.length,
45             item, returnValue,
46             s;
47
48         // Scrollbar offset defined by width of any vertical scroller in the owning grid
49         if (!Ext.isDefined(me.availableSpaceOffset)) {
50             s = me.owner.up('tablepanel').verticalScroller;
51             me.availableSpaceOffset = s ? s.width-1 : 0;
52         }
53
54         returnValue = me.callParent(arguments);
55
56         // Size to a sane minimum height before possibly being stretched to accommodate grouped headers
57         me.innerCt.setHeight(23);
58
59         // Unstretch child items before the layout which stretches them.
60         for (; i < len; i++) {
61             item = items[i];
62             item.el.setStyle({
63                 height: 'auto'
64             });
65             item.titleContainer.setStyle({
66                 height: 'auto',
67                 paddingTop: '0'
68             });
69             if (item.componentLayout && item.componentLayout.lastComponentSize) {
70                 item.componentLayout.lastComponentSize.height = item.el.dom.offsetHeight;
71             }
72         }
73         return returnValue;
74     },
75
76     // Override to enforce the forceFit config.
77     calculateChildBoxes: function(visibleItems, targetSize) {
78         var me = this,
79             calculations = me.callParent(arguments),
80             boxes = calculations.boxes,
81             metaData = calculations.meta,
82             len = boxes.length, i = 0, box, item;
83
84         if (targetSize.width && !me.isHeader) {
85             // If configured forceFit then all columns will be flexed
86             if (me.owner.forceFit) {
87
88                 for (; i < len; i++) {
89                     box = boxes[i];
90                     item = box.component;
91
92                     // Set a sane minWidth for the Box layout to be able to squeeze flexed Headers down to.
93                     item.minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
94
95                     // For forceFit, just use allocated width as the flex value, and the proportions
96                     // will end up the same whatever HeaderContainer width they are being forced into.
97                     item.flex = box.width;
98                 }
99
100                 // Recalculate based upon all columns now being flexed instead of sized.
101                 calculations = me.callParent(arguments);
102             }
103             else if (metaData.tooNarrow) {
104                 targetSize.width = metaData.desiredSize;
105             }
106         }
107
108         return calculations;
109     },
110
111     afterLayout: function() {
112         var me = this,
113             owner = me.owner,
114             topGrid,
115             bothHeaderCts,
116             otherHeaderCt,
117             thisHeight,
118             otherHeight,
119             modifiedGrid,
120             i = 0,
121             items,
122             len,
123             headerHeight;
124
125         me.callParent(arguments);
126
127         // Set up padding in items
128         if (!me.owner.hideHeaders) {
129
130             // If this is one HeaderContainer of a pair in a side-by-side locking view, then find the height
131             // of the highest one, and sync the other one to that height.
132             if (owner.lockableInjected) {
133                 topGrid = owner.up('tablepanel').up('tablepanel');
134                 bothHeaderCts = topGrid.query('headercontainer:not([isHeader])');
135                 otherHeaderCt = (bothHeaderCts[0] === owner) ? bothHeaderCts[1] : bothHeaderCts[0];
136
137                 // Both sides must be rendered for this syncing operation to work.
138                 if (!otherHeaderCt.rendered) {
139                     return;
140                 }
141
142                 // Get the height of the highest of both HeaderContainers
143                 otherHeight = otherHeaderCt.layout.getRenderTarget().getViewSize().height;
144                 if (!otherHeight) {
145                     return;
146                 }
147                 thisHeight = this.getRenderTarget().getViewSize().height;
148                 if (!thisHeight) {
149                     return;
150                 }
151
152                 // Prevent recursion back into here when the "other" grid, after adjusting to the new hight of its headerCt, attempts to inform its ownerCt
153                 // Block the upward notification by flagging the top grid's component layout as busy.
154                 topGrid.componentLayout.layoutBusy = true;
155
156                 // Assume that the correct header height is the height of this HeaderContainer
157                 headerHeight = thisHeight;
158
159                 // Synch the height of the smaller HeaderContainer to the height of the highest one.
160                 if (thisHeight > otherHeight) {
161                     otherHeaderCt.layout.align = 'stretch';
162                     otherHeaderCt.setCalculatedSize(otherHeaderCt.getWidth(), owner.getHeight(), otherHeaderCt.ownerCt);
163                     delete otherHeaderCt.layout.align;
164                     modifiedGrid = otherHeaderCt.up('tablepanel');
165                 } else if (otherHeight > thisHeight) {
166                     headerHeight = otherHeight;
167                     this.align = 'stretch';
168                     owner.setCalculatedSize(owner.getWidth(), otherHeaderCt.getHeight(), owner.ownerCt);
169                     delete this.align;
170                     modifiedGrid = owner.up('tablepanel');
171                 }
172                 topGrid.componentLayout.layoutBusy = false;
173
174                 // Gather all Header items across both Grids.
175                 items = bothHeaderCts[0].layout.getLayoutItems().concat(bothHeaderCts[1].layout.getLayoutItems());
176             } else {
177                 headerHeight = this.getRenderTarget().getViewSize().height;
178                 items = me.getLayoutItems();
179             }
180
181             len = items.length;
182             for (; i < len; i++) {
183                 items[i].setPadding(headerHeight);
184             }
185
186             // Size the View within the grid which has had its HeaderContainer entallened (That's a perfectly cromulent word BTW)
187             if (modifiedGrid) {
188                 setTimeout(function() {
189                     modifiedGrid.doLayout();
190                 }, 1);
191             }
192         }
193     },
194
195     // FIX: when flexing we actually don't have enough space as we would
196     // typically because of the scrollOffset on the GridView, must reserve this
197     updateInnerCtSize: function(tSize, calcs) {
198         var me = this,
199             extra;
200
201         // Columns must not account for scroll offset
202         if (!me.isHeader) {
203             me.tooNarrow = calcs.meta.tooNarrow;
204             extra = (me.reserveOffset ? me.availableSpaceOffset : 0);
205
206             if (calcs.meta.tooNarrow) {
207                 tSize.width = calcs.meta.desiredSize + extra;
208             } else {
209                 tSize.width += extra;
210             }
211         }
212
213         return me.callParent(arguments);
214     },
215
216     doOwnerCtLayouts: function() {
217         var ownerCt = this.owner.ownerCt;
218         if (!ownerCt.componentLayout.layoutBusy) {
219             ownerCt.doComponentLayout();
220         }
221     }
222 });