[gnome-shell-extensions/wip/window-list: 13/13] WIP: Start working on grouping
- From: Florian MÃllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell-extensions/wip/window-list: 13/13] WIP: Start working on grouping
- Date: Fri, 25 Jan 2013 02:15:08 +0000 (UTC)
commit a0e37095bfc5edd595f9f0d025c3e6aa2ce43a77
Author: Florian MÃllner <fmuellner gnome org>
Date: Thu Jan 24 20:13:52 2013 +0100
WIP: Start working on grouping
extensions/window-list/Makefile.am | 1 +
extensions/window-list/extension.js | 229 +++++++++++++++++++-
...ome.shell.extensions.window-list.gschema.xml.in | 19 ++
po/POTFILES.in | 1 +
4 files changed, 247 insertions(+), 3 deletions(-)
---
diff --git a/extensions/window-list/Makefile.am b/extensions/window-list/Makefile.am
index 48a1028..42470a9 100644
--- a/extensions/window-list/Makefile.am
+++ b/extensions/window-list/Makefile.am
@@ -1,3 +1,4 @@
EXTENSION_ID = window-list
include ../../extension.mk
+include ../../settings.mk
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index 14935bb..9dcd3fc 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -6,6 +6,17 @@ const St = imports.gi.St;
const Lang = imports.lang;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
+const PopupMenu = imports.ui.popupMenu;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
+
+const GroupingMode = {
+ NEVER: 0,
+ AUTO: 1, /* TODO: implement */
+ ALWAYS: 2
+};
const WindowTitle = new Lang.Class({
Name: 'WindowTitle',
@@ -127,6 +138,156 @@ const WindowButton = new Lang.Class({
});
+const AppButton = new Lang.Class({
+ Name: 'AppButton',
+
+ _init: function(app) {
+ this.app = app;
+
+ let stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+ this.actor = new St.Button({ style_class: 'window-button',
+ x_fill: true,
+ child: stack });
+ this.actor._delegate = this;
+
+ this.actor.connect('allocation-changed',
+ Lang.bind(this, this._updateIconGeometry));
+
+ this._singleWindowTitle = new St.Bin({ x_expand: true,
+ x_align: St.Align.START });
+ stack.add_actor(this._singleWindowTitle);
+
+ this._multiWindowTitle = new St.BoxLayout({ x_expand: true });
+ stack.add_actor(this._multiWindowTitle);
+
+ let icon = new St.Bin({ style_class: 'window-button-icon',
+ child: app.create_icon_texture(24) });
+ this._multiWindowTitle.add(icon);
+ this._multiWindowTitle.add(new St.Label({ text: app.get_name() }));
+
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menu = new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.BOTTOM);
+ this._menu.actor.hide();
+ this._menu.connect('activate', Lang.bind(this, this._onMenuActivate));
+ this._menuManager.addMenu(this._menu);
+ Main.uiGroup.add_actor(this._menu.actor);
+
+ this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+ this._switchWorkspaceId =
+ global.window_manager.connect('switch-workspace',
+ Lang.bind(this, this._updateVisibility));
+ this._updateVisibility();
+
+ this._windowsChangedId =
+ this.app.connect('windows-changed',
+ Lang.bind(this, this._windowsChanged));
+ this._windowsChanged();
+
+ this._windowTracker = Shell.WindowTracker.get_default();
+ this._notifyFocusId =
+ this._windowTracker.connect('notify::focus-app',
+ Lang.bind(this, this._updateStyle));
+ this._updateStyle();
+ },
+
+ _updateVisibility: function() {
+ let workspace = global.screen.get_active_workspace();
+ this.actor.visible = this.app.is_on_workspace(workspace);
+ },
+
+ _updateStyle: function() {
+ if (this._windowTracker.focus_app == this.app)
+ this.actor.add_style_class_name('focused');
+ else
+ this.actor.remove_style_class_name('focused');
+ },
+
+ _updateIconGeometry: function() {
+ let windows = this.app.get_windows();
+ if (!windows[0].set_icon_geometry)
+ return;
+
+ let [x, y] = this.actor.get_transformed_position();
+ let [w, h] = this.actor.get_transformed_size();
+
+ let rect = new Meta.Rectangle();
+ rect.x = x + Math.floor(w / 2);
+ rect.y = y + Math.floor(h / 2);
+ rect.width = w;
+ rect.height = h;
+
+ windows.forEach(function(w) {
+ w.set_icon_geometry(rect);
+ });
+ },
+
+
+ _getWindowList: function() {
+ let workspace = global.screen.get_active_workspace();
+ return this.app.get_windows().filter(function(win) {
+ return win.located_on_workspace(workspace);
+ });
+ },
+
+ _windowsChanged: function() {
+ let windows = this._getWindowList();
+ this._singleWindowTitle.visible = windows.length == 1;
+ this._multiWindowTitle.visible = !this._singleWindowTitle.visible;
+
+ if (this._singleWindowTitle.visible) {
+ if (!this._windowTitle) {
+ this._windowTitle = new WindowTitle(windows[0]);
+ this._singleWindowTitle.child = this._windowTitle.actor;
+ }
+ } else {
+ if (this._windowTitle) {
+ this._singleWindowTitle.child = null;
+ this._windowTitle = null;
+ }
+ }
+ },
+
+ _onClicked: function() {
+ if (this._menu.isOpen) {
+ this._menu.close();
+ return;
+ }
+
+ let windows = this._getWindowList();
+ if (windows.length == 1) {
+ if (global.display.focus_window == windows[0])
+ windows[0].minimize();
+ else
+ windows[0].activate(global.get_current_time());
+ } else {
+ this._menu.removeAll();
+
+ for (let i = 0; i < windows.length; i++) {
+ let windowTitle = new WindowTitle(windows[i]);
+ let item = new PopupMenu.PopupBaseMenuItem();
+ item.addActor(windowTitle.actor);
+ item._window = windows[i];
+ this._menu.addMenuItem(item);
+ }
+ this._menu.open();
+ }
+ },
+
+ _onMenuActivate: function(menu, child) {
+ child._window.activate(global.get_current_time());
+ },
+
+ _onDestroy: function() {
+ global.window_manager.disconnect(this._switchWorkspaceId);
+ this._windowTracker.disconnect(this._notifyFocusId);
+ this.app.disconnect(this._windowsChangedId);
+ this._menu.actor.destroy();
+ }
+});
+
+
const TrayButton = new Lang.Class({
Name: 'TrayButton',
@@ -213,6 +374,11 @@ const WindowList = new Lang.Class({
Main.layoutManager.addChrome(this.actor, { affectsStruts: true,
trackFullscreen: true });
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appStateChangedId =
+ this._appSystem.connect('app-state-changed',
+ Lang.bind(this, this._onAppStateChanged));
+
this._monitorsChangedId =
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._updatePosition));
@@ -244,9 +410,29 @@ const WindowList = new Lang.Class({
this._updateKeyboardAnchor();
}));
- let windows = Meta.get_window_actors(global.screen);
- for (let i = 0; i < windows.length; i++)
- this._onWindowAdded(null, windows[i].metaWindow);
+ this._settings = Convenience.getSettings();
+ this._settings.connect('changed::grouping-mode',
+ Lang.bind(this, this._groupingModeChanged));
+ this._groupingModeChanged();
+ },
+
+ _groupingModeChanged: function() {
+ this._groupingMode = this._settings.get_enum('grouping-mode');
+ this._populateWindowList();
+ },
+
+ _populateWindowList: function() {
+ this._windowList.destroy_all_children();
+
+ if (this._groupingMode == GroupingMode.NEVER) {
+ let windows = Meta.get_window_actors(global.screen);
+ for (let i = 0; i < windows.length; i++)
+ this._onWindowAdded(null, windows[i].metaWindow);
+ } else {
+ let apps = this._appSystem.get_running();
+ for (let i = 0; i < apps.length; i++)
+ this._addApp(apps[i]);
+ }
},
_updatePosition: function() {
@@ -263,10 +449,41 @@ const WindowList = new Lang.Class({
Main.keyboard.actor.anchor_y = anchorY;
},
+ _onAppStateChanged: function(appSys, app) {
+ if (this._groupingMode != GroupingMode.ALWAYS)
+ return;
+
+ if (app.state == Shell.AppState.RUNNING)
+ this._addApp(app);
+ else if (app.state == Shell.AppState.STOPPED)
+ this._removeApp(app);
+ },
+
+ _addApp: function(app) {
+ let button = new AppButton(app);
+ this._windowList.layout_manager.pack(button.actor,
+ true, true, true,
+ Clutter.BoxAlignment.START,
+ Clutter.BoxAlignment.START);
+ },
+
+ _removeApp: function(app) {
+ let children = this._windowList.get_children();
+ for (let i = 0; i < children.length; i++) {
+ if (children[i]._delegate.app == app) {
+ children[i].destroy();
+ return;
+ }
+ }
+ },
+
_onWindowAdded: function(ws, win) {
if (!Shell.WindowTracker.get_default().is_window_interesting(win))
return;
+ if (this._groupingMode != GroupingMode.NEVER)
+ return;
+
let button = new WindowButton(win);
this._windowList.layout_manager.pack(button.actor,
true, true, true,
@@ -275,6 +492,9 @@ const WindowList = new Lang.Class({
},
_onWindowRemoved: function(ws, win) {
+ if (this._groupingMode != GroupingMode.NEVER)
+ return;
+
let children = this._windowList.get_children();
for (let i = 0; i < children.length; i++) {
if (children[i]._delegate.metaWindow == win) {
@@ -302,6 +522,9 @@ const WindowList = new Lang.Class({
},
_onDestroy: function() {
+ this._appSystem.disconnect(this._appStateChangedId);
+ this._appStateChangedId = 0;
+
Main.layoutManager.disconnect(this._monitorsChangedId);
this._monitorsChangedId = 0;
diff --git a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
new file mode 100644
index 0000000..d5bbdf4
--- /dev/null
+++ b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
@@ -0,0 +1,19 @@
+<schemalist gettext-domain="gnome-shell-extensions">
+ <enum id="org.gnome.shell.extensions.window-list.GroupingMode">
+ <value value="0" nick="never"/>
+ <value value="1" nick="auto"/>
+ <value value="2" nick="always"/>
+ </enum>
+ <schema id="org.gnome.shell.extensions.window-list"
+ path="/org/gnome/shell/extensions/window-list/">
+ <key name="grouping-mode"
+ enum="org.gnome.shell.extensions.window-list.GroupingMode">
+ <default>'never'</default>
+ <_summary>When to group windows</_summary>
+ <_description>
+ Decides when to group windows from the same application on the
+ window list. Possible values are "never", "auto" and "always".
+ </_description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6c752d6..8624edf 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,7 @@ extensions/places-menu/placeDisplay.js
extensions/systemMonitor/extension.js
extensions/user-theme/extension.js
extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in
+extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in
extensions/windowsNavigator/extension.js
extensions/workspace-indicator/extension.js
extensions/workspace-indicator/prefs.js
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]