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