gnome-shell/js/ui/overviewControls.js
Jonas Dreßler d49606bbaa overview: Get rid of panel ghost actor
Right now we use a ClutterClone ghost of the panel simply to add some
spacing to the top monitor edge.

We can remove the panelGhost by adjusting the allocations of all the
parts of the overview ourselves inside ControlsManagers vfunc_allocate,
this should also work with extensions that move the panel somewhere
else.

This makes the initial relayout of the overview significantly faster,
because we now no longer have to relayout the whole panel in the
process.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1755>
2021-03-13 22:13:25 +00:00

803 lines
27 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ControlsManager */
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;
const Util = imports.misc.util;
const WindowManager = imports.ui.windowManager;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const WorkspacesView = imports.ui.workspacesView;
const SMALL_WORKSPACE_RATIO = 0.15;
const DASH_MAX_HEIGHT_RATIO = 0.15;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
var SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME;
var ControlsState = {
HIDDEN: 0,
WINDOW_PICKER: 1,
APP_GRID: 2,
};
var ControlsManagerLayout = GObject.registerClass(
class ControlsManagerLayout extends Clutter.BoxLayout {
_init(searchEntry, appDisplay, workspacesDisplay, workspacesThumbnails,
searchController, dash, stateAdjustment) {
super._init({ orientation: Clutter.Orientation.VERTICAL });
this._appDisplay = appDisplay;
this._workspacesDisplay = workspacesDisplay;
this._workspacesThumbnails = workspacesThumbnails;
this._stateAdjustment = stateAdjustment;
this._searchEntry = searchEntry;
this._searchController = searchController;
this._dash = dash;
this._cachedWorkspaceBoxes = new Map();
this._postAllocationCallbacks = [];
stateAdjustment.connect('notify::value', () => this.layout_changed());
}
_computeWorkspacesBoxForState(state, box, startY, searchHeight, dashHeight, thumbnailsHeight) {
const workspaceBox = box.copy();
const [width, height] = workspaceBox.get_size();
const { spacing } = this;
const { expandFraction } = this._workspacesThumbnails;
switch (state) {
case ControlsState.HIDDEN:
break;
case ControlsState.WINDOW_PICKER:
workspaceBox.set_origin(0,
startY + searchHeight + spacing +
thumbnailsHeight + spacing * expandFraction);
workspaceBox.set_size(width,
height -
dashHeight - spacing -
searchHeight - spacing -
thumbnailsHeight - spacing * expandFraction);
break;
case ControlsState.APP_GRID:
workspaceBox.set_origin(0, startY + searchHeight + spacing);
workspaceBox.set_size(
width,
Math.round(height * SMALL_WORKSPACE_RATIO));
break;
}
return workspaceBox;
}
_getAppDisplayBoxForState(state, box, startY, searchHeight, dashHeight, appGridBox) {
const [width, height] = box.get_size();
const appDisplayBox = new Clutter.ActorBox();
const { spacing } = this;
switch (state) {
case ControlsState.HIDDEN:
case ControlsState.WINDOW_PICKER:
appDisplayBox.set_origin(0, box.y2);
break;
case ControlsState.APP_GRID:
appDisplayBox.set_origin(0,
startY + searchHeight + spacing + appGridBox.get_height());
break;
}
appDisplayBox.set_size(width,
height -
searchHeight - spacing -
appGridBox.get_height() - spacing -
dashHeight);
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);
}
vfunc_get_preferred_width(_container, _forHeight) {
// The MonitorConstraint will allocate us a fixed size anyway
return [0, 0];
}
vfunc_get_preferred_height(_container, _forWidth) {
// The MonitorConstraint will allocate us a fixed size anyway
return [0, 0];
}
vfunc_allocate(container, box) {
const childBox = new Clutter.ActorBox();
const { spacing } = this;
let startY = 0;
if (Main.layoutManager.panelBox.y === Main.layoutManager.primaryMonitor.y) {
startY = Main.layoutManager.panelBox.height;
box.y1 += startY;
}
const [width, height] = box.get_size();
let availableHeight = height;
// Search entry
let [searchHeight] = this._searchEntry.get_preferred_height(width);
childBox.set_origin(0, startY);
childBox.set_size(width, searchHeight);
this._searchEntry.allocate(childBox);
availableHeight -= searchHeight + spacing;
// Dash
const maxDashHeight = Math.round(box.get_height() * DASH_MAX_HEIGHT_RATIO);
this._dash.setMaxSize(width, maxDashHeight);
let [, dashHeight] = this._dash.get_preferred_height(width);
dashHeight = Math.min(dashHeight, maxDashHeight);
childBox.set_origin(0, startY + height - dashHeight);
childBox.set_size(width, dashHeight);
this._dash.allocate(childBox);
availableHeight -= dashHeight + spacing;
// Workspace Thumbnails
let thumbnailsHeight = 0;
if (this._workspacesThumbnails.visible) {
const { expandFraction } = this._workspacesThumbnails;
[thumbnailsHeight] =
this._workspacesThumbnails.get_preferred_height(width);
thumbnailsHeight = Math.min(
thumbnailsHeight * expandFraction,
height * WorkspaceThumbnail.MAX_THUMBNAIL_SCALE);
childBox.set_origin(0, startY + searchHeight + spacing);
childBox.set_size(width, thumbnailsHeight);
this._workspacesThumbnails.allocate(childBox);
}
// Workspaces
let params = [box, startY, searchHeight, dashHeight, thumbnailsHeight];
const transitionParams = this._stateAdjustment.getStateTransitionParams();
// Update cached boxes
for (const state of Object.values(ControlsState)) {
this._cachedWorkspaceBoxes.set(
state, this._computeWorkspacesBoxForState(state, ...params));
}
let workspacesBox;
if (!transitionParams.transitioning) {
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
} else {
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
workspacesBox = initialBox.interpolate(finalBox, transitionParams.progress);
}
this._workspacesDisplay.allocate(workspacesBox);
// AppDisplay
if (this._appDisplay.visible) {
const workspaceAppGridBox =
this._cachedWorkspaceBoxes.get(ControlsState.APP_GRID);
params = [box, startY, searchHeight, dashHeight, workspaceAppGridBox];
let appDisplayBox;
if (!transitionParams.transitioning) {
appDisplayBox =
this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
} else {
const initialBox =
this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
const finalBox =
this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
}
this._appDisplay.allocate(appDisplayBox);
}
// Search
childBox.set_origin(0, startY + searchHeight + spacing);
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) {
return this._cachedWorkspaceBoxes.get(state);
}
});
var OverviewAdjustment = GObject.registerClass({
Properties: {
'gesture-in-progress': GObject.ParamSpec.boolean(
'gesture-in-progress', 'Gesture in progress', 'Gesture in progress',
GObject.ParamFlags.READWRITE,
false),
},
}, class OverviewAdjustment extends St.Adjustment {
_init(actor) {
super._init({
actor,
value: ControlsState.WINDOW_PICKER,
lower: ControlsState.HIDDEN,
upper: ControlsState.APP_GRID,
});
}
getStateTransitionParams() {
const currentState = this.value;
const transition = this.get_transition('value');
let initialState = transition
? transition.get_interval().peek_initial_value()
: currentState;
let finalState = transition
? transition.get_interval().peek_final_value()
: currentState;
if (initialState > finalState) {
initialState = Math.ceil(initialState);
finalState = Math.floor(finalState);
} else {
initialState = Math.floor(initialState);
finalState = Math.ceil(finalState);
}
const length = Math.abs(finalState - initialState);
const progress = length > 0
? Math.abs((currentState - initialState) / length)
: 1;
return {
transitioning: transition !== null || this.gestureInProgress,
currentState,
initialState,
finalState,
progress,
};
}
});
var ControlsManager = GObject.registerClass(
class ControlsManager extends St.Widget {
_init() {
super._init({
style_class: 'controls-manager',
x_expand: true,
y_expand: true,
clip_to_allocation: true,
});
this._ignoreShowAppsButtonToggle = false;
this._searchEntry = new St.Entry({
style_class: 'search-entry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _('Type to search'),
track_hover: true,
can_focus: true,
});
this._searchEntry.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
this._searchEntryBin = new St.Bin({
child: this._searchEntry,
x_align: Clutter.ActorAlign.CENTER,
});
this.dash = new Dash.Dash();
let workspaceManager = global.workspace_manager;
let activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
this._workspaceAdjustment = new St.Adjustment({
actor: this,
value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
page_size: 1,
step_increment: 0,
upper: workspaceManager.n_workspaces,
});
this._stateAdjustment = new OverviewAdjustment(this);
this._stateAdjustment.connect('notify::value', this._update.bind(this));
this._nWorkspacesNotifyId =
workspaceManager.connect('notify::n-workspaces',
this._updateAdjustment.bind(this));
this._searchController = new SearchController.SearchController(
this._searchEntry,
this.dash.showAppsButton);
this._searchController.connect('notify::search-active', this._onSearchChanged.bind(this));
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(
this._workspaceAdjustment, Main.layoutManager.primaryIndex);
this._thumbnailsBox.connect('notify::should-show', () => {
this._thumbnailsBox.show();
this._thumbnailsBox.ease_property('expand-fraction',
this._thumbnailsBox.should_show ? 1 : 0, {
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._updateThumbnailsBox(),
});
});
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(
this,
this._workspaceAdjustment,
this._stateAdjustment);
this._appDisplay = new AppDisplay.AppDisplay();
this.add_child(this._searchEntryBin);
this.add_child(this._appDisplay);
this.add_child(this.dash);
this.add_child(this._searchController);
this.add_child(this._thumbnailsBox);
this.add_child(this._workspacesDisplay);
this.layout_manager = new ControlsManagerLayout(
this._searchEntryBin,
this._appDisplay,
this._workspacesDisplay,
this._thumbnailsBox,
this._searchController,
this.dash,
this._stateAdjustment);
this.dash.showAppsButton.connect('notify::checked',
this._onShowAppsButtonToggled.bind(this));
Main.ctrlAltTabManager.addGroup(
this.appDisplay,
_('Applications'),
'view-app-grid-symbolic', {
proxy: this,
focusCallback: () => {
this.dash.showAppsButton.checked = true;
this.appDisplay.navigate_focus(
null, St.DirectionType.TAB_FORWARD, false);
},
});
Main.ctrlAltTabManager.addGroup(
this._workspacesDisplay,
_('Windows'),
'focus-windows-symbolic', {
proxy: this,
focusCallback: () => {
this.dash.showAppsButton.checked = false;
this._workspacesDisplay.navigate_focus(
null, St.DirectionType.TAB_FORWARD, false);
},
});
this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
global.display.connect('overlay-key', () => {
if (this._a11ySettings.get_boolean('stickykeys-enable'))
return;
const { initialState, finalState, transitioning } =
this._stateAdjustment.getStateTransitionParams();
if (transitioning && finalState > initialState)
this._shiftState(Meta.MotionDirection.UP);
else
Main.overview.toggle();
});
Main.wm.addKeybinding(
'toggle-application-view',
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
this._toggleAppsPage.bind(this));
Main.wm.addKeybinding('shift-overview-up',
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => this._shiftState(Meta.MotionDirection.UP));
Main.wm.addKeybinding('shift-overview-down',
new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => this._shiftState(Meta.MotionDirection.DOWN));
this.connect('destroy', this._onDestroy.bind(this));
this._update();
}
_getFitModeForState(state) {
switch (state) {
case ControlsState.HIDDEN:
case ControlsState.WINDOW_PICKER:
return WorkspacesView.FitMode.SINGLE;
case ControlsState.APP_GRID:
return WorkspacesView.FitMode.ALL;
default:
return WorkspacesView.FitMode.SINGLE;
}
}
_getThumbnailsBoxParams() {
const { initialState, finalState, progress } =
this._stateAdjustment.getStateTransitionParams();
const paramsForState = s => {
let opacity, scale, translationY;
switch (s) {
case ControlsState.HIDDEN:
case ControlsState.WINDOW_PICKER:
opacity = 255;
scale = 1;
translationY = 0;
break;
case ControlsState.APP_GRID:
opacity = 0;
scale = 0.5;
translationY = this._thumbnailsBox.height / 2;
break;
default:
opacity = 255;
scale = 1;
translationY = 0;
break;
}
return { opacity, scale, translationY };
};
const initialParams = paramsForState(initialState);
const finalParams = paramsForState(finalState);
return [
Util.lerp(initialParams.opacity, finalParams.opacity, progress),
Util.lerp(initialParams.scale, finalParams.scale, progress),
Util.lerp(initialParams.translationY, finalParams.translationY, progress),
];
}
_updateThumbnailsBox(animate = false) {
const { shouldShow } = this._thumbnailsBox;
const { searchActive } = this._searchController;
const [opacity, scale, translationY] = this._getThumbnailsBoxParams();
const thumbnailsBoxVisible = shouldShow && !searchActive && opacity !== 0;
if (thumbnailsBoxVisible) {
this._thumbnailsBox.opacity = 0;
this._thumbnailsBox.visible = thumbnailsBoxVisible;
}
const params = {
opacity: searchActive ? 0 : opacity,
duration: animate ? SIDE_CONTROLS_ANIMATION_TIME : 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this._thumbnailsBox.visible = thumbnailsBoxVisible),
};
if (!searchActive) {
params.scale_x = scale;
params.scale_y = scale;
params.translation_y = translationY;
}
this._thumbnailsBox.ease(params);
}
_updateAppDisplayVisibility() {
this._appDisplay.visible =
this._stateAdjustment.value > ControlsState.WINDOW_PICKER &&
!this._searchController.searchActive;
}
_update() {
const params = this._stateAdjustment.getStateTransitionParams();
const fitMode = Util.lerp(
this._getFitModeForState(params.initialState),
this._getFitModeForState(params.finalState),
params.progress);
const { fitModeAdjustment } = this._workspacesDisplay;
fitModeAdjustment.value = fitMode;
this._updateThumbnailsBox();
this._updateAppDisplayVisibility();
}
_onSearchChanged() {
const { searchActive } = this._searchController;
if (!searchActive) {
this._updateAppDisplayVisibility();
this._workspacesDisplay.reactive = true;
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
} else {
this._searchController.show();
}
this._updateThumbnailsBox(true);
this._appDisplay.ease({
opacity: searchActive ? 0 : 255,
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._updateAppDisplayVisibility(),
});
this._workspacesDisplay.ease({
opacity: searchActive ? 0 : 255,
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._workspacesDisplay.reactive = !searchActive;
this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
},
});
this._searchController.ease({
opacity: searchActive ? 255 : 0,
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this._searchController.visible = searchActive),
});
}
_onShowAppsButtonToggled() {
if (this._ignoreShowAppsButtonToggle)
return;
const checked = this.dash.showAppsButton.checked;
const value = checked
? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER;
this._stateAdjustment.remove_transition('value');
this._stateAdjustment.ease(value, {
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
_toggleAppsPage() {
if (Main.overview.visible) {
const checked = this.dash.showAppsButton.checked;
this.dash.showAppsButton.checked = !checked;
} else {
Main.overview.show(ControlsState.APP_GRID);
}
}
_shiftState(direction) {
let { currentState, finalState } = this._stateAdjustment.getStateTransitionParams();
if (direction === Meta.MotionDirection.DOWN)
finalState = Math.max(finalState - 1, ControlsState.HIDDEN);
else if (direction === Meta.MotionDirection.UP)
finalState = Math.min(finalState + 1, ControlsState.APP_GRID);
if (finalState === currentState)
return;
if (currentState === ControlsState.HIDDEN &&
finalState === ControlsState.WINDOW_PICKER) {
Main.overview.show();
} else if (finalState === ControlsState.HIDDEN) {
Main.overview.hide();
} else {
this._stateAdjustment.ease(finalState, {
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this.dash.showAppsButton.checked =
finalState === ControlsState.APP_GRID;
},
});
}
}
_onDestroy() {
global.workspace_manager.disconnect(this._nWorkspacesNotifyId);
}
_updateAdjustment() {
let workspaceManager = global.workspace_manager;
let newNumWorkspaces = workspaceManager.n_workspaces;
let activeIndex = workspaceManager.get_active_workspace_index();
this._workspaceAdjustment.upper = newNumWorkspaces;
// A workspace might have been inserted or removed before the active
// one, causing the adjustment to go out of sync, so update the value
this._workspaceAdjustment.remove_transition('value');
this._workspaceAdjustment.value = activeIndex;
}
vfunc_unmap() {
this._workspacesDisplay.hide();
super.vfunc_unmap();
}
animateToOverview(state, callback) {
this._ignoreShowAppsButtonToggle = true;
this._searchController.prepareToEnterOverview();
this._workspacesDisplay.prepareToEnterOverview();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop();
this._stateAdjustment.value = ControlsState.HIDDEN;
this._stateAdjustment.ease(state, {
duration: Overview.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onStopped: () => {
if (callback)
callback();
},
});
this.dash.showAppsButton.checked =
state === ControlsState.APP_GRID;
this._ignoreShowAppsButtonToggle = false;
}
animateFromOverview(callback) {
this._ignoreShowAppsButtonToggle = true;
this._workspacesDisplay.prepareToLeaveOverview();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeInDesktop();
this._stateAdjustment.ease(ControlsState.HIDDEN, {
duration: Overview.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onStopped: () => {
this.dash.showAppsButton.checked = false;
this._ignoreShowAppsButtonToggle = false;
if (callback)
callback();
},
});
}
getWorkspacesBoxForState(state) {
return this.layoutManager.getWorkspacesBoxForState(state);
}
gestureBegin(tracker) {
const baseDistance = global.screen_height;
const progress = this._stateAdjustment.value;
const points = [
ControlsState.HIDDEN,
ControlsState.WINDOW_PICKER,
ControlsState.APP_GRID,
];
const transition = this._stateAdjustment.get_transition('value');
const cancelProgress = transition
? transition.get_interval().peek_final_value()
: Math.round(progress);
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
this._workspacesDisplay.prepareToEnterOverview();
this._searchController.prepareToEnterOverview();
this._stateAdjustment.gestureInProgress = true;
}
gestureProgress(progress) {
this._stateAdjustment.value = progress;
}
gestureEnd(target, duration, onComplete) {
if (target === ControlsState.HIDDEN)
this._workspacesDisplay.prepareToLeaveOverview();
this.dash.showAppsButton.checked =
target === ControlsState.APP_GRID;
this._stateAdjustment.remove_transition('value');
this._stateAdjustment.ease(target, {
duration,
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
onComplete,
});
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;
}
get appDisplay() {
return this._appDisplay;
}
});