Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / layout / component / Component.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.layout.component.Component
17  * @extends Ext.layout.Layout
18  *
19  * This class is intended to be extended or created via the {@link Ext.Component#componentLayout layout}
20  * configuration property.  See {@link Ext.Component#componentLayout} for additional details.
21  *
22  * @private
23  */
24 Ext.define('Ext.layout.component.Component', {
25
26     /* Begin Definitions */
27
28     extend: 'Ext.layout.Layout',
29
30     /* End Definitions */
31
32     type: 'component',
33
34     monitorChildren: true,
35
36     initLayout : function() {
37         var me = this,
38             owner = me.owner,
39             ownerEl = owner.el;
40
41         if (!me.initialized) {
42             if (owner.frameSize) {
43                 me.frameSize = owner.frameSize;
44             }
45             else {
46                 owner.frameSize = me.frameSize = {
47                     top: 0,
48                     left: 0,
49                     bottom: 0,
50                     right: 0
51                 };
52             }
53         }
54         me.callParent(arguments);
55     },
56
57     beforeLayout : function(width, height, isSetSize, callingContainer) {
58         this.callParent(arguments);
59
60         var me = this,
61             owner = me.owner,
62             ownerCt = owner.ownerCt,
63             layout = owner.layout,
64             isVisible = owner.isVisible(true),
65             ownerElChild = owner.el.child,
66             layoutCollection;
67
68         // Cache the size we began with so we can see if there has been any effect.
69         me.previousComponentSize = me.lastComponentSize;
70
71         // Do not allow autoing of any dimensions which are fixed
72         if (!isSetSize
73             && ((!Ext.isNumber(width) && owner.isFixedWidth()) ||
74                 (!Ext.isNumber(height) && owner.isFixedHeight()))
75             // unless we are being told to do so by the ownerCt's layout
76             && callingContainer && callingContainer !== ownerCt) {
77             
78             me.doContainerLayout();
79             return false;
80         }
81
82         // If an ownerCt is hidden, add my reference onto the layoutOnShow stack.  Set the needsLayout flag.
83         // If the owner itself is a directly hidden floater, set the needsLayout object on that for when it is shown.
84         if (!isVisible && (owner.hiddenAncestor || owner.floating)) {
85             if (owner.hiddenAncestor) {
86                 layoutCollection = owner.hiddenAncestor.layoutOnShow;
87                 layoutCollection.remove(owner);
88                 layoutCollection.add(owner);
89             }
90             owner.needsLayout = {
91                 width: width,
92                 height: height,
93                 isSetSize: false
94             };
95         }
96
97         if (isVisible && this.needsLayout(width, height)) {
98             return owner.beforeComponentLayout(width, height, isSetSize, callingContainer);
99         }
100         else {
101             return false;
102         }
103     },
104
105     /**
106     * Check if the new size is different from the current size and only
107     * trigger a layout if it is necessary.
108     * @param {Number} width The new width to set.
109     * @param {Number} height The new height to set.
110     */
111     needsLayout : function(width, height) {
112         var me = this,
113             widthBeingChanged,
114             heightBeingChanged;
115             me.lastComponentSize = me.lastComponentSize || {
116                 width: -Infinity,
117                 height: -Infinity
118             };
119
120         // If autoWidthing, or an explicitly different width is passed, then the width is being changed.
121         widthBeingChanged  = !Ext.isDefined(width)  || me.lastComponentSize.width  !== width;
122
123         // If autoHeighting, or an explicitly different height is passed, then the height is being changed.
124         heightBeingChanged = !Ext.isDefined(height) || me.lastComponentSize.height !== height;
125
126
127         // isSizing flag added to prevent redundant layouts when going up the layout chain
128         return !me.isSizing && (me.childrenChanged || widthBeingChanged || heightBeingChanged);
129     },
130
131     /**
132     * Set the size of any element supporting undefined, null, and values.
133     * @param {Number} width The new width to set.
134     * @param {Number} height The new height to set.
135     */
136     setElementSize: function(el, width, height) {
137         if (width !== undefined && height !== undefined) {
138             el.setSize(width, height);
139         }
140         else if (height !== undefined) {
141             el.setHeight(height);
142         }
143         else if (width !== undefined) {
144             el.setWidth(width);
145         }
146     },
147
148     /**
149      * Returns the owner component's resize element.
150      * @return {Ext.Element}
151      */
152      getTarget : function() {
153          return this.owner.el;
154      },
155
156     /**
157      * <p>Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.</p>
158      * May be overridden in Component layout managers which implement an inner element.
159      * @return {Ext.Element}
160      */
161     getRenderTarget : function() {
162         return this.owner.el;
163     },
164
165     /**
166     * Set the size of the target element.
167     * @param {Number} width The new width to set.
168     * @param {Number} height The new height to set.
169     */
170     setTargetSize : function(width, height) {
171         var me = this;
172         me.setElementSize(me.owner.el, width, height);
173
174         if (me.owner.frameBody) {
175             var targetInfo = me.getTargetInfo(),
176                 padding = targetInfo.padding,
177                 border = targetInfo.border,
178                 frameSize = me.frameSize;
179
180             me.setElementSize(me.owner.frameBody,
181                 Ext.isNumber(width) ? (width - frameSize.left - frameSize.right - padding.left - padding.right - border.left - border.right) : width,
182                 Ext.isNumber(height) ? (height - frameSize.top - frameSize.bottom - padding.top - padding.bottom - border.top - border.bottom) : height
183             );
184         }
185
186         me.autoSized = {
187             width: !Ext.isNumber(width),
188             height: !Ext.isNumber(height)
189         };
190
191         me.lastComponentSize = {
192             width: width,
193             height: height
194         };
195     },
196
197     getTargetInfo : function() {
198         if (!this.targetInfo) {
199             var target = this.getTarget(),
200                 body = this.owner.getTargetEl();
201
202             this.targetInfo = {
203                 padding: {
204                     top: target.getPadding('t'),
205                     right: target.getPadding('r'),
206                     bottom: target.getPadding('b'),
207                     left: target.getPadding('l')
208                 },
209                 border: {
210                     top: target.getBorderWidth('t'),
211                     right: target.getBorderWidth('r'),
212                     bottom: target.getBorderWidth('b'),
213                     left: target.getBorderWidth('l')
214                 },
215                 bodyMargin: {
216                     top: body.getMargin('t'),
217                     right: body.getMargin('r'),
218                     bottom: body.getMargin('b'),
219                     left: body.getMargin('l')
220                 }
221             };
222         }
223         return this.targetInfo;
224     },
225
226     // Start laying out UP the ownerCt's layout when flagged to do so.
227     doOwnerCtLayouts: function() {
228         var owner = this.owner,
229             ownerCt = owner.ownerCt,
230             ownerCtComponentLayout, ownerCtContainerLayout,
231             curSize = this.lastComponentSize,
232             prevSize = this.previousComponentSize,
233             widthChange  = (prevSize && curSize && Ext.isNumber(curSize.width )) ? curSize.width  !== prevSize.width  : true,
234             heightChange = (prevSize && curSize && Ext.isNumber(curSize.height)) ? curSize.height !== prevSize.height : true;
235
236         // If size has not changed, do not inform upstream layouts
237         if (!ownerCt || (!widthChange && !heightChange)) {
238             return;
239         }
240
241         ownerCtComponentLayout = ownerCt.componentLayout;
242         ownerCtContainerLayout = ownerCt.layout;
243
244         if (!owner.floating && ownerCtComponentLayout && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
245             if (!ownerCt.suspendLayout && ownerCtContainerLayout && !ownerCtContainerLayout.layoutBusy) {
246
247                 // If the owning Container may be adjusted in any of the the dimension which have changed, perform its Component layout
248                 if (((widthChange && !ownerCt.isFixedWidth()) || (heightChange && !ownerCt.isFixedHeight()))) {
249                     // Set the isSizing flag so that the upstream Container layout (called after a Component layout) can omit this component from sizing operations
250                     this.isSizing = true;
251                     ownerCt.doComponentLayout();
252                     this.isSizing = false;
253                 }
254                 // Execute upstream Container layout
255                 else if (ownerCtContainerLayout.bindToOwnerCtContainer === true) {
256                     ownerCtContainerLayout.layout();
257                 }
258             }
259         }
260     },
261
262     doContainerLayout: function() {
263         var me = this,
264             owner = me.owner,
265             ownerCt = owner.ownerCt,
266             layout = owner.layout,
267             ownerCtComponentLayout;
268
269         // Run the container layout if it exists (layout for child items)
270         // **Unless automatic laying out is suspended, or the layout is currently running**
271         if (!owner.suspendLayout && layout && layout.isLayout && !layout.layoutBusy && !layout.isAutoDock) {
272             layout.layout();
273         }
274
275         // Tell the ownerCt that it's child has changed and can be re-layed by ignoring the lastComponentSize cache.
276         if (ownerCt && ownerCt.componentLayout) {
277             ownerCtComponentLayout = ownerCt.componentLayout;
278             if (!owner.floating && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
279                 ownerCtComponentLayout.childrenChanged = true;
280             }
281         }
282     },
283
284     afterLayout : function(width, height, isSetSize, layoutOwner) {
285         this.doContainerLayout();
286         this.owner.afterComponentLayout(width, height, isSetSize, layoutOwner);
287     }
288 });
289