]>
Commit | Line | Data |
---|---|---|
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 | |
12 | Ext.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 |