[gnome-shell/T27795: 37/138] sideComponent: Add SideComponent back



commit aca373dd4837d0fe1b9a9a36fde02aec2c1c14d0
Author: Sam Spilsbury <sam endlessm com>
Date:   Sun May 28 04:59:48 2017 +0800

    sideComponent: Add SideComponent back
    
    This is used by the website panel, the discovery feed and Hack.
    
    2019-09-23: replace Tweener by Clutter implicit animations
    
    https://phabricator.endlessm.com/T2519
    https://phabricator.endlessm.com/T16876
    https://phabricator.endlessm.com/T18137
    https://phabricator.endlessm.com/T18601

 js/js-resources.gresource.xml |   1 +
 js/ui/layout.js               |  23 ++-
 js/ui/overview.js             |  12 ++
 js/ui/sideComponent.js        | 148 +++++++++++++++
 js/ui/windowManager.js        | 429 +++++++++++++++++++++++++++++++++++++++++-
 src/shell-window-tracker.c    |   8 +
 6 files changed, 619 insertions(+), 2 deletions(-)
---
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index b59be3806b..36a909d497 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -147,5 +147,6 @@
     <file>ui/forceAppExitDialog.js</file>
     <file>ui/hotCorner.js</file>
     <file>ui/monitor.js</file>
+    <file>ui/sideComponent.js</file>
   </gresource>
 </gresources>
