[gnome-shell] overview: Implement startup animation



commit c4e43efb1e9e05e718733aa53e79b079bdf6c71f
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Mon Feb 15 17:39:14 2021 -0300

    overview: Implement startup animation
    
    The new startup animation consists of rising the Dash from the bottom,
    falling the search entry from the ceiling, and going from HIDDEN to
    WINDOW_PICKER with an opacity applied.
    
    One little trick from IconGridLayout was added to ControlsManagerLayout,
    which is a promises-based wait for allocation. This is required to make
    sure that the transformed position of the search entry is valid, which
    is only the case right after an allocation.
    
    This animation also ensures that the overview is shown right on startup.
    
    For session modes that do not have an overview, continue using the same
    fade + scale animation.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1678>

 js/ui/layout.js           | 38 ++++++++++++++++---------
 js/ui/overview.js         | 21 ++++++++++++++
 js/ui/overviewControls.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 14 deletions(-)
---
diff --git a/js/ui/layout.js b/js/ui/layout.js
index f35787a304..70ece6cab8 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -664,13 +664,18 @@ var LayoutManager = GObject.registerClass({
             this.keyboardBox.hide();
 
             let monitor = this.primaryMonitor;
-            let x = monitor.x + monitor.width / 2.0;
-            let y = monitor.y + monitor.height / 2.0;
 
-            this.uiGroup.set_pivot_point(x / global.screen_width,
-                                         y / global.screen_height);
-            this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
-            this.uiGroup.opacity = 0;
+            if (!Main.sessionMode.hasOverview) {
+                const x = monitor.x + monitor.width / 2.0;
+                const y = monitor.y + monitor.height / 2.0;
+
+                this.uiGroup.set_pivot_point(
+                    x / global.screen_width,
+                    y / global.screen_height);
+                this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
+                this.uiGroup.opacity = 0;
+            }
+
             global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
 
             await this._updateBackgrounds();
@@ -700,14 +705,19 @@ var LayoutManager = GObject.registerClass({
     }
 
     _startupAnimationSession() {
-        this.uiGroup.ease({
-            scale_x: 1,
-            scale_y: 1,
-            opacity: 255,
-            duration: STARTUP_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onComplete: () => this._startupAnimationComplete(),
-        });
+        const onComplete = () => this._startupAnimationComplete();
+        if (Main.sessionMode.hasOverview) {
+            Main.overview.runStartupAnimation(onComplete);
+        } else {
+            this.uiGroup.ease({
+                scale_x: 1,
+                scale_y: 1,
+                opacity: 255,
+                duration: STARTUP_ANIMATION_TIME,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onComplete,
+            });
+        }
     }
 
     _startupAnimationComplete() {
diff --git a/js/ui/overview.js b/js/ui/overview.js
index ac1a349da6..209be42030 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -107,6 +107,10 @@ class OverviewActor extends St.BoxLayout {
         this._controls.animateFromOverview(callback);
     }
 
+    runStartupAnimation(callback) {
+        this._controls.runStartupAnimation(callback);
+    }
+
     get dash() {
         return this._controls.dash;
     }
@@ -647,6 +651,23 @@ var Overview = class {
         this._show(OverviewControls.ControlsState.APP_GRID);
     }
 
+    runStartupAnimation(callback) {
+        this._shown = true;
+        this._visible = true;
+        this._visibleTarget = true;
+        Main.layoutManager.showOverview();
+        this._syncGrab();
+
+        Meta.disable_unredirect_for_display(global.display);
+
+        this.emit('showing');
+
+        this._overview.runStartupAnimation(() => {
+            this.emit('shown');
+            callback();
+        });
+    }
+
     getShowAppsButton() {
         logError(new Error('Usage of Overview.\'getShowAppsButton\' is deprecated, ' +
             'use \'dash.showAppsButton\' property instead'));
diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js
index 4f0abeb281..72542b3813 100644
--- a/js/ui/overviewControls.js
+++ b/js/ui/overviewControls.js
@@ -5,6 +5,7 @@ const { Clutter, Gio, GObject, Meta, Shell, St } = imports.gi;
 
 const AppDisplay = imports.ui.appDisplay;
 const Dash = imports.ui.dash;
+const Layout = imports.ui.layout;
 const Main = imports.ui.main;
 const Overview = imports.ui.overview;
 const SearchController = imports.ui.searchController;
@@ -41,6 +42,7 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
         this._dash = dash;
 
         this._cachedWorkspaceBoxes = new Map();
+        this._postAllocationCallbacks = [];
 
         stateAdjustment.connect('notify::value', () => this.layout_changed());
     }
@@ -100,6 +102,14 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
         return appDisplayBox;
     }
 
+    _runPostAllocation() {
+        if (this._postAllocationCallbacks.length === 0)
+            return;
+
+        this._postAllocationCallbacks.forEach(cb => cb());
+        this._postAllocationCallbacks = [];
+    }
+
     vfunc_set_container(container) {
         this._container = container;
         this.hookup_style(container);
@@ -192,6 +202,14 @@ class ControlsManagerLayout extends Clutter.BoxLayout {
         childBox.set_size(width, availableHeight);
 
         this._searchController.allocate(childBox);
+
+        this._runPostAllocation();
+    }
+
+    ensureAllocation() {
+        this.layout_changed();
+        return new Promise(
+            resolve => this._postAllocationCallbacks.push(resolve));
     }
 
     getWorkspacesBoxForState(state) {
@@ -695,6 +713,60 @@ class ControlsManager extends St.Widget {
         this._stateAdjustment.gestureInProgress = false;
     }
 
+    async runStartupAnimation(callback) {
+        this._ignoreShowAppsButtonToggle = true;
+
+        this._searchController.prepareToEnterOverview();
+        this._workspacesDisplay.prepareToEnterOverview();
+
+        this._stateAdjustment.value = ControlsState.HIDDEN;
+        this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, {
+            duration: Overview.ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+        });
+
+        this.dash.showAppsButton.checked = false;
+        this._ignoreShowAppsButtonToggle = false;
+
+        // Set the opacity here to avoid a 1-frame flicker
+        this.opacity = 0;
+
+        // We can't run the animation before the first allocation happens
+        await this.layout_manager.ensureAllocation();
+
+        const { STARTUP_ANIMATION_TIME } = Layout;
+
+        // Opacity
+        this.ease({
+            opacity: 255,
+            duration: STARTUP_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.LINEAR,
+        });
+
+        // Search bar falls from the ceiling
+        const { primaryMonitor } = Main.layoutManager;
+        const [, y] = this._searchEntryBin.get_transformed_position();
+        const yOffset = y - primaryMonitor.y;
+
+        this._searchEntryBin.translation_y = -(yOffset + this._searchEntryBin.height);
+        this._searchEntryBin.ease({
+            translation_y: 0,
+            duration: STARTUP_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+        });
+
+        // The Dash rises from the bottom. This is the last animation to finish,
+        // so run the callback there.
+        this.dash.translation_y = this.dash.height;
+        this.dash.ease({
+            translation_y: 0,
+            delay: STARTUP_ANIMATION_TIME,
+            duration: STARTUP_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+            onComplete: () => callback(),
+        });
+    }
+
     get searchEntry() {
         return this._searchEntry;
     }


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