[gnome-shell-extensions] alternate-tab: Re-use window-switcher in gnome-shell



commit 61f86a3f293c8f552318172ce32af42c6f31926a
Author: Florian MÃllner <fmuellner gnome org>
Date:   Wed Dec 5 17:48:48 2012 +0100

    alternate-tab: Re-use window-switcher in gnome-shell
    
    gnome-shell now provides its own traditional window switcher using
    a dedicated keybinding, so update the extension to just take over
    the default application-based switchers as well.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=689724

 extensions/alternate-tab/Makefile.am               |    1 -
 extensions/alternate-tab/extension.js              |  461 ++------------------
 ...e.shell.extensions.alternate-tab.gschema.xml.in |   26 --
 extensions/alternate-tab/prefs.js                  |   14 +-
 po/POTFILES.in                                     |    2 -
 5 files changed, 42 insertions(+), 462 deletions(-)
---
diff --git a/extensions/alternate-tab/Makefile.am b/extensions/alternate-tab/Makefile.am
index 1f35392..56bc74b 100644
--- a/extensions/alternate-tab/Makefile.am
+++ b/extensions/alternate-tab/Makefile.am
@@ -3,4 +3,3 @@ EXTENSION_ID = alternate-tab
 EXTRA_MODULES = prefs.js
 
 include ../../extension.mk
-include ../../settings.mk
diff --git a/extensions/alternate-tab/extension.js b/extensions/alternate-tab/extension.js
index bfd3015..c9e0bbf 100644
--- a/extensions/alternate-tab/extension.js
+++ b/extensions/alternate-tab/extension.js
@@ -1,448 +1,63 @@
 /* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
 
-/* most of the code is borrowed from
- * > js/ui/altTab.js <
- * of the gnome-shell source code
- */
-
 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 ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
-const Convenience = Me.imports.convenience;
 
-let settings;
+let injections = {};
 
-const AppIconMode = {
-    THUMBNAIL_ONLY: 1,
-    APP_ICON_ONLY: 2,
-    BOTH: 3,
-};
-
-const SETTINGS_APP_ICON_MODE = 'app-icon-mode';
-const SETTINGS_CURRENT_WORKSPACE_ONLY = 'current-workspace-only';
-
-function mod(a, b) {
-    return ((a+b) % b);
+function init(metadata) {
 }
 
-const AltTabPopup = new Lang.Class({
-    Name: 'AlternateTab.AltTabPopup',
-
-    _init : function(settings) {
-	this._settings = settings;
-
-        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._haveModal = false;
-
-        this._currentWindow = 0;
-        this._motionTimeoutId = 0;
-        this._initialDelayTimeoutId = 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) {
-	let windows;
-
-	if (!settings.get_boolean(SETTINGS_CURRENT_WORKSPACE_ONLY)) {
-	    // 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
-	    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();
-	    });
-	} else {
-	    windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, global.screen,
-						  global.screen.get_active_workspace());
-	}
-
-        // Filter away attached modal dialogs (switch to their parents instead)
-        windows = windows.filter(function(win) { return !win.is_attached_dialog(); });
-
-        if (windows.length == 0)
-            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._settings);
-        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));
+function setKeybinding(name, func) {
+    Main.wm.setCustomKeybindingHandler(name, Main.KeybindingMode.NORMAL, func);
+}
 
