]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * This class is intended to be extended or created via the {@link Ext.Component#componentLayout layout}\r | |
3 | * configuration property. See {@link Ext.Component#componentLayout} for additional details.\r | |
4 | * @private\r | |
5 | */\r | |
6 | Ext.define('Ext.layout.component.Component', {\r | |
7 | extend: 'Ext.layout.Layout',\r | |
8 | \r | |
9 | type: 'component',\r | |
10 | \r | |
11 | isComponentLayout: true,\r | |
12 | \r | |
13 | nullBox: {},\r | |
14 | \r | |
15 | usesContentHeight: true,\r | |
16 | usesContentWidth: true,\r | |
17 | usesHeight: true,\r | |
18 | usesWidth: true,\r | |
19 | \r | |
20 | widthCache: {},\r | |
21 | heightCache: {},\r | |
22 | \r | |
23 | beginLayoutCycle: function (ownerContext, firstCycle) {\r | |
24 | var me = this,\r | |
25 | owner = me.owner,\r | |
26 | ownerCtContext = ownerContext.ownerCtContext,\r | |
27 | heightModel = ownerContext.heightModel,\r | |
28 | widthModel = ownerContext.widthModel,\r | |
29 | body = owner.el.dom === document.body,\r | |
30 | lastBox = owner.lastBox || me.nullBox,\r | |
31 | lastSize = owner.el.lastBox || me.nullBox,\r | |
32 | dirty = !body,\r | |
33 | isTopLevel = ownerContext.isTopLevel,\r | |
34 | ownerLayout, v, width, height;\r | |
35 | \r | |
36 | me.callParent([ownerContext, firstCycle]);\r | |
37 | \r | |
38 | if (firstCycle) {\r | |
39 | if (me.usesContentWidth) {\r | |
40 | ++ownerContext.consumersContentWidth;\r | |
41 | }\r | |
42 | if (me.usesContentHeight) {\r | |
43 | ++ownerContext.consumersContentHeight;\r | |
44 | }\r | |
45 | if (me.usesWidth) {\r | |
46 | ++ownerContext.consumersWidth;\r | |
47 | }\r | |
48 | if (me.usesHeight) {\r | |
49 | ++ownerContext.consumersHeight;\r | |
50 | }\r | |
51 | \r | |
52 | if (ownerCtContext && !ownerCtContext.hasRawContent) {\r | |
53 | ownerLayout = owner.ownerLayout;\r | |
54 | \r | |
55 | if (ownerLayout) {\r | |
56 | if (ownerLayout.usesWidth) {\r | |
57 | ++ownerContext.consumersWidth;\r | |
58 | }\r | |
59 | if (ownerLayout.usesHeight) {\r | |
60 | ++ownerContext.consumersHeight;\r | |
61 | }\r | |
62 | }\r | |
63 | }\r | |
64 | }\r | |
65 | \r | |
66 | // we want to publish configured dimensions as early as possible and since this is\r | |
67 | // a write phase...\r | |
68 | \r | |
69 | if (widthModel.configured) {\r | |
70 | // If the owner.el is the body, owner.width is not dirty (we don't want to write\r | |
71 | // it to the body el). For other el's, the width may already be correct in the\r | |
72 | // DOM (e.g., it is rendered in the markup initially). If the width is not\r | |
73 | // correct in the DOM, this is only going to be the case on the first cycle.\r | |
74 | width = owner[widthModel.names.width];\r | |
75 | if (isTopLevel && widthModel.calculatedFrom) {\r | |
76 | width = lastBox.width;\r | |
77 | }\r | |
78 | \r | |
79 | if (!body) {\r | |
80 | dirty = me.setWidthInDom ||\r | |
81 | (firstCycle ? width !== lastSize.width : widthModel.constrained);\r | |
82 | }\r | |
83 | \r | |
84 | \r | |
85 | ownerContext.setWidth(width, dirty);\r | |
86 | } else if (isTopLevel) {\r | |
87 | if (widthModel.calculated) {\r | |
88 | v = lastBox.width;\r | |
89 | ownerContext.setWidth(v, /*dirty=*/v !== lastSize.width);\r | |
90 | }\r | |
91 | \r | |
92 | v = lastBox.x;\r | |
93 | ownerContext.setProp('x', v, /*dirty=*/v !== lastSize.x);\r | |
94 | }\r | |
95 | \r | |
96 | if (heightModel.configured) {\r | |
97 | height = owner[heightModel.names.height];\r | |
98 | if (isTopLevel && heightModel.calculatedFrom) {\r | |
99 | height = lastBox.height;\r | |
100 | }\r | |
101 | \r | |
102 | if (!body) {\r | |
103 | dirty = firstCycle ? height !== lastSize.height\r | |
104 | : heightModel.constrained;\r | |
105 | }\r | |
106 | \r | |
107 | ownerContext.setHeight(height, dirty);\r | |
108 | } else if (isTopLevel) {\r | |
109 | if (heightModel.calculated) {\r | |
110 | v = lastBox.height;\r | |
111 | ownerContext.setHeight(v, v !== lastSize.height);\r | |
112 | }\r | |
113 | \r | |
114 | v = lastBox.y;\r | |
115 | ownerContext.setProp('y', v, /*dirty=*/v !== lastSize.y);\r | |
116 | }\r | |
117 | },\r | |
118 | \r | |
119 | finishedLayout: function(ownerContext) {\r | |
120 | var me = this,\r | |
121 | elementChildren = ownerContext.children,\r | |
122 | owner = me.owner,\r | |
123 | len, i, elContext, lastBox, props;\r | |
124 | \r | |
125 | // NOTE: In the code below we cannot use getProp because that will generate a layout dependency\r | |
126 | \r | |
127 | // Set lastBox on managed child Elements.\r | |
128 | // So that ContextItem.constructor can snag the lastBox for use by its undo method.\r | |
129 | if (elementChildren) {\r | |
130 | len = elementChildren.length;\r | |
131 | for (i = 0; i < len; i++) {\r | |
132 | elContext = elementChildren[i];\r | |
133 | elContext.el.lastBox = elContext.props;\r | |
134 | }\r | |
135 | }\r | |
136 | \r | |
137 | // Cache the size from which we are changing so that notifyOwner can notify the owningComponent with all essential information\r | |
138 | ownerContext.previousSize = me.lastComponentSize;\r | |
139 | \r | |
140 | // Cache the currently layed out size\r | |
141 | me.lastComponentSize = owner.el.lastBox = props = ownerContext.props;\r | |
142 | \r | |
143 | // lastBox is a copy of the defined props to allow save/restore of these (panel\r | |
144 | // collapse needs this)\r | |
145 | lastBox = owner.lastBox || (owner.lastBox = {});\r | |
146 | lastBox.x = props.x;\r | |
147 | lastBox.y = props.y;\r | |
148 | lastBox.width = props.width;\r | |
149 | lastBox.height = props.height;\r | |
150 | lastBox.invalid = false;\r | |
151 | \r | |
152 | me.callParent([ownerContext]);\r | |
153 | },\r | |
154 | \r | |
155 | notifyOwner: function(ownerContext) {\r | |
156 | var me = this,\r | |
157 | currentSize = me.lastComponentSize,\r | |
158 | prevSize = ownerContext.previousSize;\r | |
159 | \r | |
160 | me.owner.afterComponentLayout(currentSize.width, currentSize.height, \r | |
161 | prevSize ? prevSize.width : undefined, prevSize ? prevSize.height : undefined);\r | |
162 | },\r | |
163 | \r | |
164 | /**\r | |
165 | * Returns the owner component's resize element.\r | |
166 | * @return {Ext.dom.Element}\r | |
167 | */\r | |
168 | getTarget : function() {\r | |
169 | return this.owner.el;\r | |
170 | },\r | |
171 | \r | |
172 | /**\r | |
173 | * Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.\r | |
174 | *\r | |
175 | * May be overridden in Component layout managers which implement an inner element.\r | |
176 | * @return {Ext.dom.Element}\r | |
177 | */\r | |
178 | getRenderTarget : function() {\r | |
179 | return this.owner.el;\r | |
180 | },\r | |
181 | \r | |
182 | cacheTargetInfo: function(ownerContext) {\r | |
183 | var me = this,\r | |
184 | targetInfo = me.targetInfo,\r | |
185 | target;\r | |
186 | \r | |
187 | if (!targetInfo) {\r | |
188 | target = ownerContext.getEl('getTarget', me);\r | |
189 | \r | |
190 | me.targetInfo = targetInfo = {\r | |
191 | padding: target.getPaddingInfo(),\r | |
192 | border: target.getBorderInfo()\r | |
193 | };\r | |
194 | }\r | |
195 | \r | |
196 | return targetInfo;\r | |
197 | },\r | |
198 | \r | |
199 | measureAutoDimensions: function (ownerContext, dimensions) {\r | |
200 | // Subtle But Important:\r | |
201 | // \r | |
202 | // We don't want to call getProp/hasProp et.al. unless we in fact need that value\r | |
203 | // for our results! If we call it and don't need it, the layout manager will think\r | |
204 | // we depend on it and will schedule us again should it change.\r | |
205 | \r | |
206 | var me = this,\r | |
207 | owner = me.owner,\r | |
208 | containerLayout = owner.layout,\r | |
209 | heightModel = ownerContext.heightModel,\r | |
210 | widthModel = ownerContext.widthModel,\r | |
211 | boxParent = ownerContext.boxParent,\r | |
212 | isBoxParent = ownerContext.isBoxParent,\r | |
213 | target = ownerContext.target,\r | |
214 | props = ownerContext.props,\r | |
215 | isContainer,\r | |
216 | ret = {\r | |
217 | gotWidth: false,\r | |
218 | gotHeight: false,\r | |
219 | isContainer: (isContainer = !ownerContext.hasRawContent)\r | |
220 | },\r | |
221 | hv = dimensions || 3,\r | |
222 | zeroWidth, zeroHeight,\r | |
223 | needed = 0,\r | |
224 | got = 0,\r | |
225 | ready, size, temp, key, cache;\r | |
226 | \r | |
227 | // Note: this method is called *a lot*, so we have to be careful not to waste any\r | |
228 | // time or make useless calls or, especially, read the DOM when we can avoid it.\r | |
229 | \r | |
230 | //---------------------------------------------------------------------\r | |
231 | // Width\r | |
232 | \r | |
233 | if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) {\r | |
234 | ++needed;\r | |
235 | zeroWidth = !(hv & 1); // jshint ignore:line\r | |
236 | \r | |
237 | if (isContainer) {\r | |
238 | // as a componentLayout for a container, we rely on the container layout to\r | |
239 | // produce contentWidth...\r | |
240 | if (zeroWidth) {\r | |
241 | ret.contentWidth = 0;\r | |
242 | ret.gotWidth = true;\r | |
243 | ++got;\r | |
244 | } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) {\r | |
245 | ret.gotWidth = true;\r | |
246 | ++got;\r | |
247 | }\r | |
248 | } else {\r | |
249 | size = props.contentWidth;\r | |
250 | \r | |
251 | if (typeof size === 'number') { // if (already determined)\r | |
252 | ret.contentWidth = size;\r | |
253 | ret.gotWidth = true;\r | |
254 | ++got;\r | |
255 | } else {\r | |
256 | if (zeroWidth) {\r | |
257 | ready = true;\r | |
258 | } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {\r | |
259 | ready = false;\r | |
260 | } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {\r | |
261 | // if we have no boxParent, we are ready, but a shrinkWrap boxParent\r | |
262 | // artificially provides width early in the measurement process so\r | |
263 | // we are ready to go in that case as well...\r | |
264 | ready = true;\r | |
265 | } else {\r | |
266 | // lastly, we have a boxParent that will be given a width, so we\r | |
267 | // can wait for that width to be set in order to properly measure\r | |
268 | // whatever is inside...\r | |
269 | ready = boxParent.hasDomProp('width');\r | |
270 | }\r | |
271 | \r | |
272 | if (ready) {\r | |
273 | if (zeroWidth) {\r | |
274 | temp = 0;\r | |
275 | } else if (containerLayout && containerLayout.measureContentWidth) {\r | |
276 | // Allow the container layout to do the measurement since it\r | |
277 | // may have a better idea of how to do it even with no items:\r | |
278 | temp = containerLayout.measureContentWidth(ownerContext);\r | |
279 | } else {\r | |
280 | if (target.cacheWidth) {\r | |
281 | // if all instances of a given xtype/UI are the same size, only read the DOM once\r | |
282 | // to measure the first instance. Thereafter, retrieve the width from the cache.\r | |
283 | key = target.xtype + '-' + target.ui;\r | |
284 | cache = me.widthCache;\r | |
285 | temp = cache[key] || (cache[key] = me.measureContentWidth(ownerContext));\r | |
286 | } else {\r | |
287 | temp = me.measureContentWidth(ownerContext);\r | |
288 | }\r | |
289 | }\r | |
290 | \r | |
291 | if (!isNaN(ret.contentWidth = temp)) {\r | |
292 | ownerContext.setContentWidth(temp, true);\r | |
293 | ret.gotWidth = true;\r | |
294 | ++got;\r | |
295 | }\r | |
296 | }\r | |
297 | }\r | |
298 | }\r | |
299 | } else if (widthModel.natural && ownerContext.consumersWidth) {\r | |
300 | ++needed;\r | |
301 | size = props.width;\r | |
302 | // zeroWidth does not apply\r | |
303 | \r | |
304 | if (typeof size === 'number') { // if (already determined)\r | |
305 | ret.width = size;\r | |
306 | ret.gotWidth = true;\r | |
307 | ++got;\r | |
308 | } else {\r | |
309 | if (isBoxParent || !boxParent) {\r | |
310 | ready = true;\r | |
311 | } else {\r | |
312 | // lastly, we have a boxParent that will be given a width, so we\r | |
313 | // can wait for that width to be set in order to properly measure\r | |
314 | // whatever is inside...\r | |
315 | ready = boxParent.hasDomProp('width');\r | |
316 | }\r | |
317 | \r | |
318 | if (ready) {\r | |
319 | if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) {\r | |
320 | ownerContext.setWidth(ret.width, false);\r | |
321 | ret.gotWidth = true;\r | |
322 | ++got;\r | |
323 | }\r | |
324 | }\r | |
325 | }\r | |
326 | }\r | |
327 | \r | |
328 | //---------------------------------------------------------------------\r | |
329 | // Height\r | |
330 | \r | |
331 | if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) {\r | |
332 | ++needed;\r | |
333 | zeroHeight = !(hv & 2); // jshint ignore:line\r | |
334 | \r | |
335 | if (isContainer) {\r | |
336 | // don't ask unless we need to know...\r | |
337 | if (zeroHeight) {\r | |
338 | ret.contentHeight = 0;\r | |
339 | ret.gotHeight = true;\r | |
340 | ++got;\r | |
341 | } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) {\r | |
342 | ret.gotHeight = true;\r | |
343 | ++got;\r | |
344 | }\r | |
345 | } else {\r | |
346 | size = props.contentHeight;\r | |
347 | \r | |
348 | if (typeof size === 'number') { // if (already determined)\r | |
349 | ret.contentHeight = size;\r | |
350 | ret.gotHeight = true;\r | |
351 | ++got;\r | |
352 | } else {\r | |
353 | if (zeroHeight) {\r | |
354 | ready = true;\r | |
355 | } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {\r | |
356 | ready = false;\r | |
357 | } else if (owner.noWrap) {\r | |
358 | ready = true;\r | |
359 | } else if (!widthModel.shrinkWrap) {\r | |
360 | // fixed width, so we need the width to determine the height...\r | |
361 | ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');// && (!ownerContext.bodyContext || ownerContext.bodyContext.hasDomProp('width'));\r | |
362 | } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {\r | |
363 | // if we have no boxParent, we are ready, but an autoWidth boxParent\r | |
364 | // artificially provides width early in the measurement process so\r | |
365 | // we are ready to go in that case as well...\r | |
366 | ready = true;\r | |
367 | } else {\r | |
368 | // lastly, we have a boxParent that will be given a width, so we\r | |
369 | // can wait for that width to be set in order to properly measure\r | |
370 | // whatever is inside...\r | |
371 | ready = boxParent.hasDomProp('width');\r | |
372 | }\r | |
373 | \r | |
374 | if (ready) {\r | |
375 | if (zeroHeight) {\r | |
376 | temp = 0;\r | |
377 | } else if (containerLayout && containerLayout.measureContentHeight) {\r | |
378 | // Allow the container layout to do the measurement since it\r | |
379 | // may have a better idea of how to do it even with no items:\r | |
380 | temp = containerLayout.measureContentHeight(ownerContext);\r | |
381 | } else {\r | |
382 | if (target.cacheHeight) {\r | |
383 | // if all instances of a given xtype/UI are the same size, only read the DOM once\r | |
384 | // to measure the first instance. Thereafter, retrieve the height from the cache.\r | |
385 | key = target.xtype + '-' + target.ui;\r | |
386 | cache = me.heightCache;\r | |
387 | temp = cache[key] || (cache[key] = me.measureContentHeight(ownerContext));\r | |
388 | } else {\r | |
389 | temp = me.measureContentHeight(ownerContext);\r | |
390 | }\r | |
391 | }\r | |
392 | \r | |
393 | if (!isNaN(ret.contentHeight = temp)) {\r | |
394 | ownerContext.setContentHeight(temp, true);\r | |
395 | ret.gotHeight = true;\r | |
396 | ++got;\r | |
397 | }\r | |
398 | }\r | |
399 | }\r | |
400 | }\r | |
401 | } else if (heightModel.natural && ownerContext.consumersHeight) {\r | |
402 | ++needed;\r | |
403 | size = props.height;\r | |
404 | // zeroHeight does not apply\r | |
405 | \r | |
406 | if (typeof size === 'number') { // if (already determined)\r | |
407 | ret.height = size;\r | |
408 | ret.gotHeight = true;\r | |
409 | ++got;\r | |
410 | } else {\r | |
411 | if (isBoxParent || !boxParent) {\r | |
412 | ready = true;\r | |
413 | } else {\r | |
414 | // lastly, we have a boxParent that will be given a width, so we\r | |
415 | // can wait for that width to be set in order to properly measure\r | |
416 | // whatever is inside...\r | |
417 | ready = boxParent.hasDomProp('width');\r | |
418 | }\r | |
419 | \r | |
420 | if (ready) {\r | |
421 | if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) {\r | |
422 | ownerContext.setHeight(ret.height, false);\r | |
423 | ret.gotHeight = true;\r | |
424 | ++got;\r | |
425 | }\r | |
426 | }\r | |
427 | }\r | |
428 | }\r | |
429 | \r | |
430 | if (boxParent) {\r | |
431 | ownerContext.onBoxMeasured();\r | |
432 | }\r | |
433 | \r | |
434 | ret.gotAll = got === needed;\r | |
435 | // see if we can avoid calling this method by storing something on ownerContext.\r | |
436 | return ret;\r | |
437 | },\r | |
438 | \r | |
439 | measureContentWidth: function (ownerContext) {\r | |
440 | // contentWidth includes padding, but not border, framing or margins\r | |
441 | return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width;\r | |
442 | },\r | |
443 | \r | |
444 | measureContentHeight: function (ownerContext) {\r | |
445 | // contentHeight includes padding, but not border, framing or margins\r | |
446 | return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height;\r | |
447 | },\r | |
448 | \r | |
449 | measureOwnerHeight: function (ownerContext) {\r | |
450 | return ownerContext.el.getHeight();\r | |
451 | },\r | |
452 | \r | |
453 | measureOwnerWidth: function (ownerContext) {\r | |
454 | return ownerContext.el.getWidth();\r | |
455 | }\r | |
456 | });\r |