]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/grid/ColumnLayout.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / grid / ColumnLayout.js
CommitLineData
6527f429
DM
1/**\r
2 * @private\r
3 *\r
4 * This class is used only by the grid's HeaderContainer docked child.\r
5 *\r
6 * It adds the ability to shrink the vertical size of the inner container element back if a grouped\r
7 * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.\r
8 *\r
9 * Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls\r
10 * `setPadding` on the columns so that they lay out correctly.\r
11 */\r
12Ext.define('Ext.grid.ColumnLayout', {\r
13 extend: 'Ext.layout.container.HBox',\r
14 alias: 'layout.gridcolumn',\r
15 type : 'gridcolumn',\r
16\r
17 requires: [\r
18 'Ext.panel.Table' \r
19 ],\r
20\r
21 firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',\r
22 lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',\r
23\r
24 initLayout: function() {\r
25 this.callParent();\r
26\r
27 if (this.scrollbarWidth === undefined) {\r
28 this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width;\r
29 }\r
30 },\r
31\r
32 beginLayout: function (ownerContext) {\r
33 var me = this,\r
34 owner = me.owner,\r
35 firstCls = me.firstHeaderCls,\r
36 lastCls = me.lastHeaderCls,\r
37 bothCls = [firstCls, lastCls],\r
38 items = me.getVisibleItems(),\r
39 len = items.length,\r
40 i, item;\r
41\r
42 me.callParent([ ownerContext ]);\r
43\r
44 // Sync the first/lastCls states for all the headers.\r
45 for (i = 0; i < len; i++) {\r
46 item = items[i];\r
47\r
48 if (len === 1) {\r
49 // item is the only item so it is both first and last\r
50 item.addCls(bothCls);\r
51 }\r
52 else if (i === 0) {\r
53 // item is the first of 2+ items\r
54 item.addCls(firstCls);\r
55 item.removeCls(lastCls);\r
56 }\r
57 else if (i === len - 1) {\r
58 // item is the last of 2+ items\r
59 item.removeCls(firstCls);\r
60 item.addCls(lastCls);\r
61 }\r
62 else {\r
63 item.removeCls(bothCls);\r
64 }\r
65 }\r
66\r
67 // Start this at 0 and for the root headerCt call determineScrollbarWidth to get\r
68 // it set properly. Typically that amounts to a "delete" to expose the system's\r
69 // scrollbar width stored on our prototype.\r
70\r
71 me.scrollbarWidth = 0;\r
72\r
73 if (owner.isRootHeader) {\r
74 me.determineScrollbarWidth(ownerContext);\r
75 }\r
76 if (!me.scrollbarWidth) {\r
77 // By default Mac OS X has overlay scrollbars that do not take space, but also\r
78 // the RTL override may have set this to 0... so make sure we don't try to\r
79 // compensate for a scrollbar when there isn't one.\r
80 ownerContext.manageScrollbar = false;\r
81 }\r
82 },\r
83\r
84 moveItemBefore: function (item, before) {\r
85 var prevOwner = item.ownerCt;\r
86\r
87 // Due to the nature of grid headers, index calculation for\r
88 // moving items is complicated, especially since removals can trigger\r
89 // groups to be removed (and thus alter indexes). As such, the logic\r
90 // is simplified by removing the item first, then calculating the index\r
91 // and inserting it\r
92 if (item !== before && prevOwner) {\r
93 prevOwner.remove(item, false);\r
94 }\r
95 return this.callParent([item, before]);\r
96 },\r
97\r
98 determineScrollbarWidth: function (ownerContext) {\r
99 var me = this,\r
100 owner = me.owner,\r
101 grid = owner.grid,\r
102 // locking headerCt can refuse to reserveScrollbar, even if the locking grid\r
103 // view does reserveScrollbar (special technique for hiding the vertical\r
104 // scrollbar on the locked side)\r
105 vetoReserveScrollbar = owner.reserveScrollbar === false,\r
106 // We read this value off of the immediate grid since the locked side of a\r
107 // locking grid will not have this set. The ownerGrid in that case would have\r
108 // it set but will pass along true only to the normal side.\r
109 reserveScrollbar = grid.reserveScrollbar && !vetoReserveScrollbar,\r
110 manageScrollbar = !reserveScrollbar && !vetoReserveScrollbar &&\r
111 grid.view.scrollFlags.y;\r
112\r
113 // If we have reserveScrollbar then we will always have a vertical scrollbar so\r
114 // manageScrollbar should be false. Otherwise it is based on overflow-y:\r
115 ownerContext.manageScrollbar = manageScrollbar;\r
116\r
117 // Determine if there is any need to deal with the width of the vertical scrollbar\r
118 // and set "scrollbarWidth" to 0 if not or the system determined value (stored on\r
119 // our prototype).\r
120 //\r
121 if (!grid.ownerGrid.collapsed && (reserveScrollbar || manageScrollbar)) {\r
122 // Ensure the real scrollbarWidth value is exposed from the prototype. This\r
123 // may be needed if the scrollFlags have changed since we may have a 0 set on\r
124 // this instance from a previous layout run.\r
125 delete me.scrollbarWidth;\r
126 }\r
127\r
128 // On return, the RTL override (Ext.rtl.grid.ColumnLayout) will deal with various\r
129 // browser bugs and may set me.scrollbarWidth to 0 or a negative value.\r
130 },\r
131\r
132 calculate: function (ownerContext) {\r
133 var me = this,\r
134 grid = me.owner.grid,\r
135 // Our TableLayout buddy sets this in its beginLayout so we can work this\r
136 // out together:\r
137 viewContext = ownerContext.viewContext,\r
138 state = ownerContext.state,\r
139 context = ownerContext.context,\r
140 lockingPartnerContext, ownerGrid,\r
141 columnsChanged, columns, len, i, column, scrollbarAdjustment, viewOverflowY;\r
142\r
143 me.callParent([ ownerContext ]);\r
144\r
145 if (grid && state.parallelDone) {\r
146 lockingPartnerContext = viewContext.lockingPartnerContext;\r
147 ownerGrid = grid.ownerGrid;\r
148\r
149 // A force-fit needs to be "reflexed" so check that now. If we have to reflex\r
150 // the items, we need to re-cacheFlexes and invalidate ourselves.\r
151 if (ownerGrid.forceFit && !state.reflexed) {\r
152 if (me.convertWidthsToFlexes(ownerContext)) {\r
153 me.cacheFlexes(ownerContext);\r
154 me.done = false;\r
155 ownerContext.invalidate({\r
156 state: {\r
157 reflexed: true,\r
158 scrollbarAdjustment: me.getScrollbarAdjustment(ownerContext)\r
159 }\r
160 });\r
161 return;\r
162 }\r
163 }\r
164\r
165 // Once the parallelDone flag goes up, we need to pack up the changed column\r
166 // widths for our TableLayout partner.\r
167 if ((columnsChanged = state.columnsChanged) === undefined) {\r
168 columns = ownerContext.target.getVisibleGridColumns();\r
169 columnsChanged = false;\r
170\r
171 for (i = 0, len = columns.length; i < len; i++) {\r
172 column = context.getCmp(columns[i]);\r
173 // Since we are parallelDone, all of the children should have width,\r
174 // so we can\r
175\r
176 if (!column.lastBox || column.props.width !== column.lastBox.width) {\r
177 (columnsChanged || (columnsChanged = []))[i] = column;\r
178 }\r
179 }\r
180\r
181 state.columnsChanged = columnsChanged;\r
182 // This will trigger our TableLayout partner and allow it to proceed.\r
183 ownerContext.setProp('columnsChanged', columnsChanged);\r
184 }\r
185\r
186 if (ownerContext.manageScrollbar) {\r
187 // If we changed the column widths, we need to wait for the TableLayout to\r
188 // return whether or not we have overflowY... well, that is, if we are\r
189 // needing to tweak the scrollbarAdjustment...\r
190 scrollbarAdjustment = me.getScrollbarAdjustment(ownerContext);\r
191\r
192 if (scrollbarAdjustment) {\r
193 // Since we start with the assumption that we will need the scrollbar,\r
194 // we now need to wait to see if our guess was correct.\r
195 viewOverflowY = viewContext.getProp('viewOverflowY');\r
196 if (viewOverflowY === undefined) {\r
197 // The TableLayout has not determined this yet, so park it.\r
198 me.done = false;\r
199 return;\r
200 }\r
201\r
202 if (!viewOverflowY) {\r
203 // We have our answer, and it turns out the view did not overflow\r
204 // (even with the reduced width we gave it), so we need to remove\r
205 // the scrollbarAdjustment and go again.\r
206 if (lockingPartnerContext) {\r
207 // In a locking grid, only the normal side plays this game,\r
208 // so now that we know the resolution, we need to invalidate\r
209 // the locking view and its headerCt.\r
210 lockingPartnerContext.invalidate();\r
211 lockingPartnerContext.headerContext.invalidate();\r
212 }\r
213 viewContext.invalidate();\r
214 ownerContext.invalidate({\r
215 state: {\r
216 // Pass a 0 adjustment on into our next life. If this is\r
217 // the invalidate that resets ownerContext then this is\r
218 // put onto the new state. If not, it will reset back to\r
219 // undefined and we'll have to begin again (which is the\r
220 // correct thing to do in that case).\r
221 scrollbarAdjustment: 0\r
222 }\r
223 });\r
224 }\r
225 }\r
226 // else {\r
227 // We originally assumed we would need the scrollbar and since we do\r
228 // not now, we must be on the second pass, so we can move on...\r
229 // }\r
230 }\r
231 }\r
232 },\r
233\r
234 finishedLayout: function(ownerContext) {\r
235 this.callParent([ ownerContext ]);\r
236 if (this.owner.ariaRole === 'rowgroup') {\r
237 this.innerCt.dom.setAttribute('role', 'row');\r
238 }\r
239\r
240 // Wipe this array because it holds component references and gets cached on the object\r
241 // Can cause a circular reference\r
242 ownerContext.props.columnsChanged = null;\r
243 },\r
244\r
245 convertWidthsToFlexes: function(ownerContext) {\r
246 var me = this,\r
247 totalWidth = 0,\r
248 calculated = me.sizeModels.calculated,\r
249 childItems, len, i, childContext, item;\r
250\r
251 childItems = ownerContext.childItems;\r
252 len = childItems.length;\r
253\r
254 for (i = 0; i < len; i++) {\r
255 childContext = childItems[i];\r
256 item = childContext.target;\r
257\r
258 totalWidth += childContext.props.width;\r
259\r
260 // Only allow to be flexed if it's a resizable column\r
261 if (!(item.fixed || item.resizable === false)) {\r
262 // For forceFit, just use allocated width as the flex value, and the proportions\r
263 // will end up the same whatever HeaderContainer width they are being forced into.\r
264 item.flex = ownerContext.childItems[i].flex = childContext.props.width;\r
265 item.width = null;\r
266 childContext.widthModel = calculated;\r
267 }\r
268 }\r
269\r
270 // Only need to loop back if the total column width is not already an exact fit\r
271 return totalWidth !== ownerContext.props.width;\r
272 },\r
273\r
274 getScrollbarAdjustment: function (ownerContext) {\r
275 var me = this,\r
276 state = ownerContext.state,\r
277 grid = me.owner.grid,\r
278 scrollbarAdjustment = state.scrollbarAdjustment;\r
279\r
280 // If there is potential for a vertical scrollbar, then we start by assuming\r
281 // we will need to reserve space for it. Unless, of course, there are no\r
282 // records!\r
283 if (scrollbarAdjustment === undefined) {\r
284 scrollbarAdjustment = 0;\r
285\r
286 if (grid.reserveScrollbar || (ownerContext.manageScrollbar &&\r
287 !grid.ownerGrid.layout.ownerContext.heightModel.shrinkWrap)) {\r
288 scrollbarAdjustment = me.scrollbarWidth;\r
289 }\r
290\r
291 state.scrollbarAdjustment = scrollbarAdjustment;\r
292 }\r
293\r
294 return scrollbarAdjustment;\r
295 },\r
296\r
297 /**\r
298 * @private\r
299 * Local getContainerSize implementation accounts for vertical scrollbar in the view.\r
300 */\r
301 getContainerSize: function (ownerContext) {\r
302 var me = this,\r
303 got, needed, padding, gotWidth, gotHeight, width, height, result;\r
304\r
305 if (me.owner.isRootHeader) {\r
306 result = me.callParent([ ownerContext ]);\r
307\r
308 if (result.gotWidth) {\r
309 result.width -= me.getScrollbarAdjustment(ownerContext);\r
310 }\r
311 } else {\r
312 padding = ownerContext.paddingContext.getPaddingInfo();\r
313 got = needed = 0;\r
314\r
315 // The container size here has to be provided by the ColumnComponentLayout to\r
316 // account for borders in its odd way.\r
317 if (!ownerContext.widthModel.shrinkWrap) {\r
318 ++needed;\r
319 width = ownerContext.getProp('innerWidth');\r
320 gotWidth = (typeof width === 'number');\r
321 if (gotWidth) {\r
322 ++got;\r
323 width -= padding.width;\r
324 if (width < 0) {\r
325 width = 0;\r
326 }\r
327 }\r
328 }\r
329\r
330 if (!ownerContext.heightModel.shrinkWrap) {\r
331 ++needed;\r
332 height = ownerContext.getProp('innerHeight');\r
333 gotHeight = (typeof height === 'number');\r
334 if (gotHeight) {\r
335 ++got;\r
336 height -= padding.height;\r
337 if (height < 0) {\r
338 height = 0;\r
339 }\r
340 }\r
341 }\r
342\r
343 return {\r
344 width: width,\r
345 height: height,\r
346 needed: needed,\r
347 got: got,\r
348 gotAll: got === needed,\r
349 gotWidth: gotWidth,\r
350 gotHeight: gotHeight\r
351 };\r
352 }\r
353\r
354 return result;\r
355 },\r
356\r
357 publishInnerCtSize: function(ownerContext) {\r
358 var me = this,\r
359 owner = me.owner,\r
360 cw = ownerContext.peek('contentWidth'),\r
361 adjustment = 0;\r
362\r
363 // Pass negative "reservedSpace", so that the innerCt gets *extra* size to accommodate the view's vertical scrollbar\r
364 if (cw != null && owner.isRootHeader) {\r
365 adjustment = -ownerContext.state.scrollbarAdjustment;\r
366 }\r
367\r
368 return me.callParent([ownerContext, adjustment]);\r
369 }\r
370});\r