[gnome-shell-extensions] alternate-tab: more alternative implementations
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell-extensions] alternate-tab: more alternative implementations
- Date: Mon, 1 Aug 2011 14:02:05 +0000 (UTC)
commit 002b1b5d07affda9c0d03c0c8baa25aa416a63f7
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Mon Aug 1 15:36:45 2011 +0200
alternate-tab: more alternative implementations
Now alternate-tab shows a dialog on first usage, allowing to choose
between native behavior (app icons from all worskpaces), app icons
from current workspace only and window thumbnails only.
Patch written by Joachim Bargsten <dev bargsten org>
extensions/alternate-tab/Makefile.am | 11 +
extensions/alternate-tab/extension.js | 344 +++++++++++++++++++-
extensions/alternate-tab/metadata.json.in | 2 +-
...e.shell.extensions.alternate-tab.gschema.xml.in | 19 +
po/POTFILES.in | 1 +
5 files changed, 365 insertions(+), 12 deletions(-)
---
diff --git a/extensions/alternate-tab/Makefile.am b/extensions/alternate-tab/Makefile.am
index b8fde76..2e3930e 100644
--- a/extensions/alternate-tab/Makefile.am
+++ b/extensions/alternate-tab/Makefile.am
@@ -1,3 +1,14 @@
EXTENSION_ID = alternate-tab
include ../../extension.mk
+
+gschemas_in = org.gnome.shell.extensions.alternate-tab.gschema.xml.in
+
+ INTLTOOL_XML_NOMERGE_RULE@
+
+gsettings_SCHEMAS = $(gschemas_in:.xml.in=.xml)
+
+ GSETTINGS_RULES@
+
+CLEANFILES += $(gschemas_in:.xml.in=.valid) $(gsettings_SCHEMAS)
+EXTRA_DIST += $(gschemas_in)
diff --git a/extensions/alternate-tab/extension.js b/extensions/alternate-tab/extension.js
index 0e158c4..b71f17a 100644
--- a/extensions/alternate-tab/extension.js
+++ b/extensions/alternate-tab/extension.js
@@ -1,16 +1,324 @@
/* -*- 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 AltTab = imports.ui.altTab;
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 Main = imports.ui.main;
const Mainloop = imports.mainloop;
-const Shell= imports.gi.Shell;
+const ModalDialog = imports.ui.modalDialog;
+const Shell = imports.gi.Shell;
const St = imports.gi.St;
-
-const AltTab=imports.ui.altTab;
-const Main = imports.ui.main;
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 POPUP_FADE_TIME = 0.1; // seconds
+
+const SETTINGS_SCHEMA = 'org.gnome.shell.extensions.alternate-tab';
+const SETTINGS_BEHAVIOUR_KEY = 'behaviour';
+const SETTINGS_FIRST_TIME_KEY = 'first-time';
+
+const MODES = {
+ native: function() {
+ Main.wm._startAppSwitcher();
+ },
+ all_thumbnails: function() {
+ new AltTabPopup2();
+ },
+ workspace_icons: function() {
+ new AltTabPopupW().show();
+ }
+};
+
+const MESSAGE = N_("This is the first time you use the Alternate Tab extension. \n\
+Please choose your preferred behaviour:\n\
+\n\
+All & Thumbnails:\n\
+ This mode presents all applications from all workspaces in one selection \n\
+ list. Instead of using the application icon of every window, it uses small \n\
+ thumbnails resembling the window itself. \n\
+\n\
+Workspace & Icons:\n\
+ This mode let's you switch between the applications of your current \n\
+ workspace and gives you additionally the option to switch to the last used \n\
+ application of your previous workspace. This is always the last symbol in \n\
+ the list and is segregated by a separator/vertical line if available. \n\
+ Every window is represented by its application icon. \n\
+\n\
+Native:\n\
+ This mode is the native GNOME 3 behaviour or in other words: Clicking \n\
+ native switches the Alternate Tab extension off. \n\
+");
+
+function AltTabPopupW() {
+ this._init();
+}
+
+AltTabPopupW.prototype = {
+ __proto__ : AltTab.AltTabPopup.prototype,
+
+ show : function(backward, switch_group) {
+ let tracker = Shell.WindowTracker.get_default();
+ let apps = tracker.get_running_apps ('');
+
+ if (!apps.length)
+ return false;
+
+ if (!Main.pushModal(this.actor))
+ return false;
+ this._haveModal = true;
+
+ 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;
+
+ // Make the initial selection
+ if (switch_group) {
+ if (backward) {
+ this._select(0, this._appIcons[0].cachedWindows.length - 1);
+ } else {
+ if (this._appIcons[0].cachedWindows.length > 1)
+ this._select(0, 1);
+ else
+ this._select(0, 0);
+ }
+ } 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 & Gdk.ModifierType.MOD1_MASK)) {
+ this._finish();
+ return false;
+ }
+
+ this.actor.opacity = 0;
+ this.actor.show();
+ Tweener.addTween(this.actor,
+ { opacity: 255,
+ time: POPUP_FADE_TIME,
+ transition: 'easeOutQuad'
+ });
+
+ return true;
+ },
+
+
+ _finish : function() {
+ let app = this._appIcons[this._currentApp];
+ Main.activateWindow(app.cachedWindows[0]);
+ this.destroy();
+ }
+
+};
+
+function AppIcon(app, window) {
+ this._init(app, window);
+}
+
+AppIcon.prototype = {
+ __proto__ : AltTab.AppIcon.prototype,
+
+ _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 });
+ }
+ }
+};
+
+function WindowSwitcher(apps, altTabPopup) {
+ this._init(apps, altTabPopup);
+}
+
+WindowSwitcher.prototype = {
+ __proto__ : AltTab.AppSwitcher.prototype,
+
+ _init : function(apps, altTabPopup) {
+ 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;
+ }
+};
+
+function AltTabSettingsDialog() {
+ this._init();
+}
+
+AltTabSettingsDialog.prototype = {
+ __proto__: ModalDialog.ModalDialog.prototype,
+
+ _init : function() {
+ ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: null });
+
+ let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
+ vertical: false });
+ this.contentLayout.add(mainContentBox,
+ { x_fill: true,
+ y_fill: true });
+
+ let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
+ vertical: true });
+ mainContentBox.add(messageBox,
+ { y_align: St.Align.START });
+
+ this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
+ text: _("Alt Tab Behaviour") });
+
+ messageBox.add(this._subjectLabel,
+ { y_fill: false,
+ y_align: St.Align.START });
+
+ this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
+ text: Gettext.gettext(MESSAGE) });
+
+ messageBox.add(this._descriptionLabel,
+ { y_fill: true,
+ y_align: St.Align.START });
+
+
+ this.setButtons([
+ {
+ label: _("All & Thumbnails"),
+ action: Lang.bind(this, function() {
+ this.setBehaviour('all_thumbnails');
+ this.close();
+ })
+ },
+ {
+ label: _("Workspace & Icons"),
+ action: Lang.bind(this, function() {
+ this.setBehaviour('workspace_icons');
+ this.close();
+ })
+ },
+ {
+ label: _("Native"),
+ action: Lang.bind(this, function() {
+ this.setBehaviour('native');
+ this.close();
+ })
+ },
+ {
+ label: _("Cancel"),
+ action: Lang.bind(this, function() {
+ this.close();
+ }),
+ key: Clutter.Escape
+ }
+ ]);
+ },
+
+ setBehaviour: function(behaviour) {
+ this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
+ this._settings.set_string(SETTINGS_BEHAVIOUR_KEY, behaviour);
+ this._settings.set_boolean(SETTINGS_FIRST_TIME_KEY, false);
+ }
+};
+
function AltTabPopup2() {
this._init();
}
@@ -41,6 +349,7 @@ AltTabPopup2.prototype = {
this.show();
Main.uiGroup.add_actor(this.actor);
+ this._select(0);
},
show : function(backward) {
@@ -54,9 +363,7 @@ AltTabPopup2.prototype = {
for (let w = windows.length-1; w >= 0; w--) {
let win = windows[w].get_meta_window();
- if (win.window_type == 0) {
normal_windows.push(win);
- }
}
normal_windows.sort(Lang.bind(this, this._sortWindows));
@@ -74,8 +381,10 @@ AltTabPopup2.prototype = {
ap1 = new AltTab.AppIcon(apps[i]);
}
}
- ap1.cachedWindows = [win];
- appIcons.push(ap1);
+ if (ap1 != null) {
+ ap1.cachedWindows = [win];
+ appIcons.push(ap1);
+ }
}
if (!windows.length)
@@ -93,13 +402,13 @@ AltTabPopup2.prototype = {
this._appSwitcher = new WindowList(windows);
this._appSwitcher._altTabPopup=this;
- this._appSwitcher.highlight(0,false);
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;
+
return true
},
@@ -220,8 +529,10 @@ WindowList.prototype = {
}
}
}
+ if (ap1 != null) {
ap1.cachedWindows = [win];
this._addIcon(ap1);
+ }
}
},
@@ -230,8 +541,19 @@ WindowList.prototype = {
}
};
-function main() {
+function main(metadata) {
+ imports.gettext.bindtextdomain('gnome-shell-extensions', metadata.localedir);
+
Main.wm.setKeybindingHandler('switch_windows', function() {
- let alpopup = new AltTabPopup2();
+ let settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
+
+ if(settings.get_boolean(SETTINGS_FIRST_TIME_KEY)) {
+ new AltTabSettingsDialog().open();
+ } else {
+ let behaviour = settings.get_string(SETTINGS_BEHAVIOUR_KEY);
+ if(behaviour in MODES) {
+ MODES[behaviour]();
+ }
+ }
});
}
diff --git a/extensions/alternate-tab/metadata.json.in b/extensions/alternate-tab/metadata.json.in
index aae37e2..d811b54 100644
--- a/extensions/alternate-tab/metadata.json.in
+++ b/extensions/alternate-tab/metadata.json.in
@@ -2,7 +2,7 @@
"uuid": "@uuid@",
"name": "AlternateTab",
"description": "A replacement for Alt-Tab, allows to cycle between windows and does not group by application",
-"original-author": "thomas bouffon gmail com",
+"original-authors": [ "jw bargsten org", "thomas bouffon gmail com" ],
"shell-version": [ "@shell_current@" ],
"localedir": "@LOCALEDIR@",
"url": "@url@"
diff --git a/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in b/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
new file mode 100644
index 0000000..7dbbca7
--- /dev/null
+++ b/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
@@ -0,0 +1,19 @@
+<schemalist gettext-domain="gnome-shell-extensions">
+ <enum id="org.gnome.shell.extensions.alternate-tab.BehaviourMode">
+ <value value="0" nick="native"/>
+ <value value="1" nick="all_thumbnails"/>
+ <value value="2" nick="workspace_icons"/>
+ </enum>
+ <schema id="org.gnome.shell.extensions.alternate-tab" path="/org/gnome/shell/extensions/alternate-tab/">
+ <key name="behaviour" enum="org.gnome.shell.extensions.alternate-tab.BehaviourMode">
+ <default>'native'</default>
+ <_summary>The alt tab behaviour.</_summary>
+ <_description>Sets the Alt-Tab behaviour. Possible values are: native, all_thumbnails and workspace_icons.</_description>
+ </key>
+ <key type="b" name="first-time">
+ <default>true</default>
+ <_summary>Indicates if Alternate Tab is newly installed</_summary>
+ <_description>Ask the user for a default behaviour if true.</_description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 74678af..d1be54b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,6 @@
extensions/alternative-status-menu/extension.js
extensions/alternate-tab/extension.js
+extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
extensions/auto-move-windows/extension.js
extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml.in
extensions/dock/extension.js
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]