Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / examples / desktop / js / TaskBar.js
1 /*!
2  * Ext JS Library 3.2.1
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.ux.TaskBar
9  * @extends Ext.util.Observable
10  */
11 Ext.ux.TaskBar = function(app){
12     this.app = app;
13     this.init();
14 }
15
16 Ext.extend(Ext.ux.TaskBar, Ext.util.Observable, {
17     init : function(){
18         this.startMenu = new Ext.ux.StartMenu(Ext.apply({
19             iconCls: 'user',
20             height: 300,
21             shadow: true,
22             title: 'Jack Slocum',
23             width: 300
24         }, this.app.startConfig));
25
26         this.startBtn = new Ext.Button({
27             text: 'Start',
28             id: 'ux-startbutton',
29             iconCls:'start',
30             menu: this.startMenu,
31             menuAlign: 'bl-tl',
32             renderTo: 'ux-taskbar-start',
33             clickEvent: 'mousedown',
34             template: new Ext.Template(
35                 '<table cellspacing="0" class="x-btn"><tbody class="{1}"><tr>',
36                 '<td class="ux-startbutton-left"><i>&#160;</i></td>',
37                 '<td class="ux-startbutton-center"><em class="{2} unselectable="on">',
38                     '<button class="x-btn-text" type="{0}" style="height:30px;"></button>',
39                 '</em></td>',
40                 '<td class="ux-startbutton-right"><i>&#160;</i></td>',
41                 '</tr></tbody></table>')
42         });
43
44         var width = this.startBtn.getEl().getWidth()+10;
45
46         var sbBox = new Ext.BoxComponent({
47             el: 'ux-taskbar-start',
48             id: 'TaskBarStart',
49             minWidth: width,
50             region:'west',
51             split: true,
52             width: width
53         });
54
55         this.tbPanel = new Ext.ux.TaskButtonsPanel({
56             el: 'ux-taskbuttons-panel',
57             id: 'TaskBarButtons',
58             region:'center'
59         });
60
61         var container = new Ext.ux.TaskBarContainer({
62             el: 'ux-taskbar',
63             layout: 'border',
64             items: [sbBox,this.tbPanel]
65         });
66
67         return this;
68     },
69
70     addTaskButton : function(win){
71         return this.tbPanel.addButton(win, 'ux-taskbuttons-panel');
72     },
73
74     removeTaskButton : function(btn){
75         this.tbPanel.removeButton(btn);
76     },
77
78     setActiveButton : function(btn){
79         this.tbPanel.setActiveButton(btn);
80     }
81 });
82
83
84
85 /**
86  * @class Ext.ux.TaskBarContainer
87  * @extends Ext.Container
88  */
89 Ext.ux.TaskBarContainer = Ext.extend(Ext.Container, {
90     initComponent : function() {
91         Ext.ux.TaskBarContainer.superclass.initComponent.call(this);
92
93         this.el = Ext.get(this.el) || Ext.getBody();
94         this.el.setHeight = Ext.emptyFn;
95         this.el.setWidth = Ext.emptyFn;
96         this.el.setSize = Ext.emptyFn;
97         this.el.setStyle({
98             overflow:'hidden',
99             margin:'0',
100             border:'0 none'
101         });
102         this.el.dom.scroll = 'no';
103         this.allowDomMove = false;
104         this.autoWidth = true;
105         this.autoHeight = true;
106         Ext.EventManager.onWindowResize(this.fireResize, this);
107         this.renderTo = this.el;
108     },
109
110     fireResize : function(w, h){
111         this.onResize(w, h, w, h);
112         this.fireEvent('resize', this, w, h, w, h);
113     }
114 });
115
116
117
118 /**
119  * @class Ext.ux.TaskButtonsPanel
120  * @extends Ext.BoxComponent
121  */
122 Ext.ux.TaskButtonsPanel = Ext.extend(Ext.BoxComponent, {
123     activeButton: null,
124     enableScroll: true,
125     scrollIncrement: 0,
126     scrollRepeatInterval: 400,
127     scrollDuration: .35,
128     animScroll: true,
129     resizeButtons: true,
130     buttonWidth: 168,
131     minButtonWidth: 118,
132     buttonMargin: 2,
133     buttonWidthSet: false,
134
135     initComponent : function() {
136         Ext.ux.TaskButtonsPanel.superclass.initComponent.call(this);
137         this.on('resize', this.delegateUpdates);
138         this.items = [];
139
140         this.stripWrap = Ext.get(this.el).createChild({
141             cls: 'ux-taskbuttons-strip-wrap',
142             cn: {
143                 tag:'ul', cls:'ux-taskbuttons-strip'
144             }
145         });
146         this.stripSpacer = Ext.get(this.el).createChild({
147             cls:'ux-taskbuttons-strip-spacer'
148         });
149         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
150
151         this.edge = this.strip.createChild({
152             tag:'li',
153             cls:'ux-taskbuttons-edge'
154         });
155         this.strip.createChild({
156             cls:'x-clear'
157         });
158     },
159
160     addButton : function(win){
161         var li = this.strip.createChild({tag:'li'}, this.edge); // insert before the edge
162         var btn = new Ext.ux.TaskBar.TaskButton(win, li);
163
164         this.items.push(btn);
165
166         if(!this.buttonWidthSet){
167             this.lastButtonWidth = btn.container.getWidth();
168         }
169
170         this.setActiveButton(btn);
171         return btn;
172     },
173
174     removeButton : function(btn){
175         var li = document.getElementById(btn.container.id);
176         btn.destroy();
177         li.parentNode.removeChild(li);
178
179         var s = [];
180         for(var i = 0, len = this.items.length; i < len; i++) {
181             if(this.items[i] != btn){
182                 s.push(this.items[i]);
183             }
184         }
185         this.items = s;
186
187         this.delegateUpdates();
188     },
189
190     setActiveButton : function(btn){
191         this.activeButton = btn;
192         this.delegateUpdates();
193     },
194
195     delegateUpdates : function(){
196         /*if(this.suspendUpdates){
197             return;
198         }*/
199         if(this.resizeButtons && this.rendered){
200             this.autoSize();
201         }
202         if(this.enableScroll && this.rendered){
203             this.autoScroll();
204         }
205     },
206
207     autoSize : function(){
208         var count = this.items.length;
209         var ow = this.el.dom.offsetWidth;
210         var aw = this.el.dom.clientWidth;
211
212         if(!this.resizeButtons || count < 1 || !aw){ // !aw for display:none
213             return;
214         }
215
216         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.buttonMargin, this.buttonWidth), this.minButtonWidth); // -4 for float errors in IE
217         var btns = this.stripWrap.dom.getElementsByTagName('button');
218
219         this.lastButtonWidth = Ext.get(btns[0].id).findParent('li').offsetWidth;
220
221         for(var i = 0, len = btns.length; i < len; i++) {
222             var btn = btns[i];
223
224             var tw = Ext.get(btns[i].id).findParent('li').offsetWidth;
225             var iw = btn.offsetWidth;
226
227             btn.style.width = (each - (tw-iw)) + 'px';
228         }
229     },
230
231     autoScroll : function(){
232         var count = this.items.length;
233         var ow = this.el.dom.offsetWidth;
234         var tw = this.el.dom.clientWidth;
235
236         var wrap = this.stripWrap;
237         var cw = wrap.dom.offsetWidth;
238         var pos = this.getScrollPos();
239         var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
240
241         if(!this.enableScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
242             return;
243         }
244
245         wrap.setWidth(tw); // moved to here because of problem in Safari
246
247         if(l <= tw){
248             wrap.dom.scrollLeft = 0;
249             //wrap.setWidth(tw); moved from here because of problem in Safari
250             if(this.scrolling){
251                 this.scrolling = false;
252                 this.el.removeClass('x-taskbuttons-scrolling');
253                 this.scrollLeft.hide();
254                 this.scrollRight.hide();
255             }
256         }else{
257             if(!this.scrolling){
258                 this.el.addClass('x-taskbuttons-scrolling');
259             }
260             tw -= wrap.getMargins('lr');
261             wrap.setWidth(tw > 20 ? tw : 20);
262             if(!this.scrolling){
263                 if(!this.scrollLeft){
264                     this.createScrollers();
265                 }else{
266                     this.scrollLeft.show();
267                     this.scrollRight.show();
268                 }
269             }
270             this.scrolling = true;
271             if(pos > (l-tw)){ // ensure it stays within bounds
272                 wrap.dom.scrollLeft = l-tw;
273             }else{ // otherwise, make sure the active button is still visible
274                 this.scrollToButton(this.activeButton, true); // true to animate
275             }
276             this.updateScrollButtons();
277         }
278     },
279
280     createScrollers : function(){
281         var h = this.el.dom.offsetHeight; //var h = this.stripWrap.dom.offsetHeight;
282
283         // left
284         var sl = this.el.insertFirst({
285             cls:'ux-taskbuttons-scroller-left'
286         });
287         sl.setHeight(h);
288         sl.addClassOnOver('ux-taskbuttons-scroller-left-over');
289         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
290             interval : this.scrollRepeatInterval,
291             handler: this.onScrollLeft,
292             scope: this
293         });
294         this.scrollLeft = sl;
295
296         // right
297         var sr = this.el.insertFirst({
298             cls:'ux-taskbuttons-scroller-right'
299         });
300         sr.setHeight(h);
301         sr.addClassOnOver('ux-taskbuttons-scroller-right-over');
302         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
303             interval : this.scrollRepeatInterval,
304             handler: this.onScrollRight,
305             scope: this
306         });
307         this.scrollRight = sr;
308     },
309
310     getScrollWidth : function(){
311         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
312     },
313
314     getScrollPos : function(){
315         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
316     },
317
318     getScrollArea : function(){
319         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
320     },
321
322     getScrollAnim : function(){
323         return {
324             duration: this.scrollDuration,
325             callback: this.updateScrollButtons,
326             scope: this
327         };
328     },
329
330     getScrollIncrement : function(){
331         return (this.scrollIncrement || this.lastButtonWidth+2);
332     },
333
334     /* getBtnEl : function(item){
335         return document.getElementById(item.id);
336     }, */
337
338     scrollToButton : function(item, animate){
339         item = item.el.dom.parentNode; // li
340         if(!item){ return; }
341         var el = item; //this.getBtnEl(item);
342         var pos = this.getScrollPos(), area = this.getScrollArea();
343         var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
344         var right = left + el.offsetWidth;
345         if(left < pos){
346             this.scrollTo(left, animate);
347         }else if(right > (pos + area)){
348             this.scrollTo(right - area, animate);
349         }
350     },
351
352     scrollTo : function(pos, animate){
353         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
354         if(!animate){
355             this.updateScrollButtons();
356         }
357     },
358
359     onScrollRight : function(){
360         var sw = this.getScrollWidth()-this.getScrollArea();
361         var pos = this.getScrollPos();
362         var s = Math.min(sw, pos + this.getScrollIncrement());
363         if(s != pos){
364             this.scrollTo(s, this.animScroll);
365         }
366     },
367
368     onScrollLeft : function(){
369         var pos = this.getScrollPos();
370         var s = Math.max(0, pos - this.getScrollIncrement());
371         if(s != pos){
372             this.scrollTo(s, this.animScroll);
373         }
374     },
375
376     updateScrollButtons : function(){
377         var pos = this.getScrollPos();
378         this.scrollLeft[pos == 0 ? 'addClass' : 'removeClass']('ux-taskbuttons-scroller-left-disabled');
379         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('ux-taskbuttons-scroller-right-disabled');
380     }
381 });
382
383
384
385 /**
386  * @class Ext.ux.TaskBar.TaskButton
387  * @extends Ext.Button
388  */
389 Ext.ux.TaskBar.TaskButton = function(win, el){
390     this.win = win;
391     Ext.ux.TaskBar.TaskButton.superclass.constructor.call(this, {
392         iconCls: win.iconCls,
393         text: Ext.util.Format.ellipsis(win.title, 12),
394         renderTo: el,
395         handler : function(){
396             if(win.minimized || win.hidden){
397                 win.show();
398             }else if(win == win.manager.getActive()){
399                 win.minimize();
400             }else{
401                 win.toFront();
402             }
403         },
404         clickEvent:'mousedown',
405         template: new Ext.Template(
406             '<table cellspacing="0" class="x-btn {3}"><tbody><tr>',
407             '<td class="ux-taskbutton-left"><i>&#160;</i></td>',
408             '<td class="ux-taskbutton-center"><em class="{5} unselectable="on">',
409                 '<button class="x-btn-text {2}" type="{1}" style="height:28px;">{0}</button>',
410             '</em></td>',
411             '<td class="ux-taskbutton-right"><i>&#160;</i></td>',
412             "</tr></tbody></table>")
413     });
414 };
415
416 Ext.extend(Ext.ux.TaskBar.TaskButton, Ext.Button, {
417     onRender : function(){
418         Ext.ux.TaskBar.TaskButton.superclass.onRender.apply(this, arguments);
419
420         this.cmenu = new Ext.menu.Menu({
421             items: [{
422                 text: 'Restore',
423                 handler: function(){
424                     if(!this.win.isVisible()){
425                         this.win.show();
426                     }else{
427                         this.win.restore();
428                     }
429                 },
430                 scope: this
431             },{
432                 text: 'Minimize',
433                 handler: this.win.minimize,
434                 scope: this.win
435             },{
436                 text: 'Maximize',
437                 handler: this.win.maximize,
438                 scope: this.win
439             }, '-', {
440                 text: 'Close',
441                 handler: this.closeWin.createDelegate(this, this.win, true),
442                 scope: this.win
443             }]
444         });
445
446         this.cmenu.on('beforeshow', function(){
447             var items = this.cmenu.items.items;
448             var w = this.win;
449             items[0].setDisabled(w.maximized !== true && w.hidden !== true);
450             items[1].setDisabled(w.minimized === true);
451             items[2].setDisabled(w.maximized === true || w.hidden === true);
452         }, this);
453
454         this.el.on('contextmenu', function(e){
455             e.stopEvent();
456             if(!this.cmenu.el){
457                 this.cmenu.render();
458             }
459             var xy = e.getXY();
460             xy[1] -= this.cmenu.el.getHeight();
461             this.cmenu.showAt(xy);
462         }, this);
463     },
464
465     closeWin : function(cMenu, e, win){
466         if(!win.isVisible()){
467             win.show();
468         }else{
469             win.restore();
470         }
471         win.close();
472     }
473 });