diff --git a/js/ui/layout.js b/js/ui/layout.js
index f507cb0deb..cfbb0234ca 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -63,7 +63,8 @@ const defaultParams = {
 };
 
 var LayoutManager = GObject.registerClass({
-    Signals: { 'hot-corners-changed': {},
+    Signals: { 'background-clicked': {},
+               'hot-corners-changed': {},
                'startup-complete': {},
                'startup-prepared': {},
                'monitors-changed': {},
@@ -84,6 +85,7 @@ var LayoutManager = GObject.registerClass({
         this._inOverview = false;
         this._updateRegionIdle = 0;
 
+        this._overlayRegion = null;
         this._trackedActors = [];
         this._topActors = [];
         this._isPopupWindowVisible = false;
@@ -730,6 +732,11 @@ var LayoutManager = GObject.registerClass({
         this._trackActor(actor, params);
     }
 
+    setOverlayRegion(region) {
+        this._overlayRegion = region;
+        this._queueUpdateRegions();
+    }
+
     // addTopChrome:
     // @actor: an actor to add to the chrome
     // @params: (optional) additional params
@@ -991,6 +998,20 @@ var LayoutManager = GObject.registerClass({
             }
         }
 
+        if (this._overlayRegion != null) {
+            let numOverlayRects = this._overlayRegion.numRectangles();
+            for (let idx = 0; idx < numOverlayRects; idx++) {
+                let rect = this._overlayRegion.getRectangle(idx);
+                let metaRect = new Meta.Rectangle({
+                    x: rect.x,
+                    y: rect.y,
+                    width: rect.width,
+                    height: rect.height,
+                });
+                rects.push(metaRect);
+            }
+        }
+
         global.set_stage_input_region(rects);
         this._isPopupWindowVisible = isPopupMenuVisible;
 
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 97e5d58207..492c3e7315 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -250,6 +250,7 @@ var Overview = class {
             this.dashIconSize = this._dash.iconSize;
         });
 
+        this.viewSelector.connect('page-changed', this._onPageChanged.bind(this));
         Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
         this._relayout();
     }
@@ -274,6 +275,13 @@ var Overview = class {
         this._shellInfo.setMessage(text, options);
     }
 
+    _onPageChanged() {
+        // SideComponent hooks on this signal but can't connect directly to
+        // viewSelector since it won't be created at the time the component
+        // is enabled, so rely on the overview and re-issue it from here.
+        this.emit('page-changed');
+    }
+
     _onDragBegin() {
         this._inXdndDrag = true;
 
@@ -641,6 +649,10 @@ var Overview = class {
             this.show();
     }
 
+    getActivePage() {
+        return this.viewSelector.getActivePage();
+    }
+
     getShowAppsButton() {
         return this._dash.showAppsButton;
     }
diff --git a/js/ui/sideComponent.js b/js/ui/sideComponent.js
new file mode 100644
index 0000000000..97f3fb5b54
--- /dev/null
+++ b/js/ui/sideComponent.js
@@ -0,0 +1,148 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const { Gio, GLib, GObject, Meta } = imports.gi;
+
+const Main = imports.ui.main;
+const ViewSelector = imports.ui.viewSelector;
+
+const SIDE_COMPONENT_ROLE = 'eos-side-component';
+
+/**
+ * isSideComponentWindow:
+ * @metaWindow: an instance of #Meta.Window
+ * @return: whether the #Meta.Window belongs to a #SideComponent 
+ */
+function isSideComponentWindow (metaWindow) {
+    return metaWindow && (metaWindow.get_role() == SIDE_COMPONENT_ROLE);
+};
+
+/**
+ * shouldHideOtherWindows:
+ * @metaWindow: an instance of #Meta.Window
+ * @return: whether other windows should be hidden while this one is open
+ */
+function shouldHideOtherWindows (metaWindow) {
+    return isSideComponentWindow(metaWindow);
+};
+
+/**
+ * launchedFromDesktop:
+ * @metaWindow: an instance of #Meta.Window
+ * @return: whether the side component was launched from the desktop
+ */
+function launchedFromDesktop (metaWindow) {
+    return false;
+};
+
+var SideComponent = GObject.registerClass(
+class SideComponent extends GObject.Object {
+    _init(proxyIface, proxyName, proxyPath) {
+        super._init();
+        this._propertiesChangedId = 0;
+        this._desktopShownId = 0;
+
+        this._proxyIface = proxyIface;
+        this._proxyInfo = Gio.DBusInterfaceInfo.new_for_xml(this._proxyIface);
+        this._proxyName = proxyName;
+        this._proxyPath = proxyPath;
+
+        this._visible = false;
+        this._launchedFromDesktop = false;
+    }
+
+    enable() {
+        if (!this.proxy) {
+            this.proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
+                                             g_interface_name: this._proxyInfo.name,
+                                             g_interface_info: this._proxyInfo,
+                                             g_name: this._proxyName,
+                                             g_object_path: this._proxyPath,
+                                             g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION 
});
+            this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, this._onProxyConstructed.bind(this));
+        }
+
+        this._propertiesChangedId =
+            this.proxy.connect('g-properties-changed', this._onPropertiesChanged.bind(this));
+
+        // Clicking the background (which calls overview.showApps) hides the component,
+        // so trying to open it again will call WindowManager._mapWindow(),
+        // which will hide the overview and animate the window.
+        // Note that this is not the case when opening the window picker.
+        this._desktopShownId = Main.layoutManager.connect('background-clicked', () => {
+            if (this._visible)
+                this.hide(global.get_current_time());
+        });
+
+        // Same when clicking the background from the window picker.
+        this._overviewPageChangedId = Main.overview.connect('page-changed', () => {
+            if (this._visible && Main.overview.visible &&
+                Main.overview.getActivePage() == ViewSelector.ViewPage.APPS)
+                this.hide(global.get_current_time());
+        });
+    }
+
+    disable() {
+        if (this._propertiesChangedId > 0) {
+            this.proxy.disconnect(this._propertiesChangedId);
+            this._propertiesChangedId = 0;
+        }
+
+        if (this._desktopShownId > 0) {
+            Main.layoutManager.disconnect(this._desktopShownId);
+            this._desktopShownId = 0;
+        }
+
+        if (this._overviewPageChangedId > 0) {
+            Main.overview.disconnect(this._overviewPageChangedId);
+            this._overviewPageChangedId = 0;
+        }
+    }
+
+    _onProxyConstructed(object, res) {
+        try {
+            object.init_finish(res);
+        } catch (e) {
+            logError(e, 'Error while constructing the DBus proxy for ' + this._proxyName);
+        }
+    }
+
+    _onPropertiesChanged(proxy, changedProps, invalidatedProps) {
+        let propsDict = changedProps.deep_unpack();
+        if (propsDict.hasOwnProperty('Visible'))
+            this._onVisibilityChanged();
+    }
+
+    _onVisibilityChanged() {
+        if (this._visible == this.proxy.Visible)
+            return;
+
+        // resync visibility
+        this._visible = this.proxy.Visible;
+    }
+
+    toggle(timestamp, params) {
+        if (this._visible)
+            this.hide(timestamp, params);
+        else
+            this.show(timestamp, params);
+    }
+
+    show(timestamp, params) {
+        this._launchedFromDesktop = Main.overview.visible &&
+                                    Main.overview.getActivePage() == ViewSelector.ViewPage.APPS;
+
+        if (this._visible && Main.overview.visible)
+            // the component is already open, but obscured by the overview
+            Main.overview.hide();
+        else
+            this.callShow(timestamp, params);
+    }
+
+    hide(timestamp, params) {
+        this.callHide(timestamp, params);
+    }
+
+    get launchedFromDesktop() {
+        return this._launchedFromDesktop;
+    }
+});
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index d0917f61fa..58707c155a 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -1,7 +1,9 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported WindowManager */
 
-const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
+const { Clutter, Gdk, Gio, GLib,
+        GObject, Meta, Shell, St } = imports.gi;
+const Cairo = imports.cairo;
 const Signals = imports.signals;
 
 const AltTab = imports.ui.altTab;
@@ -18,6 +20,8 @@ const EdgeDragAction = imports.ui.edgeDragAction;
 const CloseDialog = imports.ui.closeDialog;
 const SwitchMonitor = imports.ui.switchMonitor;
 const IBusManager = imports.misc.ibusManager;
+const SideComponent = imports.ui.sideComponent;
+const BackgroundMenu = imports.ui.backgroundMenu;
 
 const { loadInterfaceXML } = imports.misc.fileUtils;
 
@@ -694,6 +698,264 @@ var ResizePopup = class {
     }
 };
 
+var DesktopOverlay = GObject.registerClass({
+    Signals: { 'clicked': {} },
+}, class DesktopOverlay extends St.Widget {
+    _init() {
+        super._init({ reactive: true });
+
+        this._shellwm = global.window_manager;
+
+        this._actorDestroyId = 0;
+        this._allocationId = 0;
+        this._destroyId = 0;
+        this._mapId = 0;
+        this._visibleId = 0;
+        this._showing = false;
+
+        this._overlayActor = null;
+        this._transientActors = [];
+
+        let action = new Clutter.ClickAction();
+        action.connect('clicked', action => {
+            if (action.get_button() != Gdk.BUTTON_PRIMARY)
+                return;
+
+            if (this._showing && this._overlayActor)
+                this.emit('clicked');
+        });
+        this.add_action(action);
+        BackgroundMenu.addBackgroundMenu(this, Main.layoutManager);
+
+        Main.overview.connect('showing', () => {
+            // hide the overlay so it doesn't conflict with the desktop
+            if (this._showing)
+                this.hide();
+        });
+        Main.overview.connect('hiding', () => {
+            // show the overlay if needed
+            if (this._showing)
+                this.show();
+        });
+
+        Main.uiGroup.add_actor(this);
+        if (Main.uiGroup.contains(global.top_window_group))
+            Main.uiGroup.set_child_below_sibling(this, global.top_window_group);
+    }
+
+    _rebuildRegion() {
+        if (!this._overlayActor.get_paint_visibility()) {
+            Main.layoutManager.setOverlayRegion(null);
+            return;
+        }
+
+        let overlayWindow = this._overlayActor.meta_window;
+        let monitorIdx = overlayWindow.get_monitor();
+        let monitor = Main.layoutManager.monitors[monitorIdx];
+        if (!monitor)
+            return;
+
+        let workArea = Main.layoutManager.getWorkAreaForMonitor(overlayWindow.get_monitor());
+        let region = new Cairo.Region();
+        region.unionRectangle(workArea);
+
+        let [x, y] = this._overlayActor.get_transformed_position();
+        let [width, height] = this._overlayActor.get_transformed_size();
+        let rect = {
+            x: Math.round(x),
+            y: Math.round(y),
+            width: Math.round(width),
+            height: Math.round(height),
+        };
+
+        region.subtractRectangle(rect);
+
+        this._transientActors.forEach(actorData => {
+            let transientActor = actorData.actor;
+
+            let [x, y] = transientActor.get_transformed_position();
+            let [width, height] = transientActor.get_transformed_size();
+            let rect = {
+                x: Math.round(x),
+                y: Math.round(y),
+                width: Math.round(width),
+                height: Math.round(height),
+            };
+
+            region.subtractRectangle(rect);
+        });
+
+        Main.layoutManager.setOverlayRegion(region);
+    }
+
+    _repositionOverlay() {
+        let overlayWindow = this._overlayActor.meta_window;
+        let monitorIdx = overlayWindow.get_monitor();
+        let monitor = Main.layoutManager.monitors[monitorIdx];
+        if (!monitor)
+            return;
+
+        // The width and height of the overlay need to be the width and height
+        // of the workArea. We already capture inputs correctly
+        // outside the overlay window by setting the region of the
+        // layoutManager overlay, so it is safe to just take up the
+        // entire workArea.
+        let workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIdx);
+        this.width = workArea.width;
+        this.height = workArea.height;
+        this.y = this._overlayActor.y;
+
+        if (this._overlayActor.x <= monitor.x)
+            this.x = monitor.x + monitor.width - this.width;
+        else
+            this.x = monitor.x;
+    }
+
+    _recalculateOverlay() {
+        this._repositionOverlay();
+        this._rebuildRegion();
+    }
+
+    _findTransientActor(actor) {
+        for (let i = 0; i < this._transientActors.length; i++) {
+            let actorData = this._transientActors[i];
+            if (actorData.actor == actor)
+                return i;
+        }
+        return -1;
+    }
+
+    _untrackTransientActor(actor) {
+        let idx = this._findTransientActor(actor);
+        if (idx == -1) {
+            log('Trying to untrack a non-tracked transient actor!');
+            return;
+        }
+
+        let actorData = this._transientActors[idx];
+        this._transientActors.splice(idx, 1);
+
+        actor.disconnect(actorData.visibleId);
+        actor.disconnect(actorData.allocationId);
+        actor.disconnect(actorData.destroyId);
+
+        this._rebuildRegion();
+    }
+
+    _trackTransientActor(actor) {
+        if (this._findTransientActor(actor) != -1) {
+            log('Trying to track twice the same transient actor!');
+            return;
+        }
+
+        let actorData = {};
+        actorData.actor = actor;
+        actorData.visibleId = actor.connect(
+            'notify::visible', this._recalculateOverlay.bind(this));
+        actorData.allocationId = actor.connect(
+            'notify::allocation', this._recalculateOverlay.bind(this));
+        actorData.destroyId = actor.connect(
+            'destroy', this._untrackTransientActor.bind(this));
+
+        this._transientActors.push(actorData);
+        this._recalculateOverlay();
+    }
+
+    _untrackActor() {
+        this._transientActors.forEach((actorData) => {
+            this._untrackTransientActor(actorData.actor);
+        });
+        this._transientActors = [];
+
+        if (this._visibleId > 0) {
+            this._overlayActor.disconnect(this._visibleId);
+            this._visibleId = 0;
+        }
+
+        if (this._allocationId > 0) {
+            this._overlayActor.disconnect(this._allocationId);
+            this._allocationId = 0;
+        }
+
+        if (this._actorDestroyId > 0) {
+            this._overlayActor.disconnect(this._actorDestroyId);
+            this._actorDestroyId = 0;
+        }
+
+        if (this._destroyId > 0) {
+            this._shellwm.disconnect(this._destroyId);
+            this._destroyId = 0;
+        }
+
+        if (this._mapId > 0) {
+            this._shellwm.disconnect(this._mapId);
+            this._mapId = 0;
+        }
+
+        Main.layoutManager.setOverlayRegion(null);
+    }
+
+    _trackActor() {
+        let overlayWindow = this._overlayActor.meta_window;
+
+        this._visibleId = this._overlayActor.connect(
+            'notify::visible', this._recalculateOverlay.bind(this));
+        this._allocationId = this._overlayActor.connect(
+            'notify::allocation', this._recalculateOverlay.bind(this));
+        this._actorDestroyId = this._overlayActor.connect(
+            'destroy', this._untrackActor.bind(this));
+
+        this._mapId = this._shellwm.connect('map', (shellwm, actor) => {
+            let newWindow = actor.meta_window;
+            if (overlayWindow.is_ancestor_of_transient(newWindow))
+                this._trackTransientActor(actor);
+        });
+        this._destroyId = this._shellwm.connect('destroy', (shellwm, actor) => {
+            let destroyedWindow = actor.meta_window;
+            if (overlayWindow.is_ancestor_of_transient(destroyedWindow))
+                this._untrackTransientActor(actor);
+        });
+
+        // seed the transient actors
+        overlayWindow.foreach_transient((transientWindow) => {
+            let transientActor = overlayWindow.get_compositor_private();
+            if (transientActor != null)
+                this._trackTransientActor(transientActor);
+        });
+
+        this._recalculateOverlay();
+    }
+
+    _setOverlayActor(actor) {
+        if (actor == this._overlayActor)
+            return;
+
+        this._untrackActor();
+        this._overlayActor = actor;
+
+        if (this._overlayActor)
+            this._trackActor();
+    }
+
+    get overlayActor() {
+        return this._overlayActor;
+    }
+
+    showOverlay(actor) {
+        this._setOverlayActor(actor);
+
+        this._showing = true;
+        this.show();
+    }
+
+    hideOverlay() {
+        this._setOverlayActor(null);
+
+        this._showing = false;
+        this.hide();
+    }
+});
+
 var WindowManager = class {
     constructor() {
         this._shellwm =  global.window_manager;
@@ -711,6 +973,17 @@ var WindowManager = class {
 
         this._allowedKeybindings = {};
 
+        this._desktopOverlay = new DesktopOverlay();
+        this._showDesktopOnDestroyDone = false;
+
+        // The desktop overlay needs to replicate the background's functionality;
+        // when clicked, we animate the side component out before emitting "background-clicked".
+        this._desktopOverlay.connect('clicked', () => {
+            this._slideSideComponentOut(this._shellwm,
+                                        this._desktopOverlay.overlayActor,
+                                        () => Main.layoutManager.emit('background-clicked'));
+        });
+
         this._isWorkspacePrepended = false;
 
         this._switchData = null;
@@ -1262,6 +1535,11 @@ var WindowManager = class {
         if (this._removeEffect(this._skippedActors, actor))
             return false;
 
+        // We should always animate side component windows in, even
+        // if the overview is visible
+        if (SideComponent.isSideComponentWindow(actor.meta_window))
+            return true;
+
         if (!this._shouldAnimate())
             return false;
 
@@ -1281,6 +1559,30 @@ var WindowManager = class {
         return false;
     }
 
+    _slideSideComponentOut(shellwm, actor, onComplete) {
+        let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+        if (!monitor) {
+            onComplete.apply(this, [shellwm, actor]);
+            return;
+        }
+
+        actor.opacity = 255;
+        actor.show();
+
+        let endX;
+        if (actor.x <= monitor.x)
+            endX = monitor.x - actor.width;
+        else
+            endX = monitor.x + monitor.width;
+
+        actor.ease({
+            x: endX,
+            duration: WINDOW_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+            onComplete: () => onComplete(shellwm, actor),
+        });
+    }
+
     _minimizeWindow(shellwm, actor) {
         let types = [Meta.WindowType.NORMAL,
                      Meta.WindowType.MODAL_DIALOG,
@@ -1609,6 +1911,86 @@ var WindowManager = class {
         dimmer.setDimmed(false, this._shouldAnimate());
     }
 
+    _hideOtherWindows(actor, animate) {
+        let winActors = global.get_window_actors();
+        for (let winActor of winActors) {
+            if (!winActor.get_meta_window().showing_on_its_workspace())
+                continue;
+
+            if (SideComponent.isSideComponentWindow(winActor.meta_window))
+                continue;
+
+            if (animate) {
+                winActor.ease({
+                    opacity: 0,
+                    duration: WINDOW_ANIMATION_TIME,
+                    mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                    onComplete: winActor => winActor.hide(),
+                });
+            } else {
+                winActor.opacity = 0;
+                winActor.hide();
+            }
+        }
+
+        this._desktopOverlay.showOverlay(actor);
+    }
+
+    _showOtherWindows(actor, animate) {
+        let winActors = global.get_window_actors();
+        for (let winActor of winActors) {
+            if (!winActor.get_meta_window().showing_on_its_workspace())
+                continue;
+
+            if (SideComponent.isSideComponentWindow(winActor.meta_window))
+                continue;
+
+            if (animate && winActor.opacity != 255) {
+                winActor.show();
+                winActor.ease({
+                    opacity: 255,
+                    duration: WINDOW_ANIMATION_TIME,
+                    mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                });
+            } else {
+                winActor.opacity = 255;
+                winActor.show();
+            }
+        }
+
+        this._desktopOverlay.hideOverlay();
+    }
+
+    _mapSideComponent(shellwm, actor, animateFade) {
+        let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+        if (!monitor) {
+            this._mapWindowDone(shellwm, actor);
+            return;
+        }
+
+        let origX = actor.x;
+        if (origX == monitor.x) {
+            // the side bar will appear from the left side
+            actor.set_position(monitor.x - actor.width, actor.y);
+        } else {
+            // ... from the right side
+            actor.set_position(monitor.x + monitor.width, actor.y);
+        }
+
+        actor.ease({
+            x: origX,
+            duration: WINDOW_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+            onComplete: this._mapWindowDone.bind(this),
+        });
+
+        actor.opacity = 255;
+        actor.show();
+
+        if (SideComponent.shouldHideOtherWindows(actor.meta_window))
+            this._hideOtherWindows(actor, animateFade);
+    }
+
     _mapWindow(shellwm, actor) {
         actor._windowType = actor.meta_window.get_window_type();
         actor._notifyWindowTypeSignalId =
@@ -1654,10 +2036,28 @@ var WindowManager = class {
                      Meta.WindowType.DIALOG,
                      Meta.WindowType.MODAL_DIALOG];
         if (!this._shouldAnimateActor(actor, types)) {
+            if (SideComponent.shouldHideOtherWindows(actor.meta_window))
+                this._showOtherWindows(actor, false);
+
             shellwm.completed_map(actor);
             return;
         }
 
+        if (SideComponent.isSideComponentWindow(actor.meta_window)) {
+            this._mapping.push(actor);
+
+            if (Main.overview.visible) {
+                let overviewHiddenId = Main.overview.connect('hidden', () => {
+                    Main.overview.disconnect(overviewHiddenId);
+                    this._mapSideComponent(shellwm, actor, false);
+                });
+                Main.overview.hide();
+            } else {
+                this._mapSideComponent(shellwm, actor, true);
+            }
+            return;
+        }
+
         switch (actor._windowType) {
         case Meta.WindowType.NORMAL:
             actor.set_pivot_point(0.5, 1.0);
@@ -1749,6 +2149,25 @@ var WindowManager = class {
             return;
         }
 
+        if (SideComponent.isSideComponentWindow(actor.meta_window)) {
+            this._destroying.push(actor);
+            this._slideSideComponentOut(shellwm, actor,
+                                        this._destroyWindowDone.bind(this));
+
+            // if the side component does not have the focus at this point,
+            // that means that it is closing because another window has gotten it
+            // and therefore we should not try to show the desktop
+            this._showDesktopOnDestroyDone = actor.meta_window.has_focus() &&
+                                             SideComponent.launchedFromDesktop(actor.meta_window);
+
+            if (!this._showDesktopOnDestroyDone && SideComponent.shouldHideOtherWindows(actor.meta_window)) {
+                // reveal other windows while we slide out the side component
+                this._showOtherWindows(actor, true);
+            }
+
+            return;
+        }
+
         switch (actor.meta_window.window_type) {
         case Meta.WindowType.NORMAL:
             actor.set_pivot_point(0.5, 0.5);
@@ -1795,6 +2214,14 @@ var WindowManager = class {
                 parent.disconnect(actor._parentDestroyId);
                 actor._parentDestroyId = 0;
             }
+
+            if (SideComponent.isSideComponentWindow(actor.meta_window) && this._showDesktopOnDestroyDone) {
+                Main.overview.showApps();
+
+                if (SideComponent.shouldHideOtherWindows(actor.meta_window))
+                    this._showOtherWindows(actor, false);
+            }
+
             shellwm.completed_destroy(actor);
         }
     }
diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c
index 16abd42673..a75e728ebb 100644
--- a/src/shell-window-tracker.c
+++ b/src/shell-window-tracker.c
@@ -24,6 +24,7 @@
  * Copyright Red Hat, Inc. 2006-2008
  */
 
+#define SIDE_COMPONENT_ROLE "eos-side-component"
 #define SPEEDWAGON_ROLE "eos-speedwagon"
 
 /**
@@ -372,6 +373,10 @@ get_app_for_window (ShellWindowTracker    *tracker,
   MetaWindow *transient_for;
   const char *startup_id;
 
+  /* Side components don't have an associated app */
+  if (g_strcmp0 (meta_window_get_role (window), SIDE_COMPONENT_ROLE) == 0)
+    return NULL;
+
   transient_for = meta_window_get_transient_for (window);
   if (transient_for != NULL)
     return get_app_for_window (tracker, transient_for);
@@ -821,6 +826,9 @@ shell_window_tracker_get_startup_sequences (ShellWindowTracker *self)
 gboolean
 shell_window_tracker_is_window_interesting (MetaWindow *window)
 {
+  if (g_strcmp0 (meta_window_get_role (window), SIDE_COMPONENT_ROLE) == 0)
+    return TRUE;
+
   if (meta_window_is_skip_taskbar (window))
     return FALSE;
 


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