Adding upstream version 48.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
parent
0d8723e422
commit
1fcdbd5df9
1059 changed files with 623842 additions and 0 deletions
864
js/ui/overviewControls.js
Normal file
864
js/ui/overviewControls.js
Normal file
|
@ -0,0 +1,864 @@
|
|||
import GLib from 'gi://GLib';
|
||||
import Clutter from 'gi://Clutter';
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
import Meta from 'gi://Meta';
|
||||
import Shell from 'gi://Shell';
|
||||
import St from 'gi://St';
|
||||
|
||||
import * as AppDisplay from './appDisplay.js';
|
||||
import * as Dash from './dash.js';
|
||||
import * as Layout from './layout.js';
|
||||
import * as Main from './main.js';
|
||||
import * as Overview from './overview.js';
|
||||
import * as SearchController from './searchController.js';
|
||||
import * as Util from '../misc/util.js';
|
||||
import * as WindowManager from './windowManager.js';
|
||||
import * as WorkspaceThumbnail from './workspaceThumbnail.js';
|
||||
import * as WorkspacesView from './workspacesView.js';
|
||||
|
||||
export const SMALL_WORKSPACE_RATIO = 0.15;
|
||||
const DASH_MAX_HEIGHT_RATIO = 0.16;
|
||||
const VERTICAL_SPACING_RATIO = 0.02;
|
||||
const THUMBNAILS_SPACING_ADJUSTMENT_TOP = 0.6;
|
||||
const THUMBNAILS_SPACING_ADJUSTMENT_BOTTOM = 0.4;
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
|
||||
export const SIDE_CONTROLS_ANIMATION_TIME = 250;
|
||||
|
||||
/** @enum {number} */
|
||||
export const ControlsState = {
|
||||
HIDDEN: 0,
|
||||
WINDOW_PICKER: 1,
|
||||
APP_GRID: 2,
|
||||
};
|
||||
|
||||
const ControlsManagerLayout = GObject.registerClass(
|
||||
class ControlsManagerLayout extends Clutter.LayoutManager {
|
||||
_init(searchEntry, appDisplay, workspacesDisplay, workspacesThumbnails,
|
||||
searchController, dash, stateAdjustment) {
|
||||
super._init();
|
||||
|
||||
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());
|
||||
|
||||
this._workAreaBox = new Clutter.ActorBox();
|
||||
global.display.connectObject(
|
||||
'workareas-changed', () => this._updateWorkAreaBox(),
|
||||
this);
|
||||
Main.layoutManager.connectObject(
|
||||
'monitors-changed', () => this._updateWorkAreaBox(),
|
||||
this);
|
||||
this._updateWorkAreaBox();
|
||||
}
|
||||
|
||||
_updateWorkAreaBox() {
|
||||
const monitor = Main.layoutManager.primaryMonitor;
|
||||
if (!monitor)
|
||||
return;
|
||||
|
||||
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
|
||||
const startX = workArea.x - monitor.x;
|
||||
const startY = workArea.y - monitor.y;
|
||||
this._workAreaBox.set_origin(startX, startY);
|
||||
this._workAreaBox.set_size(workArea.width, workArea.height);
|
||||
}
|
||||
|
||||
_computeWorkspacesBoxForState(state, box, searchHeight, dashHeight, thumbnailsHeight, spacing) {
|
||||
const workspaceBox = box.copy();
|
||||
const [width, height] = workspaceBox.get_size();
|
||||
const {y1: startY} = this._workAreaBox;
|
||||
const {expandFraction} = this._workspacesThumbnails;
|
||||
|
||||
switch (state) {
|
||||
case ControlsState.HIDDEN:
|
||||
workspaceBox.set_origin(...this._workAreaBox.get_origin());
|
||||
workspaceBox.set_size(...this._workAreaBox.get_size());
|
||||
break;
|
||||
case ControlsState.WINDOW_PICKER:
|
||||
workspaceBox.set_origin(0,
|
||||
startY + searchHeight + Math.round(spacing * THUMBNAILS_SPACING_ADJUSTMENT_TOP) +
|
||||
thumbnailsHeight + Math.round(spacing * THUMBNAILS_SPACING_ADJUSTMENT_BOTTOM) * expandFraction);
|
||||
workspaceBox.set_size(width,
|
||||
height -
|
||||
dashHeight - spacing -
|
||||
searchHeight - Math.round(spacing * THUMBNAILS_SPACING_ADJUSTMENT_TOP) -
|
||||
thumbnailsHeight - Math.round(spacing * THUMBNAILS_SPACING_ADJUSTMENT_BOTTOM) * 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, searchHeight, dashHeight, workspacesBox, spacing) {
|
||||
const [width, height] = box.get_size();
|
||||
const {y1: startY} = this._workAreaBox;
|
||||
const appDisplayBox = new Clutter.ActorBox();
|
||||
|
||||
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 + workspacesBox.get_height() + spacing);
|
||||
break;
|
||||
}
|
||||
|
||||
appDisplayBox.set_size(width,
|
||||
height -
|
||||
searchHeight - spacing -
|
||||
workspacesBox.get_height() - spacing -
|
||||
dashHeight - spacing);
|
||||
|
||||
return appDisplayBox;
|
||||
}
|
||||
|
||||
_runPostAllocation() {
|
||||
if (this._postAllocationCallbacks.length === 0)
|
||||
return;
|
||||
|
||||
this._postAllocationCallbacks.forEach(cb => cb());
|
||||
this._postAllocationCallbacks = [];
|
||||
}
|
||||
|
||||
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 startY = this._workAreaBox.y1;
|
||||
box.y1 += startY;
|
||||
const [width, height] = box.get_size();
|
||||
const spacing = Math.round(height * VERTICAL_SPACING_RATIO);
|
||||
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 * this._workspacesThumbnails.maxThumbnailScale);
|
||||
childBox.set_origin(0, startY + searchHeight + Math.round(spacing * THUMBNAILS_SPACING_ADJUSTMENT_TOP));
|
||||
childBox.set_size(width, thumbnailsHeight);
|
||||
this._workspacesThumbnails.allocate(childBox);
|
||||
}
|
||||
|
||||
// Workspaces
|
||||
let params = [box, searchHeight, dashHeight, thumbnailsHeight, spacing];
|
||||
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, searchHeight, dashHeight, workspaceAppGridBox, spacing];
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
export const OverviewAdjustment = GObject.registerClass({
|
||||
Properties: {
|
||||
'gesture-in-progress': GObject.ParamSpec.boolean(
|
||||
'gesture-in-progress', null, null,
|
||||
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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export const 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();
|
||||
|
||||
this._workspaceAdjustment = Main.createWorkspacesAdjustment(this);
|
||||
|
||||
this._stateAdjustment = new OverviewAdjustment(this);
|
||||
this._stateAdjustment.connect('notify::value', this._update.bind(this));
|
||||
|
||||
this._searchController = new SearchController.SearchController(
|
||||
this._searchEntry,
|
||||
this.dash.showAppsButton);
|
||||
this._searchController.connect('notify::search-active', this._onSearchChanged.bind(this));
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', () => {
|
||||
this._thumbnailsBox.setMonitorIndex(Main.layoutManager.primaryIndex);
|
||||
});
|
||||
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,
|
||||
_('Apps'),
|
||||
'shell-focus-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'),
|
||||
'shell-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});
|
||||
|
||||
this._lastOverlayKeyTime = 0;
|
||||
global.display.connect('overlay-key', () => {
|
||||
if (this._a11ySettings.get_boolean('stickykeys-enable'))
|
||||
return;
|
||||
|
||||
const {initialState, finalState, transitioning} =
|
||||
this._stateAdjustment.getStateTransitionParams();
|
||||
|
||||
const time = GLib.get_monotonic_time() / 1000;
|
||||
const timeDiff = time - this._lastOverlayKeyTime;
|
||||
this._lastOverlayKeyTime = time;
|
||||
|
||||
const shouldShift = St.Settings.get().enable_animations
|
||||
? transitioning && finalState > initialState
|
||||
: Main.overview.visible && timeDiff < Overview.ANIMATION_TIME;
|
||||
|
||||
if (shouldShift)
|
||||
this._shiftState(Meta.MotionDirection.UP);
|
||||
else
|
||||
Main.overview.toggle();
|
||||
});
|
||||
|
||||
// connect_after to give search controller first dibs on the event
|
||||
global.stage.connect_after('key-press-event', (actor, event) => {
|
||||
if (this._searchController.searchActive)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (global.stage.key_focus &&
|
||||
!this.contains(global.stage.key_focus))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
const {finalState} =
|
||||
this._stateAdjustment.getStateTransitionParams();
|
||||
let keynavDisplay;
|
||||
|
||||
if (finalState === ControlsState.WINDOW_PICKER)
|
||||
keynavDisplay = this._workspacesDisplay;
|
||||
else if (finalState === ControlsState.APP_GRID)
|
||||
keynavDisplay = this._appDisplay;
|
||||
|
||||
if (!keynavDisplay)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
const symbol = event.get_key_symbol();
|
||||
if (symbol === Clutter.KEY_Tab || symbol === Clutter.KEY_Down) {
|
||||
keynavDisplay.navigate_focus(
|
||||
null, St.DirectionType.TAB_FORWARD, false);
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (symbol === Clutter.KEY_ISO_Left_Tab) {
|
||||
keynavDisplay.navigate_focus(
|
||||
null, St.DirectionType.TAB_BACKWARD, false);
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
});
|
||||
|
||||
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._update();
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
}
|
||||
|
||||
_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.set({
|
||||
visible: thumbnailsBoxVisible,
|
||||
expandFraction: thumbnailsBoxVisible ? 1.0 : 0.0,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
if (!searchActive) {
|
||||
params.scale_x = scale;
|
||||
params.scale_y = scale;
|
||||
params.translation_y = translationY;
|
||||
}
|
||||
|
||||
this._thumbnailsBox.ease(params);
|
||||
}
|
||||
|
||||
_updateAppDisplayVisibility(stateTransitionParams = null) {
|
||||
if (!stateTransitionParams)
|
||||
stateTransitionParams = this._stateAdjustment.getStateTransitionParams();
|
||||
|
||||
const {initialState, finalState} = stateTransitionParams;
|
||||
const state = Math.max(initialState, finalState);
|
||||
|
||||
this._appDisplay.visible =
|
||||
state > 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(params);
|
||||
}
|
||||
|
||||
_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;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
vfunc_unmap() {
|
||||
super.vfunc_unmap();
|
||||
this._workspacesDisplay?.hide();
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
delete this._searchEntryBin;
|
||||
delete this._appDisplay;
|
||||
delete this.dash;
|
||||
delete this._searchController;
|
||||
delete this._thumbnailsBox;
|
||||
delete this._workspacesDisplay;
|
||||
}
|
||||
|
||||
prepareToEnterOverview() {
|
||||
this._searchController.prepareToEnterOverview();
|
||||
this._workspacesDisplay.prepareToEnterOverview();
|
||||
}
|
||||
|
||||
prepareToLeaveOverview() {
|
||||
this._searchController.prepareToLeaveOverview();
|
||||
this._workspacesDisplay.prepareToLeaveOverview();
|
||||
}
|
||||
|
||||
animateToOverview(state, callback) {
|
||||
this._ignoreShowAppsButtonToggle = true;
|
||||
|
||||
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._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);
|
||||
this._stateAdjustment.remove_transition('value');
|
||||
|
||||
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
|
||||
this.prepareToEnterOverview();
|
||||
this._stateAdjustment.gestureInProgress = true;
|
||||
}
|
||||
|
||||
gestureProgress(progress) {
|
||||
this._stateAdjustment.value = progress;
|
||||
}
|
||||
|
||||
gestureEnd(target, duration, onComplete) {
|
||||
if (target === ControlsState.HIDDEN)
|
||||
this.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() {
|
||||
this._ignoreShowAppsButtonToggle = true;
|
||||
|
||||
this.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 resolve the promise there.
|
||||
this.dash.translation_y = this.dash.height + this.dash.margin_bottom;
|
||||
return new Promise(resolve => {
|
||||
this.dash.ease({
|
||||
translation_y: 0,
|
||||
delay: STARTUP_ANIMATION_TIME,
|
||||
duration: STARTUP_ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onStopped: () => resolve(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get searchController() {
|
||||
return this._searchController;
|
||||
}
|
||||
|
||||
get searchEntry() {
|
||||
return this._searchEntry;
|
||||
}
|
||||
|
||||
get appDisplay() {
|
||||
return this._appDisplay;
|
||||
}
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue