updated on Thu Jan 12 04:00:44 UTC 2012
[aur-mirror.git] / gnome-shell-extension-coverflow-alt-tab / switcher.js
blobd632c2b2b3199aa59a5d02ec4f34b3b809dc1cb7
1 /* -*0 mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -* */
3 /* CoverflowAltTab::Switcher:
4  *
5  * The implementation of the switcher UI. Handles keyboard events.
6  */
8 const Lang = imports.lang;
10 const Clutter = imports.gi.Clutter;
11 const St = imports.gi.St;
12 const Meta = imports.gi.Meta;
13 const Shell = imports.gi.Shell;
15 const AltTab = imports.ui.altTab;
16 const Main = imports.ui.main;
17 const Tweener = imports.ui.tweener;
19 let WINDOWPREVIEW_SCALE = 0.5;
21 function Switcher(windows, actions) {
22         this._init(windows, actions);
25 Switcher.prototype = {
26         _init: function(windows, actions) {
27                 this._windows = windows;
28                 this._windowTitle = null;
29                 this._modifierMask = null;
30                 this._currentIndex = 0;
31                 this._actions = actions;
32                 this._haveModal = false;
34                 let monitor = Main.layoutManager.primaryMonitor;
35                 this.actor = new St.Group({ visible: true });
37                 // background
38                 this._background = new St.Group({
39                         style_class: 'coverflow-switcher',
40                         visible: true,
41                         x: 0,
42                         y: 0,
43                         opacity: 0,
44                         width: monitor.width,
45                         height: monitor.height,
46                 });
47                 this._background.add_actor(new St.Bin({
48                         style_class: 'coverflow-switcher-gradient',
49                         visible: true,
50                         x: 0,
51                         y: monitor.height / 2,
52                         width: monitor.width,
53                         height: monitor.height / 2,
54                 }));
55                 this.actor.add_actor(this._background);
57                 // create previews
58                 let currentWorkspace = global.screen.get_active_workspace();
59                 this._previewLayer = new St.Group({ visible: true });
60                 this._previews = [];
61                 for (let i in windows) {
62                         let metaWin = windows[i];
63                         let compositor = windows[i].get_compositor_private();
64                         if (compositor) {
65                                 let texture = compositor.get_texture();
66                                 let [width, height] = texture.get_size();
68                                 let scale = 1.0;
69                                 if (width > monitor.width * WINDOWPREVIEW_SCALE ||
70                                         height > monitor.height * WINDOWPREVIEW_SCALE) {
71                                         scale = Math.min(monitor.width * WINDOWPREVIEW_SCALE / width, monitor.height * WINDOWPREVIEW_SCALE / height);
72                                 }
74                                 let clone = new Clutter.Clone({
75                                         opacity: (metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) ? 255 : 0,
76                                         source: texture,
77                                         reactive: false,
78                                         rotation_center_y: new Clutter.Vertex({ x: width * scale / 2, y: 0.0, z: 0.0 }),
79                                         x: compositor.x,
80                                         y: compositor.y,
81                                 });
83                                 clone.target_width = width * scale;
84                                 clone.target_height = height * scale;
85                                 clone.target_width_side = width * scale * 0.5;
86                                 clone.target_height_side = height * scale * 0.7;
88                                 this._previews.push(clone);
89                                 this._previewLayer.add_actor(clone);
90                         }
91                 }
93                 this.actor.add_actor(this._previewLayer);
94                 Main.uiGroup.add_actor(this.actor);
95         },
97         show: function(shellwm, binding, mask, window, backwords) {
98                 if (!Main.pushModal(this.actor)) {
99                         return false;
100                 }
102                 this._haveModal = true;
103                 this._modifierMask = AltTab.primaryModifier(mask);
105                 this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
106                 this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
107                 this.actor.show();
109                 // hide all window actors
110                 let windows = global.get_window_actors();
111                 for (let i in windows) {
112                         windows[i].hide();
113                 }
115                 this._next();
117                 // There's a race condition; if the user released Alt before
118                 // we gotthe grab, then we won't be notified. (See
119                 // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
120                 // details) So we check now. (Have to do this after updating
121                 // selection.)
122                 let [x, y, mods] = global.get_pointer();
123                 if (!(mods & this._modifierMask)) {
124                         this._activateSelected();
125                         return false;
126                 }
128                 Tweener.addTween(this._background, {
129                         opacity: 255,
130                         time: 0.25,
131                         transition: 'easeOutQuad'
132                 });
134                 return true;
135         },
137         _next: function() {
138                 this._currentIndex = (this._currentIndex + 1) % this._windows.length;
139                 this._updateCoverflow();
140         },
142         _previous: function() {
143                 this._currentIndex = (this._currentIndex + this._windows.length - 1) % this._windows.length;
144                 this._updateCoverflow();
145         },
147         _updateCoverflow: function() {
148                 let monitor = Main.layoutManager.primaryMonitor;
150                 // window title label
151                 if (this._windowTitle) {
152                         Tweener.addTween(this._windowTitle, {
153                                 opacity: 0,
154                                 time: 0.25,
155                                 transition: 'easeOutQuad',
156                                 onComplete: Lang.bind(this._background, this._background.remove_actor, this._windowTitle),
157                         });
158                 }
159                 this._windowTitle = new St.Label({
160                         style_class: 'modal-dialog',
161                         text: this._windows[this._currentIndex].get_title(),
162                         opacity: 0,
163                 });
164                 this._windowTitle.add_style_class_name('run-dialog');
165                 this._windowTitle.add_style_class_name('coverflow-window-title-label');
166                 this._background.add_actor(this._windowTitle);
167                 this._windowTitle.x = (monitor.width - this._windowTitle.width) / 2;
168                 this._windowTitle.y = monitor.height - this._windowTitle.height - 20;
169                 Tweener.addTween(this._windowTitle, {
170                         opacity: 255,
171                         time: 0.25,
172                         transition: 'easeOutQuad',
173                 });
175                 // preview windows
176                 for (let i in this._previews) {
177                         let preview = this._previews[i];
179                         if (i == this._currentIndex) {
180                                 preview.raise_top();
181                                 Tweener.addTween(preview, {
182                                         opacity: 255,
183                                         x: (monitor.width - preview.target_width) / 2,
184                                         y: (monitor.height - preview.target_height) / 2,
185                                         width: preview.target_width,
186                                         height: preview.target_height,
187                                         rotation_angle_y: 0.0,
188                                         time: 0.25,
189                                         transition: 'easeOutQuad',
190                                 });
191                         } else if (i < this._currentIndex) {
192                                 preview.raise_top();
193                                 Tweener.addTween(preview, {
194                                         opacity: 255,
195                                         x: monitor.width * 0.2 - preview.target_width_side / 2 + 25 * (i - this._currentIndex),
196                                         y: (monitor.height - preview.target_height_side) / 2,
197                                         width: preview.target_width_side,
198                                         height: preview.target_height_side,
199                                         rotation_angle_y: 60.0,
200                                         time: 0.25,
201                                         transition: 'easeOutQuad',
202                                 });
203                         } else if (i > this._currentIndex) {
204                                 preview.lower_bottom();
205                                 Tweener.addTween(preview, {
206                                         opacity: 255,
207                                         x: monitor.width * 0.8 - preview.target_width_side / 2 + 25 * (i - this._currentIndex),
208                                         y: (monitor.height - preview.target_height_side) / 2,
209                                         width: preview.target_width_side,
210                                         height: preview.target_height_side,
211                                         rotation_angle_y: -60.0,
212                                         time: 0.25,
213                                         transition: 'easeOutQuad',
214                                 });
215                         }
216                 }
217         },
219         _keyPressEvent: function(actor, event) {
220                 let keysym = event.get_key_symbol();
221                 let event_state = Shell.get_event_state(event);
223                 let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
224                 let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
226                 if (keysym == Clutter.Escape) {
227                         this.destroy();
228                 } else if (keysym == Clutter.q || keysym == Clutter.Q) {
229                         this._actions['remove_selected'](this._windows[this._currentIndex]);
230                         this.destroy();
231                 } else if (action == Meta.KeyBindingAction.SWITCH_GROUP ||
232                                    action == Meta.KeyBindingAction.SWITCH_WINDOWS ||
233                                    action == Meta.KeyBindingAction.SWITCH_PANELS) {
234                         backwards ? this._previous() : this._next();
235                 } else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWORD ||
236                                    action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWORD) {
237                         this._previous();
238                 }
240                 return true;
241         },
243         _keyReleaseEvent: function(actor, event) {
244                 let [x, y, mods] = global.get_pointer();
245                 let state = mods & this._modifierMask;
247                 if (state == 0) {
248                         this._activateSelected();
249                 }
251                 return true;
252         },
254         _activateSelected: function() {
255                 this._actions['activate_selected'](this._windows[this._currentIndex]);
256                 this.destroy();
257         },
259         _onHideBackgroundCompleted: function() {
260                 Main.uiGroup.remove_actor(this.actor);
262                 // show all window actors
263                 let currentWorkspace = global.screen.get_active_workspace();
264                 let windows = global.get_window_actors();
265                 for (let i in windows) {
266                         let metaWin = windows[i].get_meta_window();
267                         if (metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) {
268                                 windows[i].show();
269                         }
270                 }
271         },
273         _onDestroy: function() {
274                 let monitor = Main.layoutManager.primaryMonitor;
276                 // preview windows
277                 let currentWorkspace = global.screen.get_active_workspace();
278                 for (let i in this._previews) {
279                         let preview = this._previews[i];
280                         let metaWin = this._windows[i];
281                         let compositor = this._windows[i].get_compositor_private();
283                         Tweener.addTween(preview, {
284                                 opacity: (metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) ? 255 : 0,
285                                 x: compositor.x,
286                                 y: compositor.y,
287                                 width: compositor.width,
288                                 height: compositor.height,
289                                 rotation_angle_y: 0.0,
290                                 time: 0.25,
291                                 transition: 'easeOutQuad',
292                         });
293                 }
295                 // background
296                 Tweener.removeTweens(this._background);
297                 Tweener.addTween(this._background, {
298                         opacity: 0,
299                         time: 0.25,
300                         transition: 'easeOutQuad',
301                         onComplete: Lang.bind(this, this._onHideBackgroundCompleted),
302                 });
304                 if (this._haveModal) {
305                         Main.popModal(this.actor);
306                         this._haveModal = false;
307                 }
309                 this._windows = null;
310                 this._windowTitle = null;
311                 this._previews = null;
312                 this._previewLayer = null;
313         },
315         destroy: function() {
316                 this._onDestroy();
317         },