Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / src / layout / component / Component.js
1 /**
2  * @class Ext.layout.component.Component
3  * @extends Ext.layout.Layout
4  * @private
5  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Component#componentLayout layout}</b></tt>
6  * configuration property.  See <tt><b>{@link Ext.Component#componentLayout}</b></tt> for additional details.</p>
7  */
8
9 Ext.define('Ext.layout.component.Component', {
10
11     /* Begin Definitions */
12
13     extend: 'Ext.layout.Layout',
14
15     /* End Definitions */
16
17     type: 'component',
18
19     monitorChildren: true,
20
21     initLayout : function() {
22         var me = this,
23             owner = me.owner,
24             ownerEl = owner.el;
25
26         if (!me.initialized) {
27             if (owner.frameSize) {
28                 me.frameSize = owner.frameSize;
29             }
30             else {
31                 owner.frameSize = me.frameSize = {
32                     top: 0,
33                     left: 0,
34                     bottom: 0,
35                     right: 0
36                 }; 
37             }
38         }
39         me.callParent(arguments);
40     },
41
42     beforeLayout : function(width, height, isSetSize, layoutOwner) {
43         this.callParent(arguments);
44
45         var me = this,
46             owner = me.owner,
47             ownerCt = owner.ownerCt,
48             layout = owner.layout,
49             isVisible = owner.isVisible(true),
50             ownerElChild = owner.el.child,
51             layoutCollection;
52
53         /*
54          * Do not layout calculatedSized components for fixedLayouts unless the ownerCt == layoutOwner
55          * fixedLayouts means layouts which are never auto/auto in the sizing that comes from their ownerCt.
56          * Currently 3 layouts MAY be auto/auto (Auto, Border, and Box)
57          * The reason for not allowing component layouts is to stop component layouts from things such as Updater and
58          * form Validation.
59          */
60         if (!isSetSize && !(Ext.isNumber(width) && Ext.isNumber(height)) && ownerCt && ownerCt.layout && ownerCt.layout.fixedLayout && ownerCt != layoutOwner) {
61             me.doContainerLayout();
62             return false;
63         }
64
65         // If an ownerCt is hidden, add my reference onto the layoutOnShow stack.  Set the needsLayout flag.
66         // If the owner itself is a directly hidden floater, set the needsLayout object on that for when it is shown.
67         if (!isVisible && (owner.hiddenAncestor || owner.floating)) {
68             if (owner.hiddenAncestor) {
69                 layoutCollection = owner.hiddenAncestor.layoutOnShow;
70                 layoutCollection.remove(owner);
71                 layoutCollection.add(owner);
72             }
73             owner.needsLayout = {
74                 width: width,
75                 height: height,
76                 isSetSize: false
77             };
78         }
79
80         if (isVisible && this.needsLayout(width, height)) {
81             me.rawWidth = width;
82             me.rawHeight = height;
83             return owner.beforeComponentLayout(width, height, isSetSize, layoutOwner);
84         }
85         else {
86             return false;
87         }
88     },
89
90     /**
91     * Check if the new size is different from the current size and only
92     * trigger a layout if it is necessary.
93     * @param {Mixed} width The new width to set.
94     * @param {Mixed} height The new height to set.
95     */
96     needsLayout : function(width, height) {
97         this.lastComponentSize = this.lastComponentSize || {
98             width: -Infinity,
99             height: -Infinity
100         };
101         return (this.childrenChanged || this.lastComponentSize.width !== width || this.lastComponentSize.height !== height);
102     },
103
104     /**
105     * Set the size of any element supporting undefined, null, and values.
106     * @param {Mixed} width The new width to set.
107     * @param {Mixed} height The new height to set.
108     */
109     setElementSize: function(el, width, height) {
110         if (width !== undefined && height !== undefined) {
111             el.setSize(width, height);
112         }
113         else if (height !== undefined) {
114             el.setHeight(height);
115         }
116         else if (width !== undefined) {
117             el.setWidth(width);
118         }
119     },
120
121     /**
122      * Returns the owner component's resize element.
123      * @return {Ext.core.Element}
124      */
125      getTarget : function() {
126          return this.owner.el;
127      },
128
129     /**
130      * <p>Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.</p>
131      * May be overridden in Component layout managers which implement an inner element.
132      * @return {Ext.core.Element}
133      */
134     getRenderTarget : function() {
135         return this.owner.el;
136     },
137
138     /**
139     * Set the size of the target element.
140     * @param {Mixed} width The new width to set.
141     * @param {Mixed} height The new height to set.
142     */
143     setTargetSize : function(width, height) {
144         var me = this;
145         me.setElementSize(me.owner.el, width, height);
146
147         if (me.owner.frameBody) {
148             var targetInfo = me.getTargetInfo(),
149                 padding = targetInfo.padding,
150                 border = targetInfo.border,
151                 frameSize = me.frameSize;
152
153             me.setElementSize(me.owner.frameBody,
154                 Ext.isNumber(width) ? (width - frameSize.left - frameSize.right - padding.left - padding.right - border.left - border.right) : width,
155                 Ext.isNumber(height) ? (height - frameSize.top - frameSize.bottom - padding.top - padding.bottom - border.top - border.bottom) : height
156             );
157         }
158
159         me.autoSized = {
160             width: !Ext.isNumber(width),
161             height: !Ext.isNumber(height)
162         };
163
164         me.lastComponentSize = {
165             width: width,
166             height: height
167         };
168     },
169
170     getTargetInfo : function() {
171         if (!this.targetInfo) {
172             var target = this.getTarget(),
173                 body = this.owner.getTargetEl();
174
175             this.targetInfo = {
176                 padding: {
177                     top: target.getPadding('t'),
178                     right: target.getPadding('r'),
179                     bottom: target.getPadding('b'),
180                     left: target.getPadding('l')
181                 },
182                 border: {
183                     top: target.getBorderWidth('t'),
184                     right: target.getBorderWidth('r'),
185                     bottom: target.getBorderWidth('b'),
186                     left: target.getBorderWidth('l')
187                 },
188                 bodyMargin: {
189                     top: body.getMargin('t'),
190                     right: body.getMargin('r'),
191                     bottom: body.getMargin('b'),
192                     left: body.getMargin('l')
193                 } 
194             };
195         }
196         return this.targetInfo;
197     },
198
199     // Start laying out UP the ownerCt's layout when flagged to do so.
200     doOwnerCtLayouts: function() {
201         var owner = this.owner,
202             ownerCt = owner.ownerCt,
203             ownerCtComponentLayout, ownerCtContainerLayout;
204
205         if (!ownerCt) {
206             return;
207         }
208
209         ownerCtComponentLayout = ownerCt.componentLayout;
210         ownerCtContainerLayout = ownerCt.layout;
211
212         if (!owner.floating && ownerCtComponentLayout && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
213             if (!ownerCt.suspendLayout && ownerCtContainerLayout && !ownerCtContainerLayout.layoutBusy) {
214                 // AutoContainer Layout and Dock with auto in some dimension
215                 if (ownerCtContainerLayout.bindToOwnerCtComponent === true) {
216                     ownerCt.doComponentLayout();
217                 }
218                 // Box Layouts
219                 else if (ownerCtContainerLayout.bindToOwnerCtContainer === true) {
220                     ownerCtContainerLayout.layout();
221                 }
222             }
223         }
224     },
225
226     doContainerLayout: function() {
227         var me = this,
228             owner = me.owner,
229             ownerCt = owner.ownerCt,
230             layout = owner.layout,
231             ownerCtComponentLayout;
232
233         // Run the container layout if it exists (layout for child items)
234         // **Unless automatic laying out is suspended, or the layout is currently running**
235         if (!owner.suspendLayout && layout && layout.isLayout && !layout.layoutBusy) {
236             layout.layout();
237         }
238
239         // Tell the ownerCt that it's child has changed and can be re-layed by ignoring the lastComponentSize cache.
240         if (ownerCt && ownerCt.componentLayout) {
241             ownerCtComponentLayout = ownerCt.componentLayout;
242             if (!owner.floating && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
243                 ownerCtComponentLayout.childrenChanged = true;
244             }
245         }
246     },
247
248     afterLayout : function(width, height, isSetSize, layoutOwner) {
249         this.doContainerLayout();
250         this.owner.afterComponentLayout(width, height, isSetSize, layoutOwner);
251     }
252 });