diff --git a/js/ui/layout.js b/js/ui/layout.js index f35787a30..70ece6cab 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 ac1a349da..209be4203 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 4f0abeb28..72542b381 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; }