[gnome-shell-extensions] alternate-tab: refactor All & Thumbnails
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell-extensions] alternate-tab: refactor All & Thumbnails
- Date: Tue, 7 Aug 2012 21:42:32 +0000 (UTC)
commit d12307991af2b277645f6cfe1998e3956cbf2937
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Tue Aug 7 23:04:41 2012 +0200
alternate-tab: refactor All & Thumbnails
Split the two modes in two different modules, for easier maintenance.
Refactor the AllThumbnails code completely, by copying relevant
or useful code for gnome-shell directly instead of hacking around
it.
extensions/alternate-tab/Makefile.am | 2 +-
extensions/alternate-tab/allThumbnails.js | 330 ++++++++++++++++++++
extensions/alternate-tab/extension.js | 461 +---------------------------
extensions/alternate-tab/prefs.js | 4 +-
extensions/alternate-tab/workspaceIcons.js | 274 +++++++++++++++++
5 files changed, 612 insertions(+), 459 deletions(-)
---
diff --git a/extensions/alternate-tab/Makefile.am b/extensions/alternate-tab/Makefile.am
index 1f35392..d9e0f36 100644
--- a/extensions/alternate-tab/Makefile.am
+++ b/extensions/alternate-tab/Makefile.am
@@ -1,6 +1,6 @@
EXTENSION_ID = alternate-tab
-EXTRA_MODULES = prefs.js
+EXTRA_MODULES = allThumbnails.js workspaceIcons.js prefs.js
include ../../extension.mk
include ../../settings.mk
diff --git a/extensions/alternate-tab/allThumbnails.js b/extensions/alternate-tab/allThumbnails.js
new file mode 100644
index 0000000..2216bac
--- /dev/null
+++ b/extensions/alternate-tab/allThumbnails.js
@@ -0,0 +1,330 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const AltTab = imports.ui.altTab;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const Tweener = imports.ui.tweener;
+const WindowManager = imports.ui.windowManager;
+
+const Gettext = imports.gettext.domain('gnome-shell-extensions');
+const _ = Gettext.gettext;
+const N_ = function(e) { return e };
+
+function mod(a, b) {
+ return ((a+b) % b);
+}
+
+const AltTabPopupAllThumbnails = new Lang.Class({
+ Name: 'AlternateTab.AltTabPopup.AllThumbnails',
+
+ _init : function() {
+ this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
+ reactive: true });
+
+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+ this.actor.connect('allocate', Lang.bind(this, this._allocate));
+
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+ this._haveModal = false;
+
+ this._currentWindow = 0;
+ this._motionTimeoutId = 0;
+
+ // Initially disable hover so we ignore the enter-event if
+ // the switcher appears underneath the current pointer location
+ this._disableHover();
+
+ Main.uiGroup.add_actor(this.actor);
+ },
+
+ _getPreferredWidth: function (actor, forHeight, alloc) {
+ alloc.min_size = global.screen_width;
+ alloc.natural_size = global.screen_width;
+ },
+
+ _getPreferredHeight: function (actor, forWidth, alloc) {
+ alloc.min_size = global.screen_height;
+ alloc.natural_size = global.screen_height;
+ },
+
+ _allocate: function (actor, box, flags) {
+ let childBox = new Clutter.ActorBox();
+ let primary = Main.layoutManager.primaryMonitor;
+
+ let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
+ let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
+ let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
+ let vPadding = this.actor.get_theme_node().get_vertical_padding();
+ let hPadding = leftPadding + rightPadding;
+
+ // Allocate the appSwitcher
+ // We select a size based on an icon size that does not overflow the screen
+ let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
+ let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
+ childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
+ childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
+ childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
+ childBox.y2 = childBox.y1 + childNaturalHeight;
+ this._appSwitcher.actor.allocate(childBox, flags);
+ },
+
+ show : function(backward, binding, mask) {
+ // This is roughly what meta_display_get_tab_list does, except
+ // that it doesn't filter on workspace
+ // See in particular src/core/window-private.h for the filters
+ let windows = global.get_window_actors().map(function(actor) {
+ return actor.meta_window;
+ }).filter(function(win) {
+ return !win.is_override_redirect() &&
+ win.get_window_type() != Meta.WindowType.DESKTOP &&
+ win.get_window_type() != Meta.WindowType.DOCK;
+ }).sort(function(one, two) {
+ return two.get_user_time() - one.get_user_time();
+ });
+
+ if (!windows.length) {
+ this.destroy();
+ return false;
+ }
+
+ if (!Main.pushModal(this.actor)) {
+ // Probably someone else has a pointer grab, try again with keyboard only
+ if (!Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED)) {
+ return false;
+ }
+ }
+ this._haveModal = true;
+ this._modifierMask = AltTab.primaryModifier(mask);
+
+ this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
+ this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
+
+ this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
+ this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
+
+ this._appSwitcher = new WindowList(windows);
+ this.actor.add_actor(this._appSwitcher.actor);
+ this._appSwitcher.connect('item-activated', Lang.bind(this, this._windowActivated));
+ this._appSwitcher.connect('item-entered', Lang.bind(this, this._windowEntered));
+
+ // make the initial selection
+ if (backward)
+ this._select(windows.length - 1);
+ else
+ this._select(1);
+
+ this.actor.opacity = 0;
+ this.actor.show();
+
+ // There's a race condition; if the user released Alt before
+ // we got the grab, then we won't be notified. (See
+ // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
+ // details.) So we check now. (Have to do this after updating
+ // selection.)
+ let [x, y, mods] = global.get_pointer();
+ if (!(mods & this._modifierMask)) {
+ this._finish();
+ return false;
+ }
+
+ // We delay showing the popup so that fast Alt+Tab users aren't
+ // disturbed by the popup briefly flashing.
+ this._initialDelayTimeoutId = Mainloop.timeout_add(AltTab.POPUP_DELAY_TIMEOUT,
+ Lang.bind(this, function () {
+ this.actor.opacity = 255;
+ this._initialDelayTimeoutId = 0;
+ }));
+
+ return true
+ },
+
+ _windowActivated : function(thumbnailList, n) {
+ let win = this._appSwitcher.windows[n];
+ Main.activateWindow(win);
+ this.destroy();
+ },
+
+ _finish : function() {
+ let win = this._appSwitcher.windows[this._currentWindow];
+ Main.activateWindow(win);
+ this.destroy();
+ },
+
+ _keyPressEvent : function(actor, event) {
+ let keysym = event.get_key_symbol();
+ let event_state = event.get_state();
+ let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
+ let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
+
+ this._disableHover();
+
+ if (keysym == Clutter.Escape) {
+ this.destroy();
+ } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS ||
+ action == Meta.KeyBindingAction.SWITCH_GROUP) {
+ this._select(backwards ? this._previousWindow() : this._nextWindow());
+ } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD ||
+ action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
+ this._select(this._previousWindow());
+ } else {
+ if (keysym == Clutter.Left)
+ this._select(this._previousWindow());
+ else if (keysym == Clutter.Right)
+ this._select(this._nextWindow());
+ }
+
+ return true;
+ },
+
+ _keyReleaseEvent : function(actor, event) {
+ let [x, y, mods] = global.get_pointer();
+ let state = mods & this._modifierMask;
+
+ if (state == 0)
+ this._finish();
+
+ return true;
+ },
+
+ _onScroll : function(actor, event) {
+ let direction = event.get_scroll_direction();
+ if (direction == Clutter.ScrollDirection.UP)
+ this._select(this._previousWindow());
+ else if (direction == Clutter.ScrollDirection.DOWN)
+ this._select(this._nextWindow());
+
+ return true;
+ },
+
+ _clickedOutside : function(actor, event) {
+ this.destroy();
+ },
+
+ _windowEntered : function(windowSwitcher, n) {
+ if (!this._mouseActive)
+ return;
+
+ this._select(n);
+ },
+
+ _disableHover : function() {
+ this._mouseActive = false;
+
+ if (this._motionTimeoutId != 0)
+ Mainloop.source_remove(this._motionTimeoutId);
+
+ this._motionTimeoutId = Mainloop.timeout_add(AltTab.DISABLE_HOVER_TIMEOUT, Lang.bind(this, this._mouseTimedOut));
+ },
+
+ _mouseTimedOut : function() {
+ this._motionTimeoutId = 0;
+ this._mouseActive = true;
+ },
+
+ _popModal: function() {
+ if (this._haveModal) {
+ Main.popModal(this.actor);
+ this._haveModal = false;
+ }
+ },
+
+ destroy : function() {
+ this._popModal();
+ if (this.actor.visible) {
+ Tweener.addTween(this.actor,
+ { opacity: 0,
+ time: AltTab.POPUP_FADE_OUT_TIME,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this,
+ function() {
+ this.actor.destroy();
+ })
+ });
+ } else
+ this.actor.destroy();
+ },
+
+ _onDestroy : function() {
+ this._popModal();
+
+ if (this._motionTimeoutId != 0)
+ Mainloop.source_remove(this._motionTimeoutId);
+ if (this._initialDelayTimeoutId != 0)
+ Mainloop.source_remove(this._initialDelayTimeoutId);
+ },
+
+ _select : function(window) {
+ this._currentWindow = window;
+ this._appSwitcher.highlight(window);
+ },
+
+ _nextWindow: function() {
+ return mod(this._currentWindow + 1, this._appSwitcher.windows.length);
+ },
+
+ _previousWindow: function() {
+ return mod(this._currentWindow - 1, this._appSwitcher.windows.length);
+ },
+});
+
+const WindowIcon = new Lang.Class({
+ Name: 'WindowIcon',
+
+ _init: function(window) {
+ this.window = window;
+
+ this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
+ vertical: true });
+ this.icon = null;
+ this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
+
+ this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
+ this.label = new St.Label({ text: window.get_title() });
+ this.actor.add(this.label, { x_fill: false });
+ },
+
+ set_size: function(size) {
+ let mutterWindow = this.window.get_compositor_private();
+ let windowTexture = mutterWindow.get_texture();
+ let [width, height] = windowTexture.get_size();
+ let scale = Math.min(1.0, size / width, size / height);
+
+ this.clone = new Clutter.Clone({ source: windowTexture, width: width * scale, height: height * scale });
+
+ this._iconBin.set_size(size, size);
+ this._iconBin.child = this.clone;
+ }
+});
+
+const WindowList = new Lang.Class({
+ Name: 'AlternateTab.WindowList',
+ Extends: AltTab.SwitcherList,
+
+ _init : function(windows) {
+ this.parent(true);
+
+ this.windows = windows;
+ this.icons = [];
+
+ for (let i = 0; i < windows.length; i++) {
+ let win = windows[i];
+ let icon = new WindowIcon(win);
+ icon.set_size(128);
+
+ this.addItem(icon.actor, icon.label);
+ this.icons.push(icon);
+ }
+ }
+});
diff --git a/extensions/alternate-tab/extension.js b/extensions/alternate-tab/extension.js
index 4eb9b2d..f3cbaba 100644
--- a/extensions/alternate-tab/extension.js
+++ b/extensions/alternate-tab/extension.js
@@ -28,467 +28,16 @@ const N_ = function(e) { return e };
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
+const WorkspaceIcons = Me.imports.workspaceIcons;
+const AllThumbnails = Me.imports.allThumbnails;
let settings;
-const POPUP_DELAY_TIMEOUT = 150; // milliseconds
-
const SETTINGS_BEHAVIOUR_KEY = 'behaviour';
-const SETTINGS_HIGHLIGHT_SELECTED_KEY = 'highlight-selected';
-
-const AltTabPopupWorkspaceIcons = new Lang.Class({
- Name: 'AlternateTab.AltTabPopupWorkspaceIcons',
- Extends: AltTab.AltTabPopup,
-
- _windowActivated : function(thumbnailList, n) { },
-
- show : function(backward, binding, mask) {
- let appSys = Shell.AppSystem.get_default();
- let apps = appSys.get_running ();
-
- if (!apps.length)
- return false;
-
- if (!Main.pushModal(this.actor)) {
- // Probably someone else has a pointer grab, try again with keyboard only
- if (!Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED)) {
- return false;
- }
- }
- this._haveModal = true;
- this._modifierMask = AltTab.primaryModifier(mask);
-
- this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
- this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
-
- this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
- this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
-
- this._appSwitcher = new WindowSwitcher(apps, this);
- this.actor.add_actor(this._appSwitcher.actor);
- this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
- this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
-
- this._appIcons = this._appSwitcher.icons;
-
- // Need to force an allocation so we can figure out whether we
- // need to scroll when selecting
- this.actor.opacity = 0;
- this.actor.show();
- this.actor.get_allocation_box();
-
- this._highlight_selected = settings.get_boolean(SETTINGS_HIGHLIGHT_SELECTED_KEY);
-
- // Make the initial selection
- if (binding == 'switch_group') {
- //see AltTab.AltTabPopup.show function
- //cached windows are always of length one, so select first app and the window
- //the direction doesn't matter, so ignore backward
- this._select(0, 0);
- } else if (binding == 'switch_group_backward') {
- this._select(0, 0);
- } else if (binding == 'switch_windows_backward') {
- this._select(this._appIcons.length - 1);
- } else if (this._appIcons.length == 1) {
- this._select(0);
- } else if (backward) {
- this._select(this._appIcons.length - 1);
- } else {
- this._select(1);
- }
-
-
- // There's a race condition; if the user released Alt before
- // we got the grab, then we won't be notified. (See
- // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
- // details.) So we check now. (Have to do this after updating
- // selection.)
- let [x, y, mods] = global.get_pointer();
- if (!(mods & this._modifierMask)) {
- this._finish();
- return false;
- }
-
- // We delay showing the popup so that fast Alt+Tab users aren't
- // disturbed by the popup briefly flashing.
- this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
- Lang.bind(this, function () {
- this.actor.opacity = 255;
- this._initialDelayTimeoutId = 0;
- }));
-
- return true;
- },
-
- _select : function(app, window, forceAppFocus) {
- if (app != this._currentApp || window == null) {
- if (this._thumbnails)
- this._destroyThumbnails();
- }
-
- if (this._thumbnailTimeoutId != 0) {
- Mainloop.source_remove(this._thumbnailTimeoutId);
- this._thumbnailTimeoutId = 0;
- }
-
- this._thumbnailsFocused = (window != null) && !forceAppFocus;
-
- this._currentApp = app;
- this._currentWindow = window ? window : -1;
- this._appSwitcher.highlight(app, this._thumbnailsFocused);
-
- if (window != null) {
- if (!this._thumbnails)
- this._createThumbnails();
- this._currentWindow = window;
- this._thumbnails.highlight(window, forceAppFocus);
- } else if (this._appIcons[this._currentApp].cachedWindows.length > 1 &&
- !forceAppFocus) {
- this._thumbnailTimeoutId = Mainloop.timeout_add (
- THUMBNAIL_POPUP_TIME,
- Lang.bind(this, this._timeoutPopupThumbnails));
- }
- if (this._highlight_selected) {
- let current_app = this._appIcons[this._currentApp];
- Main.activateWindow(current_app.cachedWindows[0]);
- }
- },
-
- _finish : function() {
- let app = this._appIcons[this._currentApp];
- if (!app)
- return;
-
- /*
- * We've to restore the original Z-depth and order of all windows.
- *
- * Gnome-shell doesn't give an option to change Z-depth without
- * messing the window's user_time.
- *
- * Pointless if the popup wasn't showed.
- */
- if (this._highlight_selected && this.actor.opacity == 255) {
- for (let i = this._appIcons.length - 2; i >= 0; i--) {
- let app_walker = this._appIcons[i];
- Main.activateWindow(app_walker.cachedWindows[0], global.get_current_time() - i - 1);
- }
- }
-
- Main.activateWindow(app.cachedWindows[0]);
- this.destroy();
- }
-
-});
-
-const AppIcon = new Lang.Class({
- Name: 'AlternateTab.AppIcon',
- Extends: AltTab.AppIcon,
-
- _init: function(app, window) {
- this.app = app;
-
- this.cachedWindows = [];
- this.cachedWindows.push(window);
-
- this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
- vertical: true });
- this.icon = null;
- this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
-
- this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
-
- let title = window.get_title();
- if (title) {
- this.label = new St.Label({ text: title });
- let bin = new St.Bin({ x_align: St.Align.MIDDLE });
- bin.add_actor(this.label);
- this.actor.add(bin);
- }
- else {
- this.label = new St.Label({ text: this.app.get_name() });
- this.actor.add(this.label, { x_fill: false });
- }
- }
-});
-
-const WindowSwitcher = new Lang.Class({
- Name: 'AlternateTab.WindowSwitcher',
- Extends: AltTab.AppSwitcher,
-
- _init : function(apps, altTabPopup) {
- // Horrible HACK!
- // We inherit from AltTab.AppSwitcher, but only chain up to
- // AltTab.SwitcherList._init, to bypass AltTab.AppSwitcher._init
- AltTab.SwitcherList.prototype._init.call(this, true);
-
- // Construct the AppIcons, sort by time, add to the popup
- let activeWorkspace = global.screen.get_active_workspace();
- let workspaceIcons = [];
- let otherIcons = [];
- for (let i = 0; i < apps.length; i++) {
- // Cache the window list now; we don't handle dynamic changes here,
- // and we don't want to be continually retrieving it
- let windows = apps[i].get_windows();
-
- for(let j = 0; j < windows.length; j++) {
- let appIcon = new AppIcon(apps[i], windows[j]);
- if (this._isWindowOnWorkspace(windows[j], activeWorkspace)) {
- workspaceIcons.push(appIcon);
- }
- else {
- otherIcons.push(appIcon);
- }
- }
- }
-
- workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
- otherIcons.sort(Lang.bind(this, this._sortAppIcon));
-
- if(otherIcons.length > 0) {
- let mostRecentOtherIcon = otherIcons[0];
- otherIcons = [];
- otherIcons.push(mostRecentOtherIcon);
- }
-
- this.icons = [];
- this._arrows = [];
- for (let i = 0; i < workspaceIcons.length; i++)
- this._addIcon(workspaceIcons[i]);
- if (workspaceIcons.length > 0 && otherIcons.length > 0)
- this.addSeparator();
- for (let i = 0; i < otherIcons.length; i++)
- this._addIcon(otherIcons[i]);
-
- this._curApp = -1;
- this._iconSize = 0;
- this._altTabPopup = altTabPopup;
- this._mouseTimeOutId = 0;
- },
-
-
- _isWindowOnWorkspace: function(w, workspace) {
- if (w.get_workspace() == workspace)
- return true;
- return false;
- },
-
- _sortAppIcon : function(appIcon1, appIcon2) {
- let t1 = appIcon1.cachedWindows[0].get_user_time();
- let t2 = appIcon2.cachedWindows[0].get_user_time();
- if (t2 > t1) return 1;
- else return -1;
- }
-});
-
-const AltTabPopupAllThumbnails = new Lang.Class({
- Name: 'AlternateTab.AltTabPopup.AllThumbnails',
- Extends: AltTab.AltTabPopup,
-
- _init : function() {
- this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
- reactive: true });
-
- this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
- this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
- this.actor.connect('allocate', Lang.bind(this, this._allocate));
-
- this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
-
- this._haveModal = false;
-
- this._currentApp = 0;
- this._currentWindow = -1;
- this._thumbnailTimeoutId = 0;
- this._motionTimeoutId = 0;
-
-
- // Initially disable hover so we ignore the enter-event if
- // the switcher appears underneath the current pointer location
- this._disableHover();
-
- //this.show();
- Main.uiGroup.add_actor(this.actor);
- //this._select(0);
- },
-
- show : function(backward, binding, mask) {
- let windows = global.get_window_actors();
-
- let list = '';
- let normal_windows= [];
- let appIcons = [];
- let appSys = Shell.AppSystem.get_default();
- let apps = appSys.get_running();
-
- for (let w = windows.length-1; w >= 0; w--) {
- let win = windows[w].get_meta_window();
- normal_windows.push(win);
- }
- normal_windows.sort(Lang.bind(this, this._sortWindows));
-
- let win_on_top = normal_windows.shift();
- normal_windows.push(win_on_top);
- windows = normal_windows;
- for (let w = 0; w < windows.length; w++) {
- let win = windows[w];
-
- let ap1 = null;
- for (let i = 0;i < apps.length; i++) {
- let app_wins = apps[i].get_windows();
- for (let j = 0;j < app_wins.length; j++) {
- if (app_wins[j] == win)
- ap1 = new AltTab.AppIcon(apps[i]);
- }
- }
- if (ap1 != null) {
- ap1.cachedWindows = [win];
- appIcons.push(ap1);
- }
- }
-
- if (!windows.length) {
- this.destroy();
- return false;
- }
-
- if (!Main.pushModal(this.actor)) {
- // Probably someone else has a pointer grab, try again with keyboard only
- if (!Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED)) {
- return false;
- }
- }
- this._haveModal = true;
- this._modifierMask = AltTab.primaryModifier(mask);
-
- this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
- this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
-
- this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
- this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
-
- this._appSwitcher = new WindowList(windows);
- this._appSwitcher._altTabPopup=this;
- this.actor.add_actor(this._appSwitcher.actor);
- this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
- this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
-
- this._appIcons = appIcons;
-
- // make the initial selection
- if (backward)
- this._select(windows.length - 2);
- else
- this._select(0);
-
- this.actor.opacity = 0;
- this.actor.show();
-
- // There's a race condition; if the user released Alt before
- // we got the grab, then we won't be notified. (See
- // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
- // details.) So we check now. (Have to do this after updating
- // selection.)
- let [x, y, mods] = global.get_pointer();
- if (!(mods & this._modifierMask)) {
- this._finish();
- return false;
- }
-
- // We delay showing the popup so that fast Alt+Tab users aren't
- // disturbed by the popup briefly flashing.
- this._initialDelayTimeoutId = Mainloop.timeout_add(AltTab.POPUP_DELAY_TIMEOUT,
- Lang.bind(this, function () {
- this.actor.opacity = 255;
- this._initialDelayTimeoutId = 0;
- }));
-
- return true
- },
-
- _sortWindows : function(win1,win2) {
- let t1 = win1.get_user_time();
- let t2 = win2.get_user_time();
- if (t2 > t1) return 1;
- else return -1;
- },
-
- _appActivated : function(thumbnailList, n) {
- let appIcon = this._appIcons[this._currentApp];
- Main.activateWindow(appIcon.cachedWindows[0]);
- this.destroy();
- },
-
- _finish : function() {
- let app = this._appIcons[this._currentApp];
- Main.activateWindow(app.cachedWindows[0]);
- this.destroy();
- },
-});
-
-const WindowList = new Lang.Class({
- Name: 'AlternateTab.WindowList',
- Extends: AltTab.SwitcherList,
-
- _init : function(windows) {
- this.parent(true);
-
- let activeWorkspace = global.screen.get_active_workspace();
- this._labels = new Array();
- this._thumbnailBins = new Array();
- this._clones = new Array();
- this._windows = windows;
- this._arrows = new Array();
- this.icons = new Array();
- for (let w = 0; w < windows.length; w++) {
- let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
- arrow.connect('repaint', Lang.bind(this, function (area) {
- Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
- }));
- this._list.add_actor(arrow);
- this._arrows.push(arrow);
-
- arrow.hide();
-
- let win=windows[w];
-
- let appSys = Shell.AppSystem.get_default();
- let apps = appSys.get_running();
- let ap1 = null;
- for (let i = 0; i < apps.length; i++) {
- let app_wins = apps[i].get_windows();
- for (let j = 0; j < app_wins.length; j++) {
- if (app_wins[j] == win) {
- ap1 = new AltTab.AppIcon(apps[i]);
- let mutterWindow = win.get_compositor_private();
- let windowTexture = mutterWindow.get_texture ();
- let [width, height] = windowTexture.get_size();
- let scale = Math.min(1.0, 128 / width, 128 / height);
-
- let clone = new Clutter.Clone ({ source: windowTexture, reactive: true, width: width * scale, height: height * scale });
- ap1.icon = ap1.app.create_icon_texture(128);
- ap1._iconBin.set_size(128,128);
- ap1._iconBin.child = clone;
-
- ap1.label.text = win.get_title();
- }
- }
- }
- if (ap1 != null) {
- ap1.cachedWindows = [win];
- this.addItem(ap1.actor, ap1.label);
- this.icons.push(ap1);
- }
- }
- },
-
- addSeparator: function () {
- this._separator=null;
- }
-});
const MODES = {
- all_thumbnails: AltTabPopupAllThumbnails,
- workspace_icons: AltTabPopupWorkspaceIcons,
+ all_thumbnails: AllThumbnails.AltTabPopupAllThumbnails,
+ workspace_icons: WorkspaceIcons.AltTabPopupWorkspaceIcons,
};
function doAltTab(display, screen, window, binding) {
@@ -505,7 +54,7 @@ function doAltTab(display, screen, window, binding) {
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
let constructor = MODES[behaviour];
- let popup = new constructor();
+ let popup = new constructor(settings);
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
popup.destroy();
}
diff --git a/extensions/alternate-tab/prefs.js b/extensions/alternate-tab/prefs.js
index f9591e4..6aeb4f1 100644
--- a/extensions/alternate-tab/prefs.js
+++ b/extensions/alternate-tab/prefs.js
@@ -32,10 +32,10 @@ thumbnails resembling the window itself."),
},
workspace_icons: {
name: N_("Workspace & Icons"),
- description: N_("This mode let's you switch between the applications of your current \
+ description: N_("This mode lets you switch between the applications of your current \
workspace and gives you additionally the option to switch to the last used \
application of your previous workspace. This is always the last symbol in \
-the list and is segregated by a separator/vertical line if available. \n\
+the list and is separated by a separator/vertical line if available. \n\
Every window is represented by its application icon."),
extra_widgets: [
{ label: N_("Move current selection to front before closing the popup"), key: SETTINGS_HIGHLIGHT_KEY }
diff --git a/extensions/alternate-tab/workspaceIcons.js b/extensions/alternate-tab/workspaceIcons.js
new file mode 100644
index 0000000..25ca7c5
--- /dev/null
+++ b/extensions/alternate-tab/workspaceIcons.js
@@ -0,0 +1,274 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const AltTab = imports.ui.altTab;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const Tweener = imports.ui.tweener;
+const WindowManager = imports.ui.windowManager;
+
+const Gettext = imports.gettext.domain('gnome-shell-extensions');
+const _ = Gettext.gettext;
+const N_ = function(e) { return e };
+
+const SETTINGS_HIGHLIGHT_SELECTED_KEY = 'highlight-selected';
+
+const AltTabPopupWorkspaceIcons = new Lang.Class({
+ Name: 'AlternateTab.AltTabPopupWorkspaceIcons',
+ Extends: AltTab.AltTabPopup,
+
+ _init: function(settings) {
+ this.parent();
+
+ this._settings = settings;
+ },
+
+ _windowActivated : function(thumbnailList, n) { },
+
+ show : function(backward, binding, mask) {
+ let appSys = Shell.AppSystem.get_default();
+ let apps = appSys.get_running ();
+
+ if (!apps.length)
+ return false;
+
+ if (!Main.pushModal(this.actor)) {
+ // Probably someone else has a pointer grab, try again with keyboard only
+ if (!Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED)) {
+ return false;
+ }
+ }
+ this._haveModal = true;
+ this._modifierMask = AltTab.primaryModifier(mask);
+
+ this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
+ this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
+
+ this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
+ this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
+
+ this._appSwitcher = new WindowSwitcher(apps, this);
+ this.actor.add_actor(this._appSwitcher.actor);
+ this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
+ this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
+
+ this._appIcons = this._appSwitcher.icons;
+
+ // Need to force an allocation so we can figure out whether we
+ // need to scroll when selecting
+ this.actor.opacity = 0;
+ this.actor.show();
+ this.actor.get_allocation_box();
+
+ this._highlight_selected = this._settings.get_boolean(SETTINGS_HIGHLIGHT_SELECTED_KEY);
+
+ // Make the initial selection
+ if (binding == 'switch_group') {
+ //see AltTab.AltTabPopup.show function
+ //cached windows are always of length one, so select first app and the window
+ //the direction doesn't matter, so ignore backward
+ this._select(0, 0);
+ } else if (binding == 'switch_group_backward') {
+ this._select(0, 0);
+ } else if (binding == 'switch_windows_backward') {
+ this._select(this._appIcons.length - 1);
+ } else if (this._appIcons.length == 1) {
+ this._select(0);
+ } else if (backward) {
+ this._select(this._appIcons.length - 1);
+ } else {
+ this._select(1);
+ }
+
+
+ // There's a race condition; if the user released Alt before
+ // we got the grab, then we won't be notified. (See
+ // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
+ // details.) So we check now. (Have to do this after updating
+ // selection.)
+ let [x, y, mods] = global.get_pointer();
+ if (!(mods & this._modifierMask)) {
+ this._finish();
+ return false;
+ }
+
+ // We delay showing the popup so that fast Alt+Tab users aren't
+ // disturbed by the popup briefly flashing.
+ this._initialDelayTimeoutId = Mainloop.timeout_add(AltTab.POPUP_DELAY_TIMEOUT,
+ Lang.bind(this, function () {
+ this.actor.opacity = 255;
+ this._initialDelayTimeoutId = 0;
+ }));
+
+ return true;
+ },
+
+ _select : function(app, window, forceAppFocus) {
+ if (app != this._currentApp || window == null) {
+ if (this._thumbnails)
+ this._destroyThumbnails();
+ }
+
+ if (this._thumbnailTimeoutId != 0) {
+ Mainloop.source_remove(this._thumbnailTimeoutId);
+ this._thumbnailTimeoutId = 0;
+ }
+
+ this._thumbnailsFocused = (window != null) && !forceAppFocus;
+
+ this._currentApp = app;
+ this._currentWindow = window ? window : -1;
+ this._appSwitcher.highlight(app, this._thumbnailsFocused);
+
+ if (window != null) {
+ if (!this._thumbnails)
+ this._createThumbnails();
+ this._currentWindow = window;
+ this._thumbnails.highlight(window, forceAppFocus);
+ } else if (this._appIcons[this._currentApp].cachedWindows.length > 1 &&
+ !forceAppFocus) {
+ this._thumbnailTimeoutId = Mainloop.timeout_add (
+ AltTab.THUMBNAIL_POPUP_TIME,
+ Lang.bind(this, this._timeoutPopupThumbnails));
+ }
+ if (this._highlight_selected) {
+ let current_app = this._appIcons[this._currentApp];
+ Main.activateWindow(current_app.cachedWindows[0]);
+ }
+ },
+
+ _finish : function() {
+ let app = this._appIcons[this._currentApp];
+ if (!app)
+ return;
+
+ /*
+ * We've to restore the original Z-depth and order of all windows.
+ *
+ * Gnome-shell doesn't give an option to change Z-depth without
+ * messing the window's user_time.
+ *
+ * Pointless if the popup wasn't showed.
+ */
+ if (this._highlight_selected && this.actor.opacity == 255) {
+ for (let i = this._appIcons.length - 2; i >= 0; i--) {
+ let app_walker = this._appIcons[i];
+ Main.activateWindow(app_walker.cachedWindows[0], global.get_current_time() - i - 1);
+ }
+ }
+
+ Main.activateWindow(app.cachedWindows[0]);
+ this.destroy();
+ }
+
+});
+
+const AppIcon = new Lang.Class({
+ Name: 'AlternateTab.AppIcon',
+ Extends: AltTab.AppIcon,
+
+ _init: function(app, window) {
+ this.app = app;
+
+ this.cachedWindows = [];
+ this.cachedWindows.push(window);
+
+ this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
+ vertical: true });
+ this.icon = null;
+ this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
+
+ this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
+
+ let title = window.get_title();
+ if (title) {
+ this.label = new St.Label({ text: title });
+ let bin = new St.Bin({ x_align: St.Align.MIDDLE });
+ bin.add_actor(this.label);
+ this.actor.add(bin);
+ }
+ else {
+ this.label = new St.Label({ text: this.app.get_name() });
+ this.actor.add(this.label, { x_fill: false });
+ }
+ }
+});
+
+const WindowSwitcher = new Lang.Class({
+ Name: 'AlternateTab.WindowSwitcher',
+ Extends: AltTab.AppSwitcher,
+
+ _init : function(apps, altTabPopup) {
+ // Horrible HACK!
+ // We inherit from AltTab.AppSwitcher, but only chain up to
+ // AltTab.SwitcherList._init, to bypass AltTab.AppSwitcher._init
+ AltTab.SwitcherList.prototype._init.call(this, true);
+
+ // Construct the AppIcons, sort by time, add to the popup
+ let activeWorkspace = global.screen.get_active_workspace();
+ let workspaceIcons = [];
+ let otherIcons = [];
+ for (let i = 0; i < apps.length; i++) {
+ // Cache the window list now; we don't handle dynamic changes here,
+ // and we don't want to be continually retrieving it
+ let windows = apps[i].get_windows();
+
+ for(let j = 0; j < windows.length; j++) {
+ let appIcon = new AppIcon(apps[i], windows[j]);
+ if (this._isWindowOnWorkspace(windows[j], activeWorkspace)) {
+ workspaceIcons.push(appIcon);
+ }
+ else {
+ otherIcons.push(appIcon);
+ }
+ }
+ }
+
+ workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
+ otherIcons.sort(Lang.bind(this, this._sortAppIcon));
+
+ if(otherIcons.length > 0) {
+ let mostRecentOtherIcon = otherIcons[0];
+ otherIcons = [];
+ otherIcons.push(mostRecentOtherIcon);
+ }
+
+ this.icons = [];
+ this._arrows = [];
+ for (let i = 0; i < workspaceIcons.length; i++)
+ this._addIcon(workspaceIcons[i]);
+ if (workspaceIcons.length > 0 && otherIcons.length > 0)
+ this.addSeparator();
+ for (let i = 0; i < otherIcons.length; i++)
+ this._addIcon(otherIcons[i]);
+
+ this._curApp = -1;
+ this._iconSize = 0;
+ this._altTabPopup = altTabPopup;
+ this._mouseTimeOutId = 0;
+ },
+
+
+ _isWindowOnWorkspace: function(w, workspace) {
+ if (w.get_workspace() == workspace)
+ return true;
+ return false;
+ },
+
+ _sortAppIcon : function(appIcon1, appIcon2) {
+ let t1 = appIcon1.cachedWindows[0].get_user_time();
+ let t2 = appIcon2.cachedWindows[0].get_user_time();
+ if (t2 > t1) return 1;
+ else return -1;
+ }
+});
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]