]> git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/layout/container/Fit.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / layout / container / Fit.js
1 /**
2 * This is a base class for layouts that contain a single item that automatically expands to fill the layout's
3 * container. This class is intended to be extended or created via the layout:'fit'
4 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.
5 *
6 * Fit layout does not have any direct config options (other than inherited ones). To fit a panel to a container using
7 * Fit layout, simply set `layout: 'fit'` on the container and add a single panel to it.
8 *
9 * @example
10 * Ext.create('Ext.panel.Panel', {
11 * title: 'Fit Layout',
12 * width: 300,
13 * height: 150,
14 * layout:'fit',
15 * items: {
16 * title: 'Inner Panel',
17 * html: 'This is the inner panel content',
18 * bodyPadding: 20,
19 * border: false
20 * },
21 * renderTo: Ext.getBody()
22 * });
23 *
24 * If the container has multiple items, all of the items will all be equally sized. This is usually not
25 * desired, so to avoid this, place only a **single** item in the container. This sizing of all items
26 * can be used to provide a background {@link Ext.Img image} that is "behind" another item
27 * such as a {@link Ext.view.View dataview} if you also absolutely position the items.
28 */
29 Ext.define('Ext.layout.container.Fit', {
30
31 /* Begin Definitions */
32 extend: 'Ext.layout.container.Container',
33 alternateClassName: 'Ext.layout.FitLayout',
34
35 alias: 'layout.fit',
36
37 /* End Definitions */
38
39 /**
40 * @inheritdoc Ext.layout.container.Container#cfg-itemCls
41 */
42 itemCls: Ext.baseCSSPrefix + 'fit-item',
43 type: 'fit',
44
45 manageMargins: true,
46
47 sizePolicies: {
48 0: { readsWidth: 1, readsHeight: 1, setsWidth: 0, setsHeight: 0 },
49 1: { readsWidth: 0, readsHeight: 1, setsWidth: 1, setsHeight: 0 },
50 2: { readsWidth: 1, readsHeight: 0, setsWidth: 0, setsHeight: 1 },
51 3: { readsWidth: 0, readsHeight: 0, setsWidth: 1, setsHeight: 1 }
52 },
53
54 getItemSizePolicy: function (item, ownerSizeModel) {
55 // this layout's sizePolicy is derived from its owner's sizeModel:
56 var sizeModel = ownerSizeModel || this.owner.getSizeModel(),
57 mode = (sizeModel.width.shrinkWrap ? 0 : 1) | // jshint ignore:line
58 (sizeModel.height.shrinkWrap ? 0 : 2);
59
60 return this.sizePolicies[mode];
61 },
62
63 beginLayoutCycle: function (ownerContext, firstCycle) {
64 var me = this,
65 // determine these before the lastSizeModels get updated:
66 resetHeight = me.lastHeightModel && me.lastHeightModel.calculated,
67 resetWidth = me.lastWidthModel && me.lastWidthModel.calculated,
68 resetSizes = resetWidth || resetHeight,
69 maxChildMinHeight = 0, maxChildMinWidth = 0,
70 c, childItems, i, item, length, margins, minHeight, minWidth, style, undef;
71
72 me.callParent(arguments);
73
74 // Clear any dimensions which we set before calculation, in case the current
75 // settings affect the available size. This particularly effects self-sizing
76 // containers such as fields, in which the target element is naturally sized,
77 // and should not be stretched by a sized child item.
78 if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() !== 'TD') {
79 resetSizes = resetWidth = resetHeight = false;
80 }
81
82 childItems = ownerContext.childItems;
83 length = childItems.length;
84
85 for (i = 0; i < length; ++i) {
86 item = childItems[i];
87
88 // On the firstCycle, we determine the max of the minWidth/Height of the items
89 // since these can cause the container to grow scrollbars despite our attempts
90 // to fit the child to the container.
91 if (firstCycle) {
92 c = item.target;
93 minHeight = c.minHeight;
94 minWidth = c.minWidth;
95
96 if (minWidth || minHeight) {
97 margins = item.marginInfo || item.getMarginInfo();
98 // if the child item has undefined minWidth/Height, these will become
99 // NaN by adding the margins...
100 minHeight += margins.height;
101 minWidth += margins.height;
102
103 // if the child item has undefined minWidth/Height, these comparisons
104 // will evaluate to false... that is, "0 < NaN" == false...
105 if (maxChildMinHeight < minHeight) {
106 maxChildMinHeight = minHeight;
107 }
108 if (maxChildMinWidth < minWidth) {
109 maxChildMinWidth = minWidth;
110 }
111 }
112 }
113
114 if (resetSizes) {
115 style = item.el.dom.style;
116
117 if (resetHeight) {
118 style.height = '';
119 }
120 if (resetWidth) {
121 style.width = '';
122 }
123 }
124 }
125
126 if (firstCycle) {
127 ownerContext.maxChildMinHeight = maxChildMinHeight;
128 ownerContext.maxChildMinWidth = maxChildMinWidth;
129 }
130
131 // Cache the overflowX/Y flags, but make them false in shrinkWrap mode (since we
132 // won't be triggering overflow in that case) and false if we have no minSize (so
133 // no child to trigger an overflow).
134 c = ownerContext.target;
135 ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap &&
136 ownerContext.maxChildMinWidth &&
137 c.scrollFlags.x) || undef;
138
139 ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap &&
140 ownerContext.maxChildMinHeight &&
141 c.scrollFlags.y) || undef;
142 },
143
144 calculate: function (ownerContext) {
145 var me = this,
146 childItems = ownerContext.childItems,
147 length = childItems.length,
148 containerSize = me.getContainerSize(ownerContext),
149 info = {
150 length: length,
151 ownerContext: ownerContext,
152 targetSize: containerSize
153 },
154 shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
155 shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
156 overflowX = ownerContext.overflowX,
157 overflowY = ownerContext.overflowY,
158 scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight;
159
160 ownerContext.state.info = info;
161 if (overflowX || overflowY) {
162 // If we have children that have minHeight/Width, we may be forced to overflow
163 // and gain scrollbars. If so, we want to remove their space from the other
164 // axis so that we fit things inside the scrollbars rather than under them.
165 scrollbars = me.getScrollbarsNeeded(
166 overflowX && containerSize.width, overflowY && containerSize.height,
167 ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight);
168
169 if (scrollbars) {
170 scrollbarSize = Ext.getScrollbarSize();
171 if (scrollbars & 1) { // jshint ignore:line
172 // if we need the hscrollbar, remove its height
173 containerSize.height -= scrollbarSize.height;
174 }
175 if (scrollbars & 2) { // jshint ignore:line
176 // if we need the vscrollbar, remove its width
177 containerSize.width -= scrollbarSize.width;
178 }
179 }
180 }
181
182 // If length === 0, it means we either have no child items, or the children are hidden
183 if (length > 0) {
184 // Size the child items to the container (if non-shrinkWrap):
185 for (i = 0; i < length; ++i) {
186 info.index = i;
187 me.fitItem(childItems[i], info);
188 }
189 } else {
190 info.contentWidth = info.contentHeight = 0;
191 }
192
193 if (shrinkWrapHeight || shrinkWrapWidth) {
194 padding = ownerContext.targetContext.getPaddingInfo();
195
196 if (shrinkWrapWidth) {
197 if (overflowY && !containerSize.gotHeight) {
198 // if we might overflow vertically and don't have the container height,
199 // we don't know if we will need a vscrollbar or not, so we must wait
200 // for that height so that we can determine the contentWidth...
201 me.done = false;
202 } else {
203 contentWidth = info.contentWidth + padding.width;
204 // the scrollbar flag (if set) will indicate that an overflow exists on
205 // the horz(1) or vert(2) axis... if not set, then there could never be
206 // an overflow...
207 if (scrollbars & 2) { // jshint ignore:line
208 // if we need the vscrollbar, add its width
209 contentWidth += scrollbarSize.width;
210 }
211 if (!ownerContext.setContentWidth(contentWidth)) {
212 me.done = false;
213 }
214 }
215 }
216
217 if (shrinkWrapHeight) {
218 if (overflowX && !containerSize.gotWidth) {
219 // if we might overflow horizontally and don't have the container width,
220 // we don't know if we will need a hscrollbar or not, so we must wait
221 // for that width so that we can determine the contentHeight...
222 me.done = false;
223 } else {
224 contentHeight = info.contentHeight + padding.height;
225 // the scrollbar flag (if set) will indicate that an overflow exists on
226 // the horz(1) or vert(2) axis... if not set, then there could never be
227 // an overflow...
228 if (scrollbars & 1) { // jshint ignore:line
229 // if we need the hscrollbar, add its height
230 contentHeight += scrollbarSize.height;
231 }
232 if (!ownerContext.setContentHeight(contentHeight)) {
233 me.done = false;
234 }
235 }
236 }
237 }
238 },
239
240 fitItem: function (itemContext, info) {
241 var me = this;
242
243 if (itemContext.invalid) {
244 me.done = false;
245 return;
246 }
247
248 info.margins = itemContext.getMarginInfo();
249 info.needed = info.got = 0;
250
251 me.fitItemWidth(itemContext, info);
252 me.fitItemHeight(itemContext, info);
253
254 // If not all required dimensions have been satisfied, we're not done.
255 if (info.got !== info.needed) {
256 me.done = false;
257 }
258 },
259
260 fitItemWidth: function (itemContext, info) {
261 var contentWidth, width;
262 // Attempt to set only dimensions that are being controlled, not shrinkWrap dimensions
263 if (info.ownerContext.widthModel.shrinkWrap) {
264 // contentWidth must include the margins to be consistent with setItemWidth
265 width = itemContext.getProp('width') + info.margins.width;
266 // because we add margins, width will be NaN or a number (not undefined)
267
268 contentWidth = info.contentWidth;
269 if (contentWidth === undefined) {
270 info.contentWidth = width;
271 } else {
272 info.contentWidth = Math.max(contentWidth, width);
273 }
274 } else if (itemContext.widthModel.calculated) {
275 ++info.needed;
276 if (info.targetSize.gotWidth) {
277 ++info.got;
278 this.setItemWidth(itemContext, info);
279 } else {
280 // Too early to position
281 return;
282 }
283 }
284
285 this.positionItemX(itemContext, info);
286 },
287
288 fitItemHeight: function (itemContext, info) {
289 var contentHeight, height;
290 if (info.ownerContext.heightModel.shrinkWrap) {
291 // contentHeight must include the margins to be consistent with setItemHeight
292 height = itemContext.getProp('height') + info.margins.height;
293 // because we add margins, height will be NaN or a number (not undefined)
294
295 contentHeight = info.contentHeight;
296 if (contentHeight === undefined) {
297 info.contentHeight = height;
298 } else {
299 info.contentHeight = Math.max(contentHeight, height);
300 }
301 } else if (itemContext.heightModel.calculated) {
302 ++info.needed;
303 if (info.targetSize.gotHeight) {
304 ++info.got;
305 this.setItemHeight(itemContext, info);
306 } else {
307 // Too early to position
308 return;
309 }
310 }
311
312 this.positionItemY(itemContext, info);
313 },
314
315 positionItemX: function (itemContext, info) {
316 var margins = info.margins;
317
318 // Adjust position to account for configured margins or if we have multiple items
319 // (all items should overlap):
320 if (info.index || margins.left) {
321 itemContext.setProp('x', margins.left);
322 }
323
324 if (margins.width && info.ownerContext.widthModel.shrinkWrap) {
325 // Need the margins for shrink-wrapping but old IE sometimes collapses the left margin into the padding
326 itemContext.setProp('margin-right', margins.width);
327 }
328 },
329
330 positionItemY: function (itemContext, info) {
331 var margins = info.margins;
332
333 if (info.index || margins.top) {
334 itemContext.setProp('y', margins.top);
335 }
336
337 if (margins.height && info.ownerContext.heightModel.shrinkWrap) {
338 // Need the margins for shrink-wrapping but old IE sometimes collapses the top margin into the padding
339 itemContext.setProp('margin-bottom', margins.height);
340 }
341 },
342
343 setItemHeight: function (itemContext, info) {
344 itemContext.setHeight(info.targetSize.height - info.margins.height);
345 },
346
347 setItemWidth: function (itemContext, info) {
348 itemContext.setWidth(info.targetSize.width - info.margins.width);
349 }
350 });