[gnome-shell-extensions/wip/window-list: 1/13] window-list: New extension



commit f93e896ced8479384decb621f3fe8a1d3bc5de11
Author: Florian MÃllner <fmuellner gnome org>
Date:   Fri Nov 9 22:43:30 2012 +0100

    window-list: New extension

 configure.ac                            |    5 +-
 extensions/window-list/Makefile.am      |    3 +
 extensions/window-list/extension.js     |  270 +++++++++++++++++++++++++++++++
 extensions/window-list/metadata.json.in |   10 ++
 extensions/window-list/stylesheet.css   |   63 +++++++
 5 files changed, 349 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 536ba3d..9ac1e1a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,7 +27,7 @@ fi
 AC_SUBST([SHELL_VERSION])
 
 dnl keep this in alphabetic order
-CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab default-min-max launch-new-instance static-workspaces"
+CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab default-min-max launch-new-instance static-workspaces window-list"
 DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS alternative-status-menu drive-menu windowsNavigator workspace-indicator"
 ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor user-theme xrandr-indicator"
 AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS])
@@ -71,7 +71,7 @@ for e in $enable_extensions; do
 					[AC_MSG_WARN([gnome-desktop-3.0 not found, disabling xrandr-indicator])])
 			;;
 dnl		keep this in alphabetic order
-		alternate-tab|alternative-status-menu|apps-menu|auto-move-windows|default-min-max|drive-menu|example|launch-new-instance|native-window-placement|places-menu|static-workspaces|user-theme|windowsNavigator|workspace-indicator)
+		alternate-tab|alternative-status-menu|apps-menu|auto-move-windows|default-min-max|drive-menu|example|launch-new-instance|native-window-placement|places-menu|static-workspaces|user-theme|window-list|windowsNavigator|workspace-indicator)
 			ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
 			;;
 		*)
