]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * This class is intended to be extended or created via the {@link Ext.container.Container#layout layout}\r | |
3 | * configuration property. See {@link Ext.container.Container#layout} for additional details.\r | |
4 | */\r | |
5 | Ext.define('Ext.layout.container.Container', {\r | |
6 | extend: 'Ext.layout.Layout',\r | |
7 | \r | |
8 | alias: 'layout.container',\r | |
9 | \r | |
10 | alternateClassName: 'Ext.layout.ContainerLayout',\r | |
11 | \r | |
12 | mixins: [\r | |
13 | 'Ext.util.ElementContainer'\r | |
14 | ],\r | |
15 | \r | |
16 | requires: [\r | |
17 | 'Ext.XTemplate'\r | |
18 | ],\r | |
19 | \r | |
20 | type: 'container',\r | |
21 | \r | |
22 | /* End Definitions */\r | |
23 | \r | |
24 | /**\r | |
25 | * @cfg {String} itemCls\r | |
26 | * An optional extra CSS class that will be added to the container. This can be useful for\r | |
27 | * adding customized styles to the container or any of its children using standard CSS\r | |
28 | * rules. See {@link Ext.Component}.{@link Ext.Component#componentCls componentCls} also.\r | |
29 | */\r | |
30 | \r | |
31 | /**\r | |
32 | * @private\r | |
33 | * Called by an owning Panel before the Panel begins its collapse process.\r | |
34 | * Most layouts will not need to override the default Ext.emptyFn implementation.\r | |
35 | */\r | |
36 | beginCollapse: Ext.emptyFn,\r | |
37 | \r | |
38 | /**\r | |
39 | * @private\r | |
40 | * Called by an owning Panel before the Panel begins its expand process.\r | |
41 | * Most layouts will not need to override the default Ext.emptyFn implementation.\r | |
42 | */\r | |
43 | beginExpand: Ext.emptyFn,\r | |
44 | \r | |
45 | /**\r | |
46 | * An object which contains boolean properties specifying which properties are to be\r | |
47 | * animated upon flush of child Component ContextItems. For example, Accordion would\r | |
48 | * have:\r | |
49 | *\r | |
50 | * {\r | |
51 | * y: true,\r | |
52 | * height: true\r | |
53 | * }\r | |
54 | *\r | |
55 | * @private\r | |
56 | */\r | |
57 | animatePolicy: null,\r | |
58 | \r | |
59 | /**\r | |
60 | * @private\r | |
61 | * tracks the number of child items that do not use "liquid" CSS layout\r | |
62 | */\r | |
63 | activeItemCount: 0,\r | |
64 | \r | |
65 | renderTpl: [\r | |
66 | '{%this.renderBody(out,values)%}'\r | |
67 | ],\r | |
68 | \r | |
69 | usesContainerHeight: true,\r | |
70 | usesContainerWidth: true,\r | |
71 | usesHeight: true,\r | |
72 | usesWidth: true,\r | |
73 | \r | |
74 | constructor: function () {\r | |
75 | this.callParent(arguments);\r | |
76 | this.mixins.elementCt.constructor.call(this);\r | |
77 | },\r | |
78 | \r | |
79 | destroy: function() {\r | |
80 | this.callParent();\r | |
81 | this.mixins.elementCt.destroy.call(this);\r | |
82 | },\r | |
83 | \r | |
84 | /**\r | |
85 | * In addition to work done by our base classes, containers benefit from some extra\r | |
86 | * cached data. The following properties are added to the ownerContext:\r | |
87 | *\r | |
88 | * - visibleItems: the result of {@link #getVisibleItems}\r | |
89 | * - childItems: the ContextItem[] for each visible item\r | |
90 | * - targetContext: the ContextItem for the {@link #getTarget} element\r | |
91 | */\r | |
92 | beginLayout: function (ownerContext) {\r | |
93 | this.callParent(arguments);\r | |
94 | \r | |
95 | ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this);\r | |
96 | \r | |
97 | this.cacheChildItems(ownerContext);\r | |
98 | },\r | |
99 | \r | |
100 | beginLayoutCycle: function (ownerContext, firstCycle) {\r | |
101 | var me = this;\r | |
102 | \r | |
103 | me.callParent(arguments);\r | |
104 | \r | |
105 | if (firstCycle) {\r | |
106 | if (me.usesContainerHeight) {\r | |
107 | ++ownerContext.consumersContainerHeight;\r | |
108 | }\r | |
109 | if (me.usesContainerWidth) {\r | |
110 | ++ownerContext.consumersContainerWidth;\r | |
111 | }\r | |
112 | }\r | |
113 | },\r | |
114 | \r | |
115 | cacheChildItems: function (ownerContext) {\r | |
116 | var me = this,\r | |
117 | context, childItems, items, length, i;\r | |
118 | \r | |
119 | // if we neither read nor set the size of our items, we can skip creation of\r | |
120 | // the childItems array\r | |
121 | if (me.needsItemSize || me.setsItemSize) {\r | |
122 | context = ownerContext.context;\r | |
123 | childItems = ownerContext.childItems = [];\r | |
124 | items = ownerContext.visibleItems = me.getVisibleItems();\r | |
125 | length = items.length;\r | |
126 | \r | |
127 | for (i = 0; i < length; ++i) {\r | |
128 | childItems.push(context.getCmp(items[i]));\r | |
129 | }\r | |
130 | }\r | |
131 | },\r | |
132 | \r | |
133 | cacheElements: function () {\r | |
134 | var owner = this.owner;\r | |
135 | \r | |
136 | this.attachChildEls(owner.el, owner); // from ElementContainer mixin\r | |
137 | },\r | |
138 | \r | |
139 | calculate: function(ownerContext) {\r | |
140 | var props = ownerContext.props,\r | |
141 | el = ownerContext.el;\r | |
142 | \r | |
143 | if (ownerContext.widthModel.shrinkWrap && isNaN(props.width)) {\r | |
144 | ownerContext.setContentWidth(el.getWidth());\r | |
145 | }\r | |
146 | \r | |
147 | if (ownerContext.heightModel.shrinkWrap && isNaN(props.height)) {\r | |
148 | ownerContext.setContentHeight(el.getHeight());\r | |
149 | }\r | |
150 | },\r | |
151 | \r | |
152 | /**\r | |
153 | * Adds layout's itemCls and owning Container's itemCls\r | |
154 | * @protected\r | |
155 | */\r | |
156 | configureItem: function(item) {\r | |
157 | var me = this,\r | |
158 | itemCls = me.itemCls,\r | |
159 | ownerItemCls = me.owner.itemCls,\r | |
160 | needsCopy,\r | |
161 | addClasses;\r | |
162 | \r | |
163 | // Effectively callParent but without the function overhead\r | |
164 | item.ownerLayout = me;\r | |
165 | \r | |
166 | if (itemCls) {\r | |
167 | // itemCls can be a single class or an array\r | |
168 | if (typeof itemCls === 'string') {\r | |
169 | addClasses = [itemCls];\r | |
170 | } else {\r | |
171 | addClasses = itemCls;\r | |
172 | needsCopy = !!addClasses;\r | |
173 | }\r | |
174 | }\r | |
175 | if (ownerItemCls) {\r | |
176 | // Add some extra logic so we don't clone the array unnecessarily\r | |
177 | if (needsCopy) {\r | |
178 | addClasses = Ext.Array.clone(addClasses);\r | |
179 | }\r | |
180 | addClasses = Ext.Array.push(addClasses || [], ownerItemCls);\r | |
181 | }\r | |
182 | if (addClasses) {\r | |
183 | item.addCls(addClasses);\r | |
184 | }\r | |
185 | },\r | |
186 | \r | |
187 | doRenderBody: function (out, renderData) {\r | |
188 | // Careful! This method is bolted on to the renderTpl so all we get for context is\r | |
189 | // the renderData! The "this" pointer is the renderTpl instance!\r | |
190 | \r | |
191 | this.renderItems(out, renderData);\r | |
192 | this.renderContent(out, renderData);\r | |
193 | },\r | |
194 | \r | |
195 | doRenderContainer: function (out, renderData) {\r | |
196 | // Careful! This method is bolted on to the renderTpl so all we get for context is\r | |
197 | // the renderData! The "this" pointer is the renderTpl instance!\r | |
198 | \r | |
199 | var me = renderData.$comp.layout,\r | |
200 | tpl = me.getRenderTpl(),\r | |
201 | data = me.getRenderData();\r | |
202 | \r | |
203 | tpl.applyOut(data, out);\r | |
204 | },\r | |
205 | \r | |
206 | doRenderItems: function (out, renderData) {\r | |
207 | // Careful! This method is bolted on to the renderTpl so all we get for context is\r | |
208 | // the renderData! The "this" pointer is the renderTpl instance!\r | |
209 | \r | |
210 | var me = renderData.$layout,\r | |
211 | tree = me.getRenderTree();\r | |
212 | \r | |
213 | if (tree) {\r | |
214 | Ext.DomHelper.generateMarkup(tree, out);\r | |
215 | }\r | |
216 | },\r | |
217 | \r | |
218 | finishRender: function () {\r | |
219 | var me = this,\r | |
220 | target, items;\r | |
221 | \r | |
222 | me.callParent();\r | |
223 | \r | |
224 | me.cacheElements();\r | |
225 | \r | |
226 | target = me.getRenderTarget();\r | |
227 | items = me.getLayoutItems();\r | |
228 | \r | |
229 | me.finishRenderItems(target, items);\r | |
230 | },\r | |
231 | \r | |
232 | /**\r | |
233 | * @private\r | |
234 | * Called for every layout in the layout context after all the layouts have been finally flushed\r | |
235 | */\r | |
236 | notifyOwner: function() {\r | |
237 | //<debug>\r | |
238 | if (!this._hasTargetWarning && this.targetCls && !this.getTarget().hasCls(this.targetCls)) {\r | |
239 | this._hasTargetWarning = true;\r | |
240 | Ext.log.warn('targetCls is missing. This may mean that getTargetEl() is being overridden but not applyTargetCls(). ' + this.owner.id);\r | |
241 | }\r | |
242 | //</debug>\r | |
243 | this.owner.afterLayout(this);\r | |
244 | },\r | |
245 | \r | |
246 | /**\r | |
247 | * Returns the container size (that of the target). Only the fixed-sized dimensions can\r | |
248 | * be returned because the shrinkWrap dimensions are based on the contentWidth/Height\r | |
249 | * as determined by the container layout.\r | |
250 | *\r | |
251 | * @param {Ext.layout.ContextItem} ownerContext The owner's context item.\r | |
252 | * @param {Boolean} [inDom=false] True if the container size must be in the DOM.\r | |
253 | * @return {Object} The size\r | |
254 | * @return {Number} return.width The width\r | |
255 | * @return {Number} return.height The height\r | |
256 | * @protected\r | |
257 | */\r | |
258 | getContainerSize : function(ownerContext, inDom) {\r | |
259 | // Subtle But Important:\r | |
260 | //\r | |
261 | // We don't want to call getProp/hasProp et.al. unless we in fact need that value\r | |
262 | // for our results! If we call it and don't need it, the layout manager will think\r | |
263 | // we depend on it and will schedule us again should it change.\r | |
264 | \r | |
265 | var targetContext = ownerContext.targetContext,\r | |
266 | frameInfo = targetContext.getFrameInfo(),\r | |
267 | padding = ownerContext.paddingContext.getPaddingInfo(),\r | |
268 | got = 0,\r | |
269 | needed = 0,\r | |
270 | gotWidth, gotHeight, width, height;\r | |
271 | \r | |
272 | // In an shrinkWrap width/height case, we must not ask for any of these dimensions\r | |
273 | // because they will be determined by contentWidth/Height which is calculated by\r | |
274 | // this layout...\r | |
275 | \r | |
276 | // Fit/Card layouts are able to set just the width of children, allowing child's\r | |
277 | // resulting height to autosize the Container.\r | |
278 | // See examples/tabs/tabs.html for an example of this.\r | |
279 | \r | |
280 | if (!ownerContext.widthModel.shrinkWrap) {\r | |
281 | ++needed;\r | |
282 | width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');\r | |
283 | gotWidth = (typeof width === 'number');\r | |
284 | if (gotWidth) {\r | |
285 | ++got;\r | |
286 | width -= frameInfo.width + padding.width;\r | |
287 | if (width < 0) {\r | |
288 | width = 0;\r | |
289 | }\r | |
290 | }\r | |
291 | }\r | |
292 | \r | |
293 | if (!ownerContext.heightModel.shrinkWrap) {\r | |
294 | ++needed;\r | |
295 | height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');\r | |
296 | gotHeight = (typeof height === 'number');\r | |
297 | if (gotHeight) {\r | |
298 | ++got;\r | |
299 | height -= frameInfo.height + padding.height;\r | |
300 | if (height < 0) {\r | |
301 | height = 0;\r | |
302 | }\r | |
303 | }\r | |
304 | }\r | |
305 | \r | |
306 | return {\r | |
307 | width: width,\r | |
308 | height: height,\r | |
309 | needed: needed,\r | |
310 | got: got,\r | |
311 | gotAll: got === needed,\r | |
312 | gotWidth: gotWidth,\r | |
313 | gotHeight: gotHeight\r | |
314 | };\r | |
315 | },\r | |
316 | \r | |
317 | // This method is used to offset the DOM position when checking\r | |
318 | // whether the element is a certain child of the target. This is\r | |
319 | // required in cases where the extra elements prepended to the target\r | |
320 | // before any of the items. An example of this is when using labelAlign: 'top'\r | |
321 | // on a field. The label appears first in the DOM before any child items are\r | |
322 | // created, so when we check the position we need to add an extra offset.\r | |
323 | // Containers that create an innerCt are exempt because this new element\r | |
324 | // preserves the order\r | |
325 | getPositionOffset: function(position) {\r | |
326 | if (!this.createsInnerCt) {\r | |
327 | var offset = this.owner.itemNodeOffset;\r | |
328 | if (offset) {\r | |
329 | position += offset;\r | |
330 | }\r | |
331 | }\r | |
332 | return position;\r | |
333 | },\r | |
334 | \r | |
335 | /**\r | |
336 | * Returns an array of child components either for a render phase (Performed in the beforeLayout\r | |
337 | * method of the layout's base class), or the layout phase (onLayout).\r | |
338 | * @return {Ext.Component[]} of child components\r | |
339 | */\r | |
340 | getLayoutItems: function() {\r | |
341 | var owner = this.owner,\r | |
342 | items = owner && owner.items;\r | |
343 | \r | |
344 | return (items && items.items) || [];\r | |
345 | },\r | |
346 | \r | |
347 | getRenderData: function () {\r | |
348 | var comp = this.owner;\r | |
349 | \r | |
350 | return {\r | |
351 | $comp: comp,\r | |
352 | $layout: this,\r | |
353 | ownerId: comp.id\r | |
354 | };\r | |
355 | },\r | |
356 | \r | |
357 | /**\r | |
358 | * @protected\r | |
359 | * Returns all items that are rendered\r | |
360 | * @return {Array} All matching items\r | |
361 | */\r | |
362 | getRenderedItems: function() {\r | |
363 | var me = this,\r | |
364 | target = me.getRenderTarget(),\r | |
365 | items = me.getLayoutItems(),\r | |
366 | ln = items.length,\r | |
367 | renderedItems = [],\r | |
368 | i, item;\r | |
369 | \r | |
370 | for (i = 0; i < ln; i++) {\r | |
371 | item = items[i];\r | |
372 | if (item.rendered && me.isValidParent(item, target, i)) {\r | |
373 | renderedItems.push(item);\r | |
374 | }\r | |
375 | }\r | |
376 | \r | |
377 | return renderedItems;\r | |
378 | },\r | |
379 | \r | |
380 | /**\r | |
381 | * Returns the element into which rendering must take place. Defaults to the owner Container's\r | |
382 | * target element.\r | |
383 | *\r | |
384 | * May be overridden in layout managers which implement an inner element.\r | |
385 | *\r | |
386 | * @return {Ext.dom.Element}\r | |
387 | */\r | |
388 | getRenderTarget: function() {\r | |
389 | return this.owner.getTargetEl();\r | |
390 | },\r | |
391 | \r | |
392 | /**\r | |
393 | * Returns the element into which extra functional DOM elements can be inserted. Defaults to the owner Component's encapsulating element.\r | |
394 | *\r | |
395 | * May be overridden in Component layout managers which implement a {@link #getRenderTarget component render target} which must only\r | |
396 | * contain child components.\r | |
397 | * @return {Ext.dom.Element}\r | |
398 | */\r | |
399 | getElementTarget: function() {\r | |
400 | return this.getRenderTarget();\r | |
401 | },\r | |
402 | \r | |
403 | getRenderTpl: function () {\r | |
404 | var me = this,\r | |
405 | renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl');\r | |
406 | \r | |
407 | // Make sure all standard callout methods for the owner component are placed on the\r | |
408 | // XTemplate instance (but only once please):\r | |
409 | if (!renderTpl.renderContent) {\r | |
410 | me.owner.setupRenderTpl(renderTpl);\r | |
411 | }\r | |
412 | \r | |
413 | return renderTpl;\r | |
414 | },\r | |
415 | \r | |
416 | getRenderTree: function () {\r | |
417 | var result,\r | |
418 | items = this.owner.items,\r | |
419 | itemsGen,\r | |
420 | renderCfgs = {};\r | |
421 | \r | |
422 | do {\r | |
423 | itemsGen = items.generation;\r | |
424 | result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs);\r | |
425 | } while (items.generation !== itemsGen);\r | |
426 | return result;\r | |
427 | },\r | |
428 | \r | |
429 | renderChildren: function () {\r | |
430 | var me = this,\r | |
431 | ownerItems = me.owner.items,\r | |
432 | target = me.getRenderTarget(),\r | |
433 | itemsGen, items;\r | |
434 | \r | |
435 | // During the render phase, new items may be added. Specifically, a panel will\r | |
436 | // create a placeholder component during render if required, so we need to catch\r | |
437 | // it here so we can render it.\r | |
438 | do {\r | |
439 | itemsGen = ownerItems.generation;\r | |
440 | items = me.getLayoutItems();\r | |
441 | me.renderItems(items, target);\r | |
442 | } while (ownerItems.generation !== itemsGen);\r | |
443 | },\r | |
444 | \r | |
445 | getScrollbarsNeeded: function (width, height, contentWidth, contentHeight) {\r | |
446 | var scrollbarSize = Ext.getScrollbarSize(),\r | |
447 | hasWidth = typeof width === 'number',\r | |
448 | hasHeight = typeof height === 'number',\r | |
449 | needHorz = 0,\r | |
450 | needVert = 0;\r | |
451 | \r | |
452 | // No space-consuming scrollbars.\r | |
453 | if (!scrollbarSize.width) {\r | |
454 | return 0;\r | |
455 | }\r | |
456 | if (hasHeight && height < contentHeight) {\r | |
457 | needVert = 2;\r | |
458 | width -= scrollbarSize.width;\r | |
459 | }\r | |
460 | \r | |
461 | if (hasWidth && width < contentWidth) {\r | |
462 | needHorz = 1;\r | |
463 | if (!needVert && hasHeight) {\r | |
464 | height -= scrollbarSize.height;\r | |
465 | if (height < contentHeight) {\r | |
466 | needVert = 2;\r | |
467 | }\r | |
468 | }\r | |
469 | }\r | |
470 | \r | |
471 | return needVert + needHorz;\r | |
472 | },\r | |
473 | \r | |
474 | /**\r | |
475 | * Returns the owner component's resize element.\r | |
476 | * @return {Ext.dom.Element}\r | |
477 | */\r | |
478 | getTarget: function() {\r | |
479 | return this.owner.getTargetEl();\r | |
480 | },\r | |
481 | \r | |
482 | /**\r | |
483 | * @protected\r | |
484 | * Returns all items that are both rendered and visible\r | |
485 | * @return {Array} All matching items\r | |
486 | */\r | |
487 | getVisibleItems: function() {\r | |
488 | var target = this.getRenderTarget(),\r | |
489 | items = this.getLayoutItems(),\r | |
490 | ln = items.length,\r | |
491 | visibleItems = [],\r | |
492 | i, item;\r | |
493 | \r | |
494 | for (i = 0; i < ln; i++) {\r | |
495 | item = items[i];\r | |
496 | if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true && !item.floated) {\r | |
497 | visibleItems.push(item);\r | |
498 | }\r | |
499 | }\r | |
500 | \r | |
501 | return visibleItems;\r | |
502 | },\r | |
503 | \r | |
504 | getMoveAfterIndex: function (after) {\r | |
505 | return this.owner.items.indexOf(after) + 1;\r | |
506 | },\r | |
507 | \r | |
508 | moveItemBefore: function (item, before) {\r | |
509 | var owner = this.owner,\r | |
510 | items = owner.items,\r | |
511 | index = items.indexOf(item),\r | |
512 | toIndex;\r | |
513 | \r | |
514 | if (item === before) {\r | |
515 | return item;\r | |
516 | }\r | |
517 | \r | |
518 | if (before) {\r | |
519 | toIndex = items.indexOf(before);\r | |
520 | if (index > -1 && index < toIndex) {\r | |
521 | --toIndex;\r | |
522 | }\r | |
523 | } else {\r | |
524 | toIndex = items.length;\r | |
525 | }\r | |
526 | \r | |
527 | return owner.insert(toIndex, item);\r | |
528 | },\r | |
529 | \r | |
530 | setupRenderTpl: function (renderTpl) {\r | |
531 | renderTpl.renderBody = this.doRenderBody;\r | |
532 | renderTpl.renderContainer = this.doRenderContainer;\r | |
533 | renderTpl.renderItems = this.doRenderItems;\r | |
534 | },\r | |
535 | \r | |
536 | getContentTarget: function(){\r | |
537 | return this.owner.getDefaultContentTarget();\r | |
538 | },\r | |
539 | \r | |
540 | onAdd: function (item) {\r | |
541 | if (!item.liquidLayout) {\r | |
542 | ++this.activeItemCount;\r | |
543 | }\r | |
544 | this.callParent([item]);\r | |
545 | },\r | |
546 | \r | |
547 | onRemove: function(item, isDestroying) {\r | |
548 | if (!item.liquidLayout) {\r | |
549 | --this.activeItemCount;\r | |
550 | }\r | |
551 | this.callParent([item, isDestroying]);\r | |
552 | }\r | |
553 | });\r |