]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /*!\r |
2 | * Ext JS Library\r | |
3 | * Copyright(c) 2006-2014 Sencha Inc.\r | |
4 | * licensing@sencha.com\r | |
5 | * http://www.sencha.com/license\r | |
6 | */\r | |
7 | \r | |
8 | /**\r | |
9 | * @class Ext.ux.desktop.Desktop\r | |
10 | * @extends Ext.panel.Panel\r | |
11 | * <p>This class manages the wallpaper, shortcuts and taskbar.</p>\r | |
12 | */\r | |
13 | Ext.define('Ext.ux.desktop.Desktop', {\r | |
14 | extend: 'Ext.panel.Panel',\r | |
15 | \r | |
16 | alias: 'widget.desktop',\r | |
17 | \r | |
18 | uses: [\r | |
19 | 'Ext.util.MixedCollection',\r | |
20 | 'Ext.menu.Menu',\r | |
21 | 'Ext.view.View', // dataview\r | |
22 | 'Ext.window.Window',\r | |
23 | \r | |
24 | 'Ext.ux.desktop.TaskBar',\r | |
25 | 'Ext.ux.desktop.Wallpaper'\r | |
26 | ],\r | |
27 | \r | |
28 | activeWindowCls: 'ux-desktop-active-win',\r | |
29 | inactiveWindowCls: 'ux-desktop-inactive-win',\r | |
30 | lastActiveWindow: null,\r | |
31 | \r | |
32 | border: false,\r | |
33 | html: ' ',\r | |
34 | layout: 'fit',\r | |
35 | \r | |
36 | xTickSize: 1,\r | |
37 | yTickSize: 1,\r | |
38 | \r | |
39 | app: null,\r | |
40 | \r | |
41 | /**\r | |
42 | * @cfg {Array/Ext.data.Store} shortcuts\r | |
43 | * The items to add to the DataView. This can be a {@link Ext.data.Store Store} or a\r | |
44 | * simple array. Items should minimally provide the fields in the\r | |
45 | * {@link Ext.ux.desktop.ShortcutModel Shortcut}.\r | |
46 | */\r | |
47 | shortcuts: null,\r | |
48 | \r | |
49 | /**\r | |
50 | * @cfg {String} shortcutItemSelector\r | |
51 | * This property is passed to the DataView for the desktop to select shortcut items.\r | |
52 | * If the {@link #shortcutTpl} is modified, this will probably need to be modified as\r | |
53 | * well.\r | |
54 | */\r | |
55 | shortcutItemSelector: 'div.ux-desktop-shortcut',\r | |
56 | \r | |
57 | /**\r | |
58 | * @cfg {String} shortcutTpl\r | |
59 | * This XTemplate is used to render items in the DataView. If this is changed, the\r | |
60 | * {@link #shortcutItemSelector} will probably also need to changed.\r | |
61 | */\r | |
62 | shortcutTpl: [\r | |
63 | '<tpl for=".">',\r | |
64 | '<div class="ux-desktop-shortcut" id="{name}-shortcut">',\r | |
65 | '<div class="ux-desktop-shortcut-icon {iconCls}">',\r | |
66 | '<img src="',Ext.BLANK_IMAGE_URL,'" title="{name}">',\r | |
67 | '</div>',\r | |
68 | '<span class="ux-desktop-shortcut-text">{name}</span>',\r | |
69 | '</div>',\r | |
70 | '</tpl>',\r | |
71 | '<div class="x-clear"></div>'\r | |
72 | ],\r | |
73 | \r | |
74 | /**\r | |
75 | * @cfg {Object} taskbarConfig\r | |
76 | * The config object for the TaskBar.\r | |
77 | */\r | |
78 | taskbarConfig: null,\r | |
79 | \r | |
80 | windowMenu: null,\r | |
81 | \r | |
82 | initComponent: function () {\r | |
83 | var me = this;\r | |
84 | \r | |
85 | me.windowMenu = new Ext.menu.Menu(me.createWindowMenu());\r | |
86 | \r | |
87 | me.bbar = me.taskbar = new Ext.ux.desktop.TaskBar(me.taskbarConfig);\r | |
88 | me.taskbar.windowMenu = me.windowMenu;\r | |
89 | \r | |
90 | me.windows = new Ext.util.MixedCollection();\r | |
91 | \r | |
92 | me.contextMenu = new Ext.menu.Menu(me.createDesktopMenu());\r | |
93 | \r | |
94 | me.items = [\r | |
95 | { xtype: 'wallpaper', id: me.id+'_wallpaper' },\r | |
96 | me.createDataView()\r | |
97 | ];\r | |
98 | \r | |
99 | me.callParent();\r | |
100 | \r | |
101 | me.shortcutsView = me.items.getAt(1);\r | |
102 | me.shortcutsView.on('itemclick', me.onShortcutItemClick, me);\r | |
103 | \r | |
104 | var wallpaper = me.wallpaper;\r | |
105 | me.wallpaper = me.items.getAt(0);\r | |
106 | if (wallpaper) {\r | |
107 | me.setWallpaper(wallpaper, me.wallpaperStretch);\r | |
108 | }\r | |
109 | },\r | |
110 | \r | |
111 | afterRender: function () {\r | |
112 | var me = this;\r | |
113 | me.callParent();\r | |
114 | me.el.on('contextmenu', me.onDesktopMenu, me);\r | |
115 | },\r | |
116 | \r | |
117 | //------------------------------------------------------\r | |
118 | // Overrideable configuration creation methods\r | |
119 | \r | |
120 | createDataView: function () {\r | |
121 | var me = this;\r | |
122 | return {\r | |
123 | xtype: 'dataview',\r | |
124 | overItemCls: 'x-view-over',\r | |
125 | trackOver: true,\r | |
126 | itemSelector: me.shortcutItemSelector,\r | |
127 | store: me.shortcuts,\r | |
128 | style: {\r | |
129 | position: 'absolute'\r | |
130 | },\r | |
131 | x: 0, y: 0,\r | |
132 | tpl: new Ext.XTemplate(me.shortcutTpl)\r | |
133 | };\r | |
134 | },\r | |
135 | \r | |
136 | createDesktopMenu: function () {\r | |
137 | var me = this, ret = {\r | |
138 | items: me.contextMenuItems || []\r | |
139 | };\r | |
140 | \r | |
141 | if (ret.items.length) {\r | |
142 | ret.items.push('-');\r | |
143 | }\r | |
144 | \r | |
145 | ret.items.push(\r | |
146 | { text: 'Tile', handler: me.tileWindows, scope: me, minWindows: 1 },\r | |
147 | { text: 'Cascade', handler: me.cascadeWindows, scope: me, minWindows: 1 }\r | |
148 | );\r | |
149 | \r | |
150 | return ret;\r | |
151 | },\r | |
152 | \r | |
153 | createWindowMenu: function () {\r | |
154 | var me = this;\r | |
155 | return {\r | |
156 | defaultAlign: 'br-tr',\r | |
157 | items: [\r | |
158 | { text: 'Restore', handler: me.onWindowMenuRestore, scope: me },\r | |
159 | { text: 'Minimize', handler: me.onWindowMenuMinimize, scope: me },\r | |
160 | { text: 'Maximize', handler: me.onWindowMenuMaximize, scope: me },\r | |
161 | '-',\r | |
162 | { text: 'Close', handler: me.onWindowMenuClose, scope: me }\r | |
163 | ],\r | |
164 | listeners: {\r | |
165 | beforeshow: me.onWindowMenuBeforeShow,\r | |
166 | hide: me.onWindowMenuHide,\r | |
167 | scope: me\r | |
168 | }\r | |
169 | };\r | |
170 | },\r | |
171 | \r | |
172 | //------------------------------------------------------\r | |
173 | // Event handler methods\r | |
174 | \r | |
175 | onDesktopMenu: function (e) {\r | |
176 | var me = this, menu = me.contextMenu;\r | |
177 | e.stopEvent();\r | |
178 | if (!menu.rendered) {\r | |
179 | menu.on('beforeshow', me.onDesktopMenuBeforeShow, me);\r | |
180 | }\r | |
181 | menu.showAt(e.getXY());\r | |
182 | menu.doConstrain();\r | |
183 | },\r | |
184 | \r | |
185 | onDesktopMenuBeforeShow: function (menu) {\r | |
186 | var me = this, count = me.windows.getCount();\r | |
187 | \r | |
188 | menu.items.each(function (item) {\r | |
189 | var min = item.minWindows || 0;\r | |
190 | item.setDisabled(count < min);\r | |
191 | });\r | |
192 | },\r | |
193 | \r | |
194 | onShortcutItemClick: function (dataView, record) {\r | |
195 | var me = this, module = me.app.getModule(record.data.module),\r | |
196 | win = module && module.createWindow();\r | |
197 | \r | |
198 | if (win) {\r | |
199 | me.restoreWindow(win);\r | |
200 | }\r | |
201 | },\r | |
202 | \r | |
203 | onWindowClose: function(win) {\r | |
204 | var me = this;\r | |
205 | me.windows.remove(win);\r | |
206 | me.taskbar.removeTaskButton(win.taskButton);\r | |
207 | me.updateActiveWindow();\r | |
208 | },\r | |
209 | \r | |
210 | //------------------------------------------------------\r | |
211 | // Window context menu handlers\r | |
212 | \r | |
213 | onWindowMenuBeforeShow: function (menu) {\r | |
214 | var items = menu.items.items, win = menu.theWin;\r | |
215 | items[0].setDisabled(win.maximized !== true && win.hidden !== true); // Restore\r | |
216 | items[1].setDisabled(win.minimized === true); // Minimize\r | |
217 | items[2].setDisabled(win.maximized === true || win.hidden === true); // Maximize\r | |
218 | },\r | |
219 | \r | |
220 | onWindowMenuClose: function () {\r | |
221 | var me = this, win = me.windowMenu.theWin;\r | |
222 | \r | |
223 | win.close();\r | |
224 | },\r | |
225 | \r | |
226 | onWindowMenuHide: function (menu) {\r | |
227 | Ext.defer(function() {\r | |
228 | menu.theWin = null;\r | |
229 | }, 1);\r | |
230 | },\r | |
231 | \r | |
232 | onWindowMenuMaximize: function () {\r | |
233 | var me = this, win = me.windowMenu.theWin;\r | |
234 | \r | |
235 | win.maximize();\r | |
236 | win.toFront();\r | |
237 | },\r | |
238 | \r | |
239 | onWindowMenuMinimize: function () {\r | |
240 | var me = this, win = me.windowMenu.theWin;\r | |
241 | \r | |
242 | win.minimize();\r | |
243 | },\r | |
244 | \r | |
245 | onWindowMenuRestore: function () {\r | |
246 | var me = this, win = me.windowMenu.theWin;\r | |
247 | \r | |
248 | me.restoreWindow(win);\r | |
249 | },\r | |
250 | \r | |
251 | //------------------------------------------------------\r | |
252 | // Dynamic (re)configuration methods\r | |
253 | \r | |
254 | getWallpaper: function () {\r | |
255 | return this.wallpaper.wallpaper;\r | |
256 | },\r | |
257 | \r | |
258 | setTickSize: function(xTickSize, yTickSize) {\r | |
259 | var me = this,\r | |
260 | xt = me.xTickSize = xTickSize,\r | |
261 | yt = me.yTickSize = (arguments.length > 1) ? yTickSize : xt;\r | |
262 | \r | |
263 | me.windows.each(function(win) {\r | |
264 | var dd = win.dd, resizer = win.resizer;\r | |
265 | dd.xTickSize = xt;\r | |
266 | dd.yTickSize = yt;\r | |
267 | resizer.widthIncrement = xt;\r | |
268 | resizer.heightIncrement = yt;\r | |
269 | });\r | |
270 | },\r | |
271 | \r | |
272 | setWallpaper: function (wallpaper, stretch) {\r | |
273 | this.wallpaper.setWallpaper(wallpaper, stretch);\r | |
274 | return this;\r | |
275 | },\r | |
276 | \r | |
277 | //------------------------------------------------------\r | |
278 | // Window management methods\r | |
279 | \r | |
280 | cascadeWindows: function() {\r | |
281 | var x = 0, y = 0,\r | |
282 | zmgr = this.getDesktopZIndexManager();\r | |
283 | \r | |
284 | zmgr.eachBottomUp(function(win) {\r | |
285 | if (win.isWindow && win.isVisible() && !win.maximized) {\r | |
286 | win.setPosition(x, y);\r | |
287 | x += 20;\r | |
288 | y += 20;\r | |
289 | }\r | |
290 | });\r | |
291 | },\r | |
292 | \r | |
293 | createWindow: function(config, cls) {\r | |
294 | var me = this, win, cfg = Ext.applyIf(config || {}, {\r | |
295 | stateful: false,\r | |
296 | isWindow: true,\r | |
297 | constrainHeader: true,\r | |
298 | minimizable: true,\r | |
299 | maximizable: true\r | |
300 | });\r | |
301 | \r | |
302 | cls = cls || Ext.window.Window;\r | |
303 | win = me.add(new cls(cfg));\r | |
304 | \r | |
305 | me.windows.add(win);\r | |
306 | \r | |
307 | win.taskButton = me.taskbar.addTaskButton(win);\r | |
308 | win.animateTarget = win.taskButton.el;\r | |
309 | \r | |
310 | win.on({\r | |
311 | activate: me.updateActiveWindow,\r | |
312 | beforeshow: me.updateActiveWindow,\r | |
313 | deactivate: me.updateActiveWindow,\r | |
314 | minimize: me.minimizeWindow,\r | |
315 | destroy: me.onWindowClose,\r | |
316 | scope: me\r | |
317 | });\r | |
318 | \r | |
319 | win.on({\r | |
320 | boxready: function () {\r | |
321 | win.dd.xTickSize = me.xTickSize;\r | |
322 | win.dd.yTickSize = me.yTickSize;\r | |
323 | \r | |
324 | if (win.resizer) {\r | |
325 | win.resizer.widthIncrement = me.xTickSize;\r | |
326 | win.resizer.heightIncrement = me.yTickSize;\r | |
327 | }\r | |
328 | },\r | |
329 | single: true\r | |
330 | });\r | |
331 | \r | |
332 | // replace normal window close w/fadeOut animation:\r | |
333 | win.doClose = function () {\r | |
334 | win.doClose = Ext.emptyFn; // dblclick can call again...\r | |
335 | win.el.disableShadow();\r | |
336 | win.el.fadeOut({\r | |
337 | listeners: {\r | |
338 | afteranimate: function () {\r | |
339 | win.destroy();\r | |
340 | }\r | |
341 | }\r | |
342 | });\r | |
343 | };\r | |
344 | \r | |
345 | return win;\r | |
346 | },\r | |
347 | \r | |
348 | getActiveWindow: function () {\r | |
349 | var win = null,\r | |
350 | zmgr = this.getDesktopZIndexManager();\r | |
351 | \r | |
352 | if (zmgr) {\r | |
353 | // We cannot rely on activate/deactive because that fires against non-Window\r | |
354 | // components in the stack.\r | |
355 | \r | |
356 | zmgr.eachTopDown(function (comp) {\r | |
357 | if (comp.isWindow && !comp.hidden) {\r | |
358 | win = comp;\r | |
359 | return false;\r | |
360 | }\r | |
361 | return true;\r | |
362 | });\r | |
363 | }\r | |
364 | \r | |
365 | return win;\r | |
366 | },\r | |
367 | \r | |
368 | getDesktopZIndexManager: function () {\r | |
369 | var windows = this.windows;\r | |
370 | // TODO - there has to be a better way to get this...\r | |
371 | return (windows.getCount() && windows.getAt(0).zIndexManager) || null;\r | |
372 | },\r | |
373 | \r | |
374 | getWindow: function(id) {\r | |
375 | return this.windows.get(id);\r | |
376 | },\r | |
377 | \r | |
378 | minimizeWindow: function(win) {\r | |
379 | win.minimized = true;\r | |
380 | win.hide();\r | |
381 | },\r | |
382 | \r | |
383 | restoreWindow: function (win) {\r | |
384 | if (win.isVisible()) {\r | |
385 | win.restore();\r | |
386 | win.toFront();\r | |
387 | } else {\r | |
388 | win.show();\r | |
389 | }\r | |
390 | return win;\r | |
391 | },\r | |
392 | \r | |
393 | tileWindows: function() {\r | |
394 | var me = this, availWidth = me.body.getWidth(true);\r | |
395 | var x = me.xTickSize, y = me.yTickSize, nextY = y;\r | |
396 | \r | |
397 | me.windows.each(function(win) {\r | |
398 | if (win.isVisible() && !win.maximized) {\r | |
399 | var w = win.el.getWidth();\r | |
400 | \r | |
401 | // Wrap to next row if we are not at the line start and this Window will\r | |
402 | // go off the end\r | |
403 | if (x > me.xTickSize && x + w > availWidth) {\r | |
404 | x = me.xTickSize;\r | |
405 | y = nextY;\r | |
406 | }\r | |
407 | \r | |
408 | win.setPosition(x, y);\r | |
409 | x += w + me.xTickSize;\r | |
410 | nextY = Math.max(nextY, y + win.el.getHeight() + me.yTickSize);\r | |
411 | }\r | |
412 | });\r | |
413 | },\r | |
414 | \r | |
415 | updateActiveWindow: function () {\r | |
416 | var me = this, activeWindow = me.getActiveWindow(), last = me.lastActiveWindow;\r | |
417 | if (last && last.destroyed) {\r | |
418 | me.lastActiveWindow = null;\r | |
419 | return;\r | |
420 | }\r | |
421 | if (activeWindow === last) {\r | |
422 | return;\r | |
423 | }\r | |
424 | \r | |
425 | if (last) {\r | |
426 | if (last.el.dom) {\r | |
427 | last.addCls(me.inactiveWindowCls);\r | |
428 | last.removeCls(me.activeWindowCls);\r | |
429 | }\r | |
430 | last.active = false;\r | |
431 | }\r | |
432 | \r | |
433 | me.lastActiveWindow = activeWindow;\r | |
434 | \r | |
435 | if (activeWindow) {\r | |
436 | activeWindow.addCls(me.activeWindowCls);\r | |
437 | activeWindow.removeCls(me.inactiveWindowCls);\r | |
438 | activeWindow.minimized = false;\r | |
439 | activeWindow.active = true;\r | |
440 | }\r | |
441 | \r | |
442 | me.taskbar.setActiveButton(activeWindow && activeWindow.taskButton);\r | |
443 | }\r | |
444 | });\r |