@@ -97,6 +97,7 @@ AC_CONFIG_FILES([
   extensions/static-workspaces/Makefile
   extensions/systemMonitor/Makefile
   extensions/user-theme/Makefile
+  extensions/window-list/Makefile
   extensions/windowsNavigator/Makefile
   extensions/workspace-indicator/Makefile
   extensions/xrandr-indicator/Makefile
diff --git a/extensions/window-list/Makefile.am b/extensions/window-list/Makefile.am
new file mode 100644
index 0000000..48a1028
--- /dev/null
+++ b/extensions/window-list/Makefile.am
@@ -0,0 +1,3 @@
+EXTENSION_ID = window-list
+
+include ../../extension.mk
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
new file mode 100644
index 0000000..c603628
--- /dev/null
+++ b/extensions/window-list/extension.js
@@ -0,0 +1,270 @@
+const Clutter = imports.gi.Clutter;
+const Meta = imports.gi.Meta;
+const St = imports.gi.St;
+
+const Lang = imports.lang;
+const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+const WindowButton = new Lang.Class({
+    Name: 'WindowButton',
+
+    _init: function(metaWindow) {
+        this.metaWindow = metaWindow;
+
+        let box = new St.BoxLayout();
+        this.actor = new St.Button({ style_class: 'window-button',
+                                     child: box });
+        this.actor._delegate = this;
+
+        let textureCache = St.TextureCache.get_default();
+        let icon = textureCache.bind_pixbuf_property(this.metaWindow, "icon");
+        box.add(new St.Bin({ style_class: 'window-button-icon', child: icon }));
+
+        this._label = new St.Label({ text: metaWindow.title });
+        box.add(this._label);
+
+        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._notifyTitleId =
+            this.metaWindow.connect('notify::title',
+                                    Lang.bind(this, function() {
+                                        this._label.text = this.metaWindow.title;
+                                    }));
+        this._notifyMinimizedId =
+            this.metaWindow.connect('notify::minimized',
+                                    Lang.bind(this, this._updateStyle));
+        this._notifyFocusId =
+            global.display.connect('notify::focus-window',
+                                   Lang.bind(this, this._updateStyle));
+        this._updateStyle();
+    },
+
+    _onClicked: function() {
+        if (global.display.focus_window == this.metaWindow)
+            this.metaWindow.minimize();
+        else
+            this.metaWindow.activate(global.get_current_time());
+    },
+
+    _updateStyle: function() {
+        if (this.metaWindow.minimized)
+            this.actor.add_style_class_name('minimized');
+        else
+            this.actor.remove_style_class_name('minimized');
+
+        if (global.display.focus_window == this.metaWindow)
+            this.actor.add_style_class_name('focused');
+        else
+            this.actor.remove_style_class_name('focused');
+    },
+
+    _updateVisibility: function() {
+        let workspace = global.screen.get_active_workspace();
+        this.actor.visible = this.metaWindow.located_on_workspace(workspace);
+    },
+
+    _onDestroy: function() {
+        global.window_manager.disconnect(this._switchWorkspaceId);
+        this.metaWindow.disconnect(this._notifyTitleId);
+        this.metaWindow.disconnect(this._notifyMinimizedId);
+        global.display.disconnect(this._notifyFocusId);
+    }
+});
+
+
+const TrayButton = new Lang.Class({
+    Name: 'TrayButton',
+
+    _init: function() {
+        let icon = new St.Icon({ icon_name: 'dialog-information-symbolic',
+                                 style_class: 'system-status-icon' });
+        this.actor = new St.Button({ style_class: 'tray-button',
+                                     child: icon });
+        this.actor.set_x_align(Clutter.ActorAlign.END);
+        this.actor.set_x_expand(true);
+        this.actor.set_y_expand(true);
+
+        this.actor.connect('clicked', Lang.bind(this,
+            function() {
+                if (Main.messageTray._trayState == MessageTray.State.HIDDEN)
+                    Main.messageTray.toggle();
+            }));
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+        this._trayItemCount = 0;
+        Main.messageTray.getSummaryItems().forEach(Lang.bind(this,
+            function(item) {
+                this._itemAdded(Main.messageTray, item);
+            }));
+        this._itemAddedId =
+            Main.messageTray.connect('summary-item-added',
+                                     Lang.bind(this, this._itemAdded));
+        this._updateVisibility();
+    },
+
+    _itemAdded: function(tray, item) {
+        item.source._windowListDestroyId = item.source.connect('destroy', Lang.bind(this, this._itemRemoved));
+        this._trayItemCount++;
+        this._updateVisibility();
+    },
+
+    _itemRemoved: function(item) {
+        item.disconnect(item._windowListDestroyId);
+        delete item._windowListDestroyId;
+        this._trayItemCount--;
+        this.actor.checked = false;
+        this._updateVisibility();
+    },
+
+    _updateVisibility: function() {
+        this.actor.visible = this._trayItemCount > 0;
+    },
+
+    _onDestroy: function() {
+        Main.messageTray.disconnect(this._itemAddedId);
+    }
+});
+
+
+const WindowList = new Lang.Class({
+    Name: 'WindowList',
+
+    _init: function() {
+        this.actor = new St.Widget({ name: 'panel',
+                                     reactive: true,
+                                     layout_manager: new Clutter.BinLayout()});
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+        this._windowList = new St.BoxLayout({ style_class: 'window-list',
+                                              x_align: Clutter.ActorAlign.START,
+                                              x_expand: true,
+                                              y_expand: true });
+        this.actor.add_actor(this._windowList);
+
+        this._trayButton = new TrayButton();
+        this.actor.add_actor(this._trayButton.actor);
+
+        Main.layoutManager.addChrome(this.actor, { affectsStruts: true,
+                                                   trackFullscreen: true });
+
+        this._monitorsChangedId =
+            Main.layoutManager.connect('monitors-changed',
+                                       Lang.bind(this, this._updatePosition));
+        this._updatePosition();
+
+        this._nWorkspacesChangedId =
+            global.screen.connect('notify::n-workspaces',
+                                  Lang.bind(this, this._onWorkspacesChanged));
+        this._onWorkspacesChanged();
+
+        this._overviewShowingId =
+            Main.overview.connect('showing', Lang.bind(this, function() {
+                this.actor.hide();
+            }));
+
+        this._overviewHidingId =
+            Main.overview.connect('hiding', Lang.bind(this, function() {
+                this.actor.show();
+            }));
+
+        let windows = Meta.get_window_actors(global.screen);
+        for (let i = 0; i < windows.length; i++)
+            this._onWindowAdded(null, windows[i].metaWindow);
+    },
+
+    _updatePosition: function() {
+        let monitor = Main.layoutManager.primaryMonitor;
+        this.actor.width = monitor.width;
+        this.actor.set_position(monitor.x, monitor.y + monitor.height - this.actor.height);
+    },
+
+    _onWindowAdded: function(ws, win) {
+        let button = new WindowButton(win);
+        this._windowList.add(button.actor, { y_fill: true });
+    },
+
+    _onWindowRemoved: function(ws, win) {
+        let children = this._windowList.get_children();
+        for (let i = 0; i < children.length; i++) {
+            if (children[i]._delegate.metaWindow == win) {
+                children[i].destroy();
+                return;
+            }
+        }
+    },
+
+    _onWorkspacesChanged: function() {
+        let numWorkspaces = global.screen.n_workspaces;
+        for (let i = 0; i < numWorkspaces; i++) {
+            let workspace = global.screen.get_workspace_by_index(i);
+            if (workspace._windowAddedId)
+                workspace.disconnect(workspace._windowAddedId);
+            if (workspace._windowRemovedId)
+                workspace.disconnect(workspace._windowRemovedId);
+            workspace._windowAddedId =
+                workspace.connect('window-added',
+                                  Lang.bind(this, this._onWindowAdded));
+            workspace._windowRemovedId =
+                workspace.connect('window-removed',
+                                  Lang.bind(this, this._onWindowRemoved));
+        }
+    },
+
+    _onDestroy: function() {
+        Main.layoutManager.disconnect(this._monitorsChangedId);
+        this._monitorsChangedId = 0;
+
+        global.screen.disconnect(this._nWorkspacesChangedId);
+        this._nWorkspacesChangedId = 0;
+
+        Main.overview.disconnect(this._overviewShowingId);
+        Main.overview.disconnect(this._overviewHidingId);
+    }
+});
+
+let windowList;
+let injections = {};
+
+function init() {
+}
+
+function enable() {
+    windowList = new WindowList();
+
+    injections['_trayDwellTimeout'] = MessageTray.MessageTray.prototype._trayDwellTimeout;
+    MessageTray.MessageTray.prototype._trayDwellTimeout = function() {
+        return false;
+    };
+
+    injections['_tween'] = MessageTray.MessageTray.prototype._tween;
+    MessageTray.MessageTray.prototype._tween = function(actor, statevar, value, params) {
+        if (statevar == '_trayState' && !Main.overview.visible) {
+            let anchorY = windowList.actor.height;
+            actor.anchor_y = anchorY;
+        }
+        injections['_tween'].call(Main.messageTray, actor, statevar, value, params);
+    };
+    injections['_onTrayHidden'] = MessageTray.MessageTray.prototype._onTrayHidden;
+    MessageTray.MessageTray.prototype._onTrayHidden = function() {
+        this.actor.anchor_y = 0;
+        injections['_onTrayHidden'].call(Main.messageTray);
+    };
+}
+
+function disable() {
+    if (!windowList)
+        return;
+
+    windowList.actor.destroy();
+    windowList = null;
+
+    for (prop in injections)
+        MessageTray.MessageTray.prototype[prop] = injections[prop];
+}
diff --git a/extensions/window-list/metadata.json.in b/extensions/window-list/metadata.json.in
new file mode 100644
index 0000000..ebfaed6
--- /dev/null
+++ b/extensions/window-list/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Window List",
+"description": "Display a window list at the bottom of the screen",
+"shell-version": [ "@shell_current@" ],
+"url": "@url@"
+}
diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
new file mode 100644
index 0000000..dae184a
--- /dev/null
+++ b/extensions/window-list/stylesheet.css
@@ -0,0 +1,63 @@
+.window-list {
+  spacing: 2px;
+  font-size: 10pt;
+}
+
+.window-button {
+  padding: 1px;
+}
+
+.window-button:first-child:ltr {
+  padding-left: 2px;
+}
+
+.window-button:last-child:rtl {
+  padding-right: 2px;
+}
+
+.window-button > StWidget {
+  width: 250px;
+  color: #bbb;
+  background-color: black;
+  border-radius: 4px;
+  padding: 2px 6px;
+  box-shadow: inset 1px 1px 4px rgba(255,255,255,0.5);
+  text-shadow: 1px 1px 4px rgba(0,0,0,0.8);
+  spacing: 4px;
+}
+
+.window-button:hover > StWidget {
+  color: white;
+  background-color: #1f1f1f;
+}
+
+.window-button:active > StWidget {
+  box-shadow: inset 2px 2px 4px rgba(255,255,255,0.5);
+}
+
+.window-button.focused > StWidget {
+  color: white;
+  box-shadow: inset 1px 1px 4px rgba(255,255,255,0.7);
+}
+
+.window-button.focused:active > StWidget {
+  box-shadow: inset 2px 2px 4px rgba(255,255,255,0.7);
+}
+
+.window-button.minimized > StWidget {
+  color: #666;
+  box-shadow: inset -1px -1px 4px rgba(255,255,255,0.5);
+}
+
+.window-button.minimized:active > StWidget {
+  box-shadow: inset -2px -2px 4px rgba(255,255,255,0.5);
+}
+
+.window-button-icon {
+  width: 16px;
+  height: 16px;
+}
+
+.tray-button {
+  padding: 1px 1px 1px 4px;
+}



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