[gnome-shell-extensions] window-list: Show previews in workspace switcher



commit 5b07dfded9842c75dc5b7ef80d5c30f6abd65029
Author: Florian Müllner <fmuellner gnome org>
Date:   Wed Jun 26 23:55:58 2019 +0000

    window-list: Show previews in workspace switcher
    
    Currently the new horizontal workspace switcher only shows a series of
    buttons, with no indication of the workspaces' contents. Go full GNOME 2
    and add tiny draggable preview rectangles that represent the windows
    on a particular workspace.
    
    https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/74

 extensions/window-list/classic.css           |  10 ++
 extensions/window-list/stylesheet.css        |  10 ++
 extensions/window-list/workspaceIndicator.js | 154 ++++++++++++++++++++++++++-
 3 files changed, 173 insertions(+), 1 deletion(-)
---
diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css
index c533473..7079d3e 100644
--- a/extensions/window-list/classic.css
+++ b/extensions/window-list/classic.css
@@ -56,3 +56,13 @@
 .window-list-workspace-indicator .workspace.active {
   background-color: #ccc;
 }
+
+.window-list-window-preview {
+  background-color: #ededed;
+  border: 1px solid #ccc;
+}
+
+.window-list-window-preview.active {
+  background-color: #f6f5f4;
+  border: 2px solid #888;
+}
diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
index ad5978a..79d56ba 100644
--- a/extensions/window-list/stylesheet.css
+++ b/extensions/window-list/stylesheet.css
@@ -121,6 +121,16 @@
   background-color: rgba(200, 200, 200, .3);
 }
 
+.window-list-window-preview {
+  background-color: #252525;
+  border: 1px solid #ccc;
+}
+
+.window-list-window-preview.active {
+  background-color: #353535;
+  border: 2px solid #ccc;
+}
+
 .notification {
   font-weight: normal;
 }
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index d669507..84dccac 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -9,16 +9,131 @@ const PopupMenu = imports.ui.popupMenu;
 const Gettext = imports.gettext.domain('gnome-shell-extensions');
 const _ = Gettext.gettext;
 