-        // make the initial selection
-	this._currentWindow = 0;
-        if (backward)
-            this._select(this._previousWindow());
+function enable() {
+    injections['_initialSelection'] = AltTab.WindowSwitcherPopup.prototype._initialSelection;
+    AltTab.WindowSwitcherPopup.prototype._initialSelection = function(backward, binding) {
+        if (binding == 'switch-windows-backward' ||
+            binding == 'switch-applications-backward' ||
+            binding == 'switch-group-backward' || backward)
+            this._select(this._items.length - 1);
+        else if (this._items.length == 1)
+            this._select(0);
         else
-            this._select(this._nextWindow());
-
-        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());
+            this._select(1);
+    };
+    injections['_keyPressHandler'] = AltTab.WindowSwitcherPopup.prototype._keyPressHandler;
+    AltTab.WindowSwitcherPopup.prototype._keyPressHandler = function(keysym, backwards, action) {
+        if (action == Meta.KeyBindingAction.SWITCH_WINDOWS ||
+            action == Meta.KeyBindingAction.SWITCH_APPLICATIONS ||
+            action == Meta.KeyBindingAction.SWITCH_GROUP) {
+            this._select(backwards ? this._previous() : this._next());
         } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD ||
-		  action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
-            this._select(this._previousWindow());
+                   action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD ||
+                   action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
+            this._select(this._previous());
         } else {
-	    if (keysym == Clutter.Left)
-                this._select(this._previousWindow());
+            if (keysym == Clutter.Left)
+                this._select(this._previous());
             else if (keysym == Clutter.Right)
-                this._select(this._nextWindow());
+                this._select(this._next());
         }
+    };
 
-        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, this._finishDestroy),
-                             });
-        } else
-            this._finishDestroy();
-    },
-
-    _finishDestroy : function() {
-        if (this._motionTimeoutId != 0) {
-            Mainloop.source_remove(this._motionTimeoutId);
-            this._motionTimeoutId = 0;
-        }
-
-        if (this._initialDelayTimeoutId != 0) {
-            Mainloop.source_remove(this._initialDelayTimeoutId);
-            this._initialDelayTimeoutId = 0;
-        }
-
-        this.actor.destroy();
-    },
-
-    _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: 'AlternateTab.WindowIcon',
-
-    _init: function(window, settings) {
-	this.window = window;
-	this._settings = settings;
-
-        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
-                                        vertical: true });
-        this.icon = null;
-        this._iconBin = new St.Widget({ layout_manager: new Clutter.BinLayout() });
-
-        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 });
-
-	let tracker = Shell.WindowTracker.get_default();
-	this.app = tracker.get_window_app(window);
-
-        let mutterWindow = this.window.get_compositor_private();
-        let windowTexture = mutterWindow.get_texture();
-        let [width, height] = windowTexture.get_size();
-        let scale, size;
-
-	this._iconBin.destroy_all_children();
-
-	switch (this._settings.get_enum(SETTINGS_APP_ICON_MODE)) {
-	case AppIconMode.THUMBNAIL_ONLY:
-	    scale = Math.min(1.0, 128 / width, 128 / height);
-	    size = 128;
-            this.clone = new Clutter.Clone({ source: windowTexture,
-					     width: width * scale,
-					     height: height * scale,
-					     x_align: Clutter.ActorAlign.CENTER,
-					     y_align: Clutter.ActorAlign.CENTER,
-					     // usual hack for the usual bug in ClutterBinLayout...
-				             x_expand: true,
-					     y_expand: true });
-	    this._iconBin.add_actor(this.clone);
-	    break;
-
-	case AppIconMode.BOTH:
-	    scale = Math.min(1.0, 128 / width, 128 / height);
-	    size = 128;
-            this.clone = new Clutter.Clone({ source: windowTexture,
-					     width: width * scale,
-					     height: height * scale,
-					     x_align: Clutter.ActorAlign.CENTER,
-					     y_align: Clutter.ActorAlign.CENTER,
-					     // usual hack for the usual bug in ClutterBinLayout...
-				             x_expand: true,
-					     y_expand: true });
-	    this._iconBin.add_actor(this.clone);
-
-	    if (this.app) {
-		this.appIcon = this.app.create_icon_texture(size / 2);
-		this.appIcon.x_expand = this.appIcon.y_expand = true;
-		this.appIcon.x_align = Clutter.ActorAlign.END;
-		this.appIcon.y_align = Clutter.ActorAlign.END;
-		this._iconBin.add_actor(this.appIcon);
-	    }
-	    break;
-
-	case AppIconMode.APP_ICON_ONLY:
-	    size = 96;
-	    if (this.app) {
-		this.appIcon = this.app.create_icon_texture(size);
-		this.appIcon.x_expand = this.appIcon.y_expand = true;
-	    } else {
-		this.appIcon = new St.Icon({ icon_name: 'icon-missing',
-					     icon_size: size,
-					     x_expand: true,
-					     y_expand: true });
-	    }
-	    this._iconBin.add_actor(this.appIcon);
-	}
-
-        this._iconBin.set_size(size, size);
-    }
-});
-
-const WindowList = new Lang.Class({
-    Name: 'AlternateTab.WindowList',
-    Extends: AltTab.SwitcherList,
-
-    _init : function(windows, settings) {
-        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, settings);
-
-            this.addItem(icon.actor, icon.label);
-            this.icons.push(icon);
-	}
-    }
-});
-
-function doAltTab(display, screen, window, binding) {
-    if (Main.wm._workspaceSwitcherPopup)
-        Main.wm._workspaceSwitcherPopup.actor.hide();
-
-    let modifiers = binding.get_modifiers()
-    let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
-
-    let popup = new AltTabPopup(settings);
-    if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
-        popup.destroy();
-}
-
-function init(metadata) {
-    Convenience.initTranslations();
-    settings = Convenience.getSettings();
-}
-
-function setKeybinding(name, func) {
-    Main.wm.setCustomKeybindingHandler(name, Main.KeybindingMode.NORMAL, func);
-}
-
-function enable() {
-    setKeybinding('switch-windows', doAltTab);
-    setKeybinding('switch-group', doAltTab);
-    setKeybinding('switch-windows-backward', doAltTab);
-    setKeybinding('switch-group-backward', doAltTab);
+    setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
+    setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
+    setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
+    setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher));
 }
 
 function disable() {
-    setKeybinding('switch-windows', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
+    setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
     setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
-    setKeybinding('switch-windows-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
+    setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
     setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher));
+
+    for (prop in injections)
+        AltTab.WindowSwitcherPopup.prototype[prop] = injections[prop];
 }
