1 /* -*0 mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -* */
3 /* CoverflowAltTab::Switcher:
5 * The implementation of the switcher UI. Handles keyboard events.
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 });
38 this._background = new St.Group({
39 style_class: 'coverflow-switcher',
45 height: monitor.height,
47 this._background.add_actor(new St.Bin({
48 style_class: 'coverflow-switcher-gradient',
51 y: monitor.height / 2,
53 height: monitor.height / 2,
55 this.actor.add_actor(this._background);
58 let currentWorkspace = global.screen.get_active_workspace();
59 this._previewLayer = new St.Group({ visible: true });
61 for (let i in windows) {
62 let metaWin = windows[i];
63 let compositor = windows[i].get_compositor_private();
65 let texture = compositor.get_texture();
66 let [width, height] = texture.get_size();
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);
74 let clone = new Clutter.Clone({
75 opacity: (metaWin.get_workspace() == currentWorkspace || metaWin.is_on_all_workspaces()) ? 255 : 0,
78 rotation_center_y: new Clutter.Vertex({ x: width * scale / 2, y: 0.0, z: 0.0 }),
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);
93 this.actor.add_actor(this._previewLayer);
94 Main.uiGroup.add_actor(this.actor);
97 show: function(shellwm, binding, mask, window, backwords) {
98 if (!Main.pushModal(this.actor)) {
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));
109 // hide all window actors
110 let windows = global.get_window_actors();
111 for (let i in windows) {
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
122 let [x, y, mods] = global.get_pointer();
123 if (!(mods & this._modifierMask)) {
124 this._activateSelected();
128 Tweener.addTween(this._background, {
131 transition: 'easeOutQuad'
138 this._currentIndex = (this._currentIndex + 1) % this._windows.length;
139 this._updateCoverflow();
142 _previous: function() {
143 this._currentIndex = (this._currentIndex + this._windows.length - 1) % this._windows.length;
144 this._updateCoverflow();
147 _updateCoverflow: function() {
148 let monitor = Main.layoutManager.primaryMonitor;
150 // window title label
151 if (this._windowTitle) {
152 Tweener.addTween(this._windowTitle, {
155 transition: 'easeOutQuad',
156 onComplete: Lang.bind(this._background, this._background.remove_actor, this._windowTitle),
159 this._windowTitle = new St.Label({
160 style_class: 'modal-dialog',
161 text: this._windows[this._currentIndex].get_title(),
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, {
172 transition: 'easeOutQuad',
176 for (let i in this._previews) {
177 let preview = this._previews[i];
179 if (i == this._currentIndex) {
181 Tweener.addTween(preview, {
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,
189 transition: 'easeOutQuad',
191 } else if (i < this._currentIndex) {
193 Tweener.addTween(preview, {
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,
201 transition: 'easeOutQuad',
203 } else if (i > this._currentIndex) {
204 preview.lower_bottom();
205 Tweener.addTween(preview, {
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,
213 transition: 'easeOutQuad',
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) {
228 } else if (keysym == Clutter.q || keysym == Clutter.Q) {
229 this._actions['remove_selected'](this._windows[this._currentIndex]);
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) {
243 _keyReleaseEvent: function(actor, event) {
244 let [x, y, mods] = global.get_pointer();
245 let state = mods & this._modifierMask;
248 this._activateSelected();
254 _activateSelected: function() {
255 this._actions['activate_selected'](this._windows[this._currentIndex]);
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()) {
273 _onDestroy: function() {
274 let monitor = Main.layoutManager.primaryMonitor;
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,
287 width: compositor.width,
288 height: compositor.height,
289 rotation_angle_y: 0.0,
291 transition: 'easeOutQuad',
296 Tweener.removeTweens(this._background);
297 Tweener.addTween(this._background, {
300 transition: 'easeOutQuad',
301 onComplete: Lang.bind(this, this._onHideBackgroundCompleted),
304 if (this._haveModal) {
305 Main.popModal(this.actor);
306 this._haveModal = false;
309 this._windows = null;
310 this._windowTitle = null;
311 this._previews = null;
312 this._previewLayer = null;
315 destroy: function() {