+let WindowPreview = GObject.registerClass({
+    GTypeName: 'WindowListWindowPreview'
+}, class WindowPreview extends St.Button {
+    _init(window) {
+        super._init({
+            style_class: 'window-list-window-preview'
+        });
+
+        this._delegate = this;
+        DND.makeDraggable(this, { restoreOnSuccess: true });
+
+        this._window = window;
+
+        this.connect('destroy', this._onDestroy.bind(this));
+
+        this._sizeChangedId = this._window.connect('size-changed',
+            this._relayout.bind(this));
+        this._positionChangedId = this._window.connect('position-changed',
+            this._relayout.bind(this));
+        this._minimizedChangedId = this._window.connect('notify::minimized',
+            this._relayout.bind(this));
+        this._monitorEnteredId = global.display.connect('window-entered-monitor',
+            this._relayout.bind(this));
+        this._monitorLeftId = global.display.connect('window-left-monitor',
+            this._relayout.bind(this));
+
+        // Do initial layout when we get a parent
+        let id = this.connect('parent-set', () => {
+            this.disconnect(id);
+            if (!this.get_parent())
+                return;
+            this._laterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+                this._laterId = 0;
+                this._relayout();
+                return false;
+            });
+        });
+
+        this._focusChangedId = global.display.connect('notify::focus-window',
+            this._onFocusChanged.bind(this));
+        this._onFocusChanged();
+    }
+
+    // needed for DND
+    get realWindow() {
+        return this._window.get_compositor_private();
+    }
+
+    _onDestroy() {
+        this._window.disconnect(this._sizeChangedId);
+        this._window.disconnect(this._positionChangedId);
+        this._window.disconnect(this._minimizedChangedId);
+        global.display.disconnect(this._monitorEnteredId);
+        global.display.disconnect(this._monitorLeftId);
+        global.display.disconnect(this._focusChangedId);
+        if (this._laterId)
+            Meta.later_remove(this._laterId);
+    }
+
+    _onFocusChanged() {
+        if (global.display.focus_window == this._window)
+            this.add_style_class_name('active');
+        else
+            this.remove_style_class_name('active');
+    }
+
+    _relayout() {
+        let monitor = Main.layoutManager.findIndexForActor(this);
+        this.visible = monitor == this._window.get_monitor() &&
+            this._window.showing_on_its_workspace();
+
+        if (!this.visible)
+            return;
+
+        let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
+        let hscale = this.get_parent().allocation.get_width() / workArea.width;
+        let vscale = this.get_parent().allocation.get_height() / workArea.height;
+
+        let frameRect = this._window.get_frame_rect();
+        this.set_size(
+            Math.round(Math.min(frameRect.width, workArea.width) * hscale),
+            Math.round(Math.min(frameRect.height, workArea.height) * vscale));
+        this.set_position(
+            Math.round(frameRect.x * hscale),
+            Math.round(frameRect.y * vscale));
+    }
+});
+
 let WorkspaceThumbnail = GObject.registerClass({
     GTypeName: 'WindowListWorkspaceThumbnail'
 }, class WorkspaceThumbnail extends St.Button {
     _init(index) {
         super._init({
-            style_class: 'workspace'
+            style_class: 'workspace',
+            child: new Clutter.Actor({
+                layout_manager: new Clutter.BinLayout(),
+                clip_to_allocation: true
+            }),
+            x_fill: true,
+            y_fill: true
         });
 
+        this.connect('destroy', this._onDestroy.bind(this));
+
         this._index = index;
         this._delegate = this; // needed for DND
+
+        this._windowPreviews = new Map();
+
+        let workspaceManager = global.workspace_manager;
+        this._workspace = workspaceManager.get_workspace_by_index(index);
+
+        this._windowAddedId = this._workspace.connect('window-added',
+            (ws, window) => {
+                this._addWindow(window);
+            });
+        this._windowRemovedId = this._workspace.connect('window-removed',
+            (ws, window) => {
+                this._removeWindow(window);
+            });
+        this._restackedId = global.display.connect('restacked',
+            this._onRestacked.bind(this));
+
+        this._workspace.list_windows().forEach(w => this._addWindow(w));
+        this._onRestacked();
     }
 
     acceptDrop(source) {
@@ -37,6 +152,37 @@ let WorkspaceThumbnail = GObject.registerClass({
             return DND.DragMotionResult.CONTINUE;
     }
 
+    _addWindow(window) {
+        if (this._windowPreviews.has(window))
+            return;
+
+        let preview = new WindowPreview(window);
+        preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
+        this._windowPreviews.set(window, preview);
+        this.child.add_child(preview);
+    }
+
+    _removeWindow(window) {
+        let preview = this._windowPreviews.get(window);
+        if (!preview)
+            return;
+
+        this._windowPreviews.delete(window);
+        preview.destroy();
+    }
+
+    _onRestacked() {
+        let lastPreview = null;
+        let windows = global.get_window_actors().map(a => a.meta_window);
+        for (let i = 0; i < windows.length; i++) {
+            let preview = this._windowPreviews.get(windows[i]);
+            if (!preview)
+                continue;
+
+            this.child.set_child_above_sibling(preview, lastPreview);
+            lastPreview = preview;
+        }
+    }
 
     _moveWindow(window) {
         let monitorIndex = Main.layoutManager.findIndexForActor(this);
@@ -50,6 +196,12 @@ let WorkspaceThumbnail = GObject.registerClass({
         if (ws)
             ws.activate(global.get_current_time());
     }
+
+    _onDestroy() {
+        this._workspace.disconnect(this._windowAddedId);
+        this._workspace.disconnect(this._windowRemovedId);
+        global.display.disconnect(this._restackedId);
+    }
 });
 
 var WorkspaceIndicator = GObject.registerClass({


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