diff --git a/extensions/alternate-tab/prefs.js b/extensions/alternate-tab/prefs.js
index 6f1856b..b6ec54a 100644
--- a/extensions/alternate-tab/prefs.js
+++ b/extensions/alternate-tab/prefs.js
@@ -1,11 +1,5 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
-/* most of the code is borrowed from
- * > js/ui/altTab.js <
- * of the gnome-shell source code
- */
-
-const Gdk = imports.gi.Gdk;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
 const GObject = imports.gi.GObject;
@@ -23,9 +17,9 @@ const SETTINGS_APP_ICON_MODE = 'app-icon-mode';
 const SETTINGS_CURRENT_WORKSPACE_ONLY = 'current-workspace-only';
 
 const MODES = {
-    thumbnail_only: N_("Thumbnail only"),
-    app_icon_only: N_("Application icon only"),
-    both: N_("Thumbnail and application icon"),
+    'thumbnail-only': N_("Thumbnail only"),
+    'app-icon-only': N_("Application icon only"),
+    'both': N_("Thumbnail and application icon"),
 };
 
 const AltTabSettingsWidget = new GObject.Class({
@@ -38,7 +32,7 @@ const AltTabSettingsWidget = new GObject.Class({
         this.margin = 10;
 	this.orientation = Gtk.Orientation.VERTICAL;
 
-        this._settings = Convenience.getSettings();
+        this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 
         let presentLabel = _("Present windows as");
         this.add(new Gtk.Label({ label: presentLabel, sensitive: true,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 01b6d6d..6c752d6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,8 +1,6 @@
 data/gnome-classic.desktop.in
 data/gnome-classic.session.desktop.in.in
 data/gnome-shell-classic.desktop.in.in
-extensions/alternate-tab/extension.js
-extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
 extensions/alternate-tab/prefs.js
 extensions/alternative-status-menu/extension.js
 extensions/alternative-status-menu/org.gnome.shell.extensions.alternative-status-menu.gschema.xml.in



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]