Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / draw / Component.js
1 /**
2  * @class Ext.draw.Component
3  * @extends Ext.Component
4  *
5  * The Draw Component is a surface in which sprites can be rendered. The Draw Component
6  * manages and holds a `Surface` instance: an interface that has
7  * an SVG or VML implementation depending on the browser capabilities and where
8  * Sprites can be appended.
9  * {@img Ext.draw.Component/Ext.draw.Component.png Ext.draw.Component component}
10  * One way to create a draw component is:
11  * 
12  *     var drawComponent = Ext.create('Ext.draw.Component', {
13  *         viewBox: false,
14  *         items: [{
15  *             type: 'circle',
16  *             fill: '#79BB3F',
17  *             radius: 100,
18  *             x: 100,
19  *             y: 100
20  *         }]
21  *     });
22  *   
23  *     Ext.create('Ext.Window', {
24  *         width: 215,
25  *         height: 235,
26  *         layout: 'fit',
27  *         items: [drawComponent]
28  *     }).show();
29  * 
30  * In this case we created a draw component and added a sprite to it.
31  * The *type* of the sprite is *circle* so if you run this code you'll see a yellow-ish
32  * circle in a Window. When setting `viewBox` to `false` we are responsible for setting the object's position and
33  * dimensions accordingly. 
34  * 
35  * You can also add sprites by using the surface's add method:
36  *    
37  *     drawComponent.surface.add({
38  *         type: 'circle',
39  *         fill: '#79BB3F',
40  *         radius: 100,
41  *         x: 100,
42  *         y: 100
43  *     });
44  *  
45  * For more information on Sprites, the core elements added to a draw component's surface,
46  * refer to the Ext.draw.Sprite documentation.
47  */
48 Ext.define('Ext.draw.Component', {
49
50     /* Begin Definitions */
51
52     alias: 'widget.draw',
53
54     extend: 'Ext.Component',
55
56     requires: [
57         'Ext.draw.Surface',
58         'Ext.layout.component.Draw'
59     ],
60
61     /* End Definitions */
62
63     /**
64      * @cfg {Array} enginePriority
65      * Defines the priority order for which Surface implementation to use. The first
66      * one supported by the current environment will be used.
67      */
68     enginePriority: ['Svg', 'Vml'],
69
70     baseCls: Ext.baseCSSPrefix + 'surface',
71
72     componentLayout: 'draw',
73
74     /**
75      * @cfg {Boolean} viewBox
76      * Turn on view box support which will scale and position items in the draw component to fit to the component while
77      * maintaining aspect ratio. Note that this scaling can override other sizing settings on yor items. Defaults to true.
78      */
79     viewBox: true,
80
81     /**
82      * @cfg {Boolean} autoSize
83      * Turn on autoSize support which will set the bounding div's size to the natural size of the contents. Defaults to false.
84      */
85     autoSize: false,
86     
87     /**
88      * @cfg {Array} gradients (optional) Define a set of gradients that can be used as `fill` property in sprites.
89      * The gradients array is an array of objects with the following properties:
90      *
91      * <ul>
92      * <li><strong>id</strong> - string - The unique name of the gradient.</li>
93      * <li><strong>angle</strong> - number, optional - The angle of the gradient in degrees.</li>
94      * <li><strong>stops</strong> - object - An object with numbers as keys (from 0 to 100) and style objects
95      * as values</li>
96      * </ul>
97      * 
98      
99      For example:
100      
101      <pre><code>
102         gradients: [{
103             id: 'gradientId',
104             angle: 45,
105             stops: {
106                 0: {
107                     color: '#555'
108                 },
109                 100: {
110                     color: '#ddd'
111                 }
112             }
113         },  {
114             id: 'gradientId2',
115             angle: 0,
116             stops: {
117                 0: {
118                     color: '#590'
119                 },
120                 20: {
121                     color: '#599'
122                 },
123                 100: {
124                     color: '#ddd'
125                 }
126             }
127         }]
128      </code></pre>
129      
130      Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
131      
132      <pre><code>
133         sprite.setAttributes({
134             fill: 'url(#gradientId)'
135         }, true);
136      </code></pre>
137      
138      */
139
140     initComponent: function() {
141         this.callParent(arguments);
142
143         this.addEvents(
144             'mousedown',
145             'mouseup',
146             'mousemove',
147             'mouseenter',
148             'mouseleave',
149             'click'
150         );
151     },
152
153     /**
154      * @private
155      *
156      * Create the Surface on initial render
157      */
158     onRender: function() {
159         var me = this,
160             viewBox = me.viewBox,
161             autoSize = me.autoSize,
162             bbox, items, width, height, x, y;
163         me.callParent(arguments);
164
165         me.createSurface();
166
167         items = me.surface.items;
168
169         if (viewBox || autoSize) {
170             bbox = items.getBBox();
171             width = bbox.width;
172             height = bbox.height;
173             x = bbox.x;
174             y = bbox.y;
175             if (me.viewBox) {
176                 me.surface.setViewBox(x, y, width, height);
177             }
178             else {
179                 // AutoSized
180                 me.autoSizeSurface();
181             }
182         }
183     },
184
185     //@private
186     autoSizeSurface: function() {
187         var me = this,
188             items = me.surface.items,
189             bbox = items.getBBox(),
190             width = bbox.width,
191             height = bbox.height;
192         items.setAttributes({
193             translate: {
194                 x: -bbox.x,
195                 //Opera has a slight offset in the y axis.
196                 y: -bbox.y + (+Ext.isOpera)
197             }
198         }, true);
199         if (me.rendered) {
200             me.setSize(width, height);
201         }
202         else {
203             me.surface.setSize(width, height);
204         }
205         me.el.setSize(width, height);
206     },
207
208     /**
209      * Create the Surface instance. Resolves the correct Surface implementation to
210      * instantiate based on the 'enginePriority' config. Once the Surface instance is
211      * created you can use the handle to that instance to add sprites. For example:
212      *
213      <pre><code>
214         drawComponent.surface.add(sprite);
215      </code></pre>
216      */
217     createSurface: function() {
218         var surface = Ext.draw.Surface.create(Ext.apply({}, {
219                 width: this.width,
220                 height: this.height,
221                 renderTo: this.el
222             }, this.initialConfig));
223         this.surface = surface;
224
225         function refire(eventName) {
226             return function(e) {
227                 this.fireEvent(eventName, e);
228             };
229         }
230
231         surface.on({
232             scope: this,
233             mouseup: refire('mouseup'),
234             mousedown: refire('mousedown'),
235             mousemove: refire('mousemove'),
236             mouseenter: refire('mouseenter'),
237             mouseleave: refire('mouseleave'),
238             click: refire('click')
239         });
240     },
241
242
243     /**
244      * @private
245      * 
246      * Clean up the Surface instance on component destruction
247      */
248     onDestroy: function() {
249         var surface = this.surface;
250         if (surface) {
251             surface.destroy();
252         }
253         this.callParent(arguments);
254     }
255
256 });