3d6bf43649
Wayland clients can request their surfaces to be fullscreened before commiting a buffer which means that we need to handle fullscreen requests for which the old size is 0x0, preferably without warnings. Since the mapping animation also runs for these windows, we can simply bail out and ignore the fullscreen size change. https://bugzilla.gnome.org/show_bug.cgi?id=770345
1960 lines
76 KiB
JavaScript
1960 lines
76 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const GLib = imports.gi.GLib;
|
|
const Gio = imports.gi.Gio;
|
|
const Lang = imports.lang;
|
|
const Mainloop = imports.mainloop;
|
|
const Meta = imports.gi.Meta;
|
|
const Pango = imports.gi.Pango;
|
|
const St = imports.gi.St;
|
|
const Shell = imports.gi.Shell;
|
|
const Signals = imports.signals;
|
|
|
|
const AltTab = imports.ui.altTab;
|
|
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
|
const Main = imports.ui.main;
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
const Tweener = imports.ui.tweener;
|
|
const WindowMenu = imports.ui.windowMenu;
|
|
const PadOsd = imports.ui.padOsd;
|
|
|
|
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
|
const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2;
|
|
const SHOW_WINDOW_ANIMATION_TIME = 0.15;
|
|
const DIALOG_SHOW_WINDOW_ANIMATION_TIME = 0.1;
|
|
const DESTROY_WINDOW_ANIMATION_TIME = 0.15;
|
|
const DIALOG_DESTROY_WINDOW_ANIMATION_TIME = 0.1;
|
|
const WINDOW_ANIMATION_TIME = 0.25;
|
|
const DIM_BRIGHTNESS = -0.3;
|
|
const DIM_TIME = 0.500;
|
|
const UNDIM_TIME = 0.250;
|
|
|
|
const DISPLAY_REVERT_TIMEOUT = 20; // in seconds - keep in sync with mutter
|
|
const ONE_SECOND = 1000; // in ms
|
|
|
|
const DisplayChangeDialog = new Lang.Class({
|
|
Name: 'DisplayChangeDialog',
|
|
Extends: ModalDialog.ModalDialog,
|
|
|
|
_init: function(wm) {
|
|
this.parent({ styleClass: 'prompt-dialog' });
|
|
|
|
this._wm = wm;
|
|
|
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
|
vertical: false });
|
|
this.contentLayout.add(mainContentBox,
|
|
{ x_fill: true,
|
|
y_fill: true });
|
|
|
|
let icon = new St.Icon({ icon_name: 'preferences-desktop-display-symbolic' });
|
|
mainContentBox.add(icon,
|
|
{ x_fill: true,
|
|
y_fill: false,
|
|
x_align: St.Align.END,
|
|
y_align: St.Align.START });
|
|
|
|
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
|
|
vertical: true });
|
|
mainContentBox.add(messageBox,
|
|
{ expand: true, y_align: St.Align.START });
|
|
|
|
let subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
|
|
text: _("Do you want to keep these display settings?") });
|
|
messageBox.add(subjectLabel,
|
|
{ y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
this._countDown = DISPLAY_REVERT_TIMEOUT;
|
|
let message = this._formatCountDown();
|
|
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
|
text: this._formatCountDown() });
|
|
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
|
this._descriptionLabel.clutter_text.line_wrap = true;
|
|
|
|
messageBox.add(this._descriptionLabel,
|
|
{ y_fill: true,
|
|
y_align: St.Align.START });
|
|
|
|
/* Translators: this and the following message should be limited in lenght,
|
|
to avoid ellipsizing the labels.
|
|
*/
|
|
this._cancelButton = this.addButton({ label: _("Revert Settings"),
|
|
action: Lang.bind(this, this._onFailure),
|
|
key: Clutter.Escape });
|
|
this._okButton = this.addButton({ label: _("Keep Changes"),
|
|
action: Lang.bind(this, this._onSuccess),
|
|
default: true });
|
|
|
|
this._timeoutId = Mainloop.timeout_add(ONE_SECOND, Lang.bind(this, this._tick));
|
|
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._tick');
|
|
},
|
|
|
|
close: function(timestamp) {
|
|
if (this._timeoutId > 0) {
|
|
Mainloop.source_remove(this._timeoutId);
|
|
this._timeoutId = 0;
|
|
}
|
|
|
|
this.parent(timestamp);
|
|
},
|
|
|
|
_formatCountDown: function() {
|
|
let fmt = ngettext("Settings changes will revert in %d second",
|
|
"Settings changes will revert in %d seconds");
|
|
return fmt.format(this._countDown);
|
|
},
|
|
|
|
_tick: function() {
|
|
this._countDown--;
|
|
|
|
if (this._countDown == 0) {
|
|
/* mutter already takes care of failing at timeout */
|
|
this._timeoutId = 0;
|
|
this.close();
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
|
|
this._descriptionLabel.text = this._formatCountDown();
|
|
return GLib.SOURCE_CONTINUE;
|
|
},
|
|
|
|
_onFailure: function() {
|
|
this._wm.complete_display_change(false);
|
|
this.close();
|
|
},
|
|
|
|
_onSuccess: function() {
|
|
this._wm.complete_display_change(true);
|
|
this.close();
|
|
},
|
|
});
|
|
|
|
const WindowDimmer = new Lang.Class({
|
|
Name: 'WindowDimmer',
|
|
|
|
_init: function(actor) {
|
|
this._brightnessEffect = new Clutter.BrightnessContrastEffect();
|
|
actor.add_effect(this._brightnessEffect);
|
|
this.actor = actor;
|
|
this._enabled = true;
|
|
this._dimFactor = 0.0;
|
|
this._syncEnabled();
|
|
},
|
|
|
|
_syncEnabled: function() {
|
|
this._brightnessEffect.enabled = (this._enabled && this._dimFactor > 0);
|
|
},
|
|
|
|
setEnabled: function(enabled) {
|
|
this._enabled = enabled;
|
|
this._syncEnabled();
|
|
},
|
|
|
|
set dimFactor(factor) {
|
|
this._dimFactor = factor;
|
|
this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS);
|
|
this._syncEnabled();
|
|
},
|
|
|
|
get dimFactor() {
|
|
return this._dimFactor;
|
|
}
|
|
});
|
|
|
|
function getWindowDimmer(actor) {
|
|
let enabled = Meta.prefs_get_attach_modal_dialogs();
|
|
if (actor._windowDimmer)
|
|
actor._windowDimmer.setEnabled(enabled);
|
|
|
|
if (enabled) {
|
|
if (!actor._windowDimmer)
|
|
actor._windowDimmer = new WindowDimmer(actor);
|
|
return actor._windowDimmer;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When the last window closed on a workspace is a dialog or splash
|
|
* screen, we assume that it might be an initial window shown before
|
|
* the main window of an application, and give the app a grace period
|
|
* where it can map another window before we remove the workspace.
|
|
*/
|
|
const LAST_WINDOW_GRACE_TIME = 1000;
|
|
|
|
const WorkspaceTracker = new Lang.Class({
|
|
Name: 'WorkspaceTracker',
|
|
|
|
_init: function(wm) {
|
|
this._wm = wm;
|
|
|
|
this._workspaces = [];
|
|
this._checkWorkspacesId = 0;
|
|
|
|
this._pauseWorkspaceCheck = false;
|
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
|
tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces));
|
|
|
|
global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged));
|
|
global.window_manager.connect('switch-workspace', Lang.bind(this, this._queueCheckWorkspaces));
|
|
|
|
global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor));
|
|
global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor));
|
|
global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked));
|
|
|
|
this._workspaceSettings = this._getWorkspaceSettings();
|
|
this._workspaceSettings.connect('changed::dynamic-workspaces', Lang.bind(this, this._queueCheckWorkspaces));
|
|
|
|
this._nWorkspacesChanged();
|
|
},
|
|
|
|
_getWorkspaceSettings: function() {
|
|
let settings = global.get_overrides_settings();
|
|
if (settings &&
|
|
settings.settings_schema.list_keys().indexOf('dynamic-workspaces') > -1)
|
|
return settings;
|
|
return new Gio.Settings({ schema_id: 'org.gnome.mutter' });
|
|
},
|
|
|
|
blockUpdates: function() {
|
|
this._pauseWorkspaceCheck = true;
|
|
},
|
|
|
|
unblockUpdates: function() {
|
|
this._pauseWorkspaceCheck = false;
|
|
},
|
|
|
|
_checkWorkspaces: function() {
|
|
let i;
|
|
let emptyWorkspaces = [];
|
|
|
|
if (!Meta.prefs_get_dynamic_workspaces()) {
|
|
this._checkWorkspacesId = 0;
|
|
return false;
|
|
}
|
|
|
|
// Update workspaces only if Dynamic Workspace Management has not been paused by some other function
|
|
if (this._pauseWorkspaceCheck)
|
|
return true;
|
|
|
|
for (i = 0; i < this._workspaces.length; i++) {
|
|
let lastRemoved = this._workspaces[i]._lastRemovedWindow;
|
|
if ((lastRemoved &&
|
|
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
|
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
|
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
|
this._workspaces[i]._keepAliveId)
|
|
emptyWorkspaces[i] = false;
|
|
else
|
|
emptyWorkspaces[i] = true;
|
|
}
|
|
|
|
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
|
for (i = 0; i < sequences.length; i++) {
|
|
let index = sequences[i].get_workspace();
|
|
if (index >= 0 && index <= global.screen.n_workspaces)
|
|
emptyWorkspaces[index] = false;
|
|
}
|
|
|
|
let windows = global.get_window_actors();
|
|
for (i = 0; i < windows.length; i++) {
|
|
let actor = windows[i];
|
|
let win = actor.get_meta_window();
|
|
|
|
if (win.is_on_all_workspaces())
|
|
continue;
|
|
|
|
let workspaceIndex = win.get_workspace().index();
|
|
emptyWorkspaces[workspaceIndex] = false;
|
|
}
|
|
|
|
// If we don't have an empty workspace at the end, add one
|
|
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
|
global.screen.append_new_workspace(false, global.get_current_time());
|
|
emptyWorkspaces.push(false);
|
|
}
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
emptyWorkspaces[activeWorkspaceIndex] = false;
|
|
|
|
// Delete other empty workspaces; do it from the end to avoid index changes
|
|
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
|
if (emptyWorkspaces[i])
|
|
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
|
|
}
|
|
|
|
this._checkWorkspacesId = 0;
|
|
return false;
|
|
},
|
|
|
|
keepWorkspaceAlive: function(workspace, duration) {
|
|
if (workspace._keepAliveId)
|
|
Mainloop.source_remove(workspace._keepAliveId);
|
|
|
|
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
|
|
workspace._keepAliveId = 0;
|
|
this._queueCheckWorkspaces();
|
|
return GLib.SOURCE_REMOVE;
|
|
}));
|
|
GLib.Source.set_name_by_id(workspace._keepAliveId, '[gnome-shell] this._queueCheckWorkspaces');
|
|
},
|
|
|
|
_windowRemoved: function(workspace, window) {
|
|
workspace._lastRemovedWindow = window;
|
|
this._queueCheckWorkspaces();
|
|
let id = Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, Lang.bind(this, function() {
|
|
if (workspace._lastRemovedWindow == window) {
|
|
workspace._lastRemovedWindow = null;
|
|
this._queueCheckWorkspaces();
|
|
}
|
|
return GLib.SOURCE_REMOVE;
|
|
}));
|
|
GLib.Source.set_name_by_id(id, '[gnome-shell] this._queueCheckWorkspaces');
|
|
},
|
|
|
|
_windowLeftMonitor: function(metaScreen, monitorIndex, metaWin) {
|
|
// If the window left the primary monitor, that
|
|
// might make that workspace empty
|
|
if (monitorIndex == Main.layoutManager.primaryIndex)
|
|
this._queueCheckWorkspaces();
|
|
},
|
|
|
|
_windowEnteredMonitor: function(metaScreen, monitorIndex, metaWin) {
|
|
// If the window entered the primary monitor, that
|
|
// might make that workspace non-empty
|
|
if (monitorIndex == Main.layoutManager.primaryIndex)
|
|
this._queueCheckWorkspaces();
|
|
},
|
|
|
|
_windowsRestacked: function() {
|
|
// Figure out where the pointer is in case we lost track of
|
|
// it during a grab. (In particular, if a trayicon popup menu
|
|
// is dismissed, see if we need to close the message tray.)
|
|
global.sync_pointer();
|
|
},
|
|
|
|
_queueCheckWorkspaces: function() {
|
|
if (this._checkWorkspacesId == 0)
|
|
this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._checkWorkspaces));
|
|
},
|
|
|
|
_nWorkspacesChanged: function() {
|
|
let oldNumWorkspaces = this._workspaces.length;
|
|
let newNumWorkspaces = global.screen.n_workspaces;
|
|
|
|
if (oldNumWorkspaces == newNumWorkspaces)
|
|
return false;
|
|
|
|
let lostWorkspaces = [];
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
let w;
|
|
|
|
// Assume workspaces are only added at the end
|
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
this._workspaces[w] = global.screen.get_workspace_by_index(w);
|
|
|
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
let workspace = this._workspaces[w];
|
|
workspace._windowAddedId = workspace.connect('window-added', Lang.bind(this, this._queueCheckWorkspaces));
|
|
workspace._windowRemovedId = workspace.connect('window-removed', Lang.bind(this, this._windowRemoved));
|
|
}
|
|
|
|
} else {
|
|
// Assume workspaces are only removed sequentially
|
|
// (e.g. 2,3,4 - not 2,4,7)
|
|
let removedIndex;
|
|
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
for (let w = 0; w < oldNumWorkspaces; w++) {
|
|
let workspace = global.screen.get_workspace_by_index(w);
|
|
if (this._workspaces[w] != workspace) {
|
|
removedIndex = w;
|
|
break;
|
|
}
|
|
}
|
|
|
|
let lostWorkspaces = this._workspaces.splice(removedIndex, removedNum);
|
|
lostWorkspaces.forEach(function(workspace) {
|
|
workspace.disconnect(workspace._windowAddedId);
|
|
workspace.disconnect(workspace._windowRemovedId);
|
|
});
|
|
}
|
|
|
|
this._queueCheckWorkspaces();
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
const TilePreview = new Lang.Class({
|
|
Name: 'TilePreview',
|
|
|
|
_init: function() {
|
|
this.actor = new St.Widget();
|
|
global.window_group.add_actor(this.actor);
|
|
|
|
this._reset();
|
|
this._showing = false;
|
|
},
|
|
|
|
show: function(window, tileRect, monitorIndex) {
|
|
let windowActor = window.get_compositor_private();
|
|
if (!windowActor)
|
|
return;
|
|
|
|
global.window_group.set_child_below_sibling(this.actor, windowActor);
|
|
|
|
if (this._rect && this._rect.equal(tileRect))
|
|
return;
|
|
|
|
let changeMonitor = (this._monitorIndex == -1 ||
|
|
this._monitorIndex != monitorIndex);
|
|
|
|
this._monitorIndex = monitorIndex;
|
|
this._rect = tileRect;
|
|
|
|
let monitor = Main.layoutManager.monitors[monitorIndex];
|
|
|
|
this._updateStyle(monitor);
|
|
|
|
if (!this._showing || changeMonitor) {
|
|
let monitorRect = new Meta.Rectangle({ x: monitor.x,
|
|
y: monitor.y,
|
|
width: monitor.width,
|
|
height: monitor.height });
|
|
let [, rect] = window.get_frame_rect().intersect(monitorRect);
|
|
this.actor.set_size(rect.width, rect.height);
|
|
this.actor.set_position(rect.x, rect.y);
|
|
this.actor.opacity = 0;
|
|
}
|
|
|
|
this._showing = true;
|
|
this.actor.show();
|
|
Tweener.addTween(this.actor,
|
|
{ x: tileRect.x,
|
|
y: tileRect.y,
|
|
width: tileRect.width,
|
|
height: tileRect.height,
|
|
opacity: 255,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
},
|
|
|
|
hide: function() {
|
|
if (!this._showing)
|
|
return;
|
|
|
|
this._showing = false;
|
|
Tweener.addTween(this.actor,
|
|
{ opacity: 0,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: Lang.bind(this, this._reset)
|
|
});
|
|
},
|
|
|
|
_reset: function() {
|
|
this.actor.hide();
|
|
this._rect = null;
|
|
this._monitorIndex = -1;
|
|
},
|
|
|
|
_updateStyle: function(monitor) {
|
|
let styles = ['tile-preview'];
|
|
if (this._monitorIndex == Main.layoutManager.primaryIndex)
|
|
styles.push('on-primary');
|
|
if (this._rect.x == monitor.x)
|
|
styles.push('tile-preview-left');
|
|
if (this._rect.x + this._rect.width == monitor.x + monitor.width)
|
|
styles.push('tile-preview-right');
|
|
|
|
this.actor.style_class = styles.join(' ');
|
|
}
|
|
});
|
|
|
|
const TouchpadWorkspaceSwitchAction = new Lang.Class({
|
|
Name: 'TouchpadWorkspaceSwitchAction',
|
|
|
|
_init: function(actor) {
|
|
this._dx = 0;
|
|
this._dy = 0;
|
|
actor.connect('captured-event', Lang.bind(this, this._handleEvent));
|
|
},
|
|
|
|
_checkActivated: function() {
|
|
const MOTION_THRESHOLD = 50;
|
|
let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW;
|
|
let dir;
|
|
|
|
if ((allowedModes & Main.actionMode) == 0)
|
|
return;
|
|
|
|
if (this._dy < -MOTION_THRESHOLD)
|
|
dir = Meta.MotionDirection.DOWN;
|
|
else if (this._dy > MOTION_THRESHOLD)
|
|
dir = Meta.MotionDirection.UP;
|
|
else if (this._dx < -MOTION_THRESHOLD)
|
|
dir = Meta.MotionDirection.RIGHT;
|
|
else if (this._dx > MOTION_THRESHOLD)
|
|
dir = Meta.MotionDirection.LEFT;
|
|
else
|
|
return;
|
|
|
|
this.emit('activated', dir);
|
|
},
|
|
|
|
_handleEvent: function(actor, event) {
|
|
if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE)
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
if (event.get_touchpad_gesture_finger_count() != 4)
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) {
|
|
let [dx, dy] = event.get_gesture_motion_delta(event);
|
|
|
|
this._dx += dx;
|
|
this._dy += dy;
|
|
} else {
|
|
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END)
|
|
this._checkActivated();
|
|
|
|
this._dx = 0;
|
|
this._dy = 0;
|
|
}
|
|
|
|
return Clutter.EVENT_STOP;
|
|
}
|
|
});
|
|
Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype);
|
|
|
|
const WorkspaceSwitchAction = new Lang.Class({
|
|
Name: 'WorkspaceSwitchAction',
|
|
Extends: Clutter.SwipeAction,
|
|
|
|
_init : function() {
|
|
const MOTION_THRESHOLD = 50;
|
|
|
|
this.parent();
|
|
this.set_n_touch_points(4);
|
|
this.set_threshold_trigger_distance(MOTION_THRESHOLD, MOTION_THRESHOLD);
|
|
|
|
global.display.connect('grab-op-begin', Lang.bind(this, function() {
|
|
this.cancel();
|
|
}));
|
|
},
|
|
|
|
vfunc_gesture_prepare : function(actor) {
|
|
let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW;
|
|
|
|
if (!this.parent(actor))
|
|
return false;
|
|
|
|
return (allowedModes & Main.actionMode);
|
|
},
|
|
|
|
vfunc_swept : function(actor, direction) {
|
|
let dir;
|
|
|
|
if (direction & Clutter.SwipeDirection.UP)
|
|
dir = Meta.MotionDirection.DOWN;
|
|
else if (direction & Clutter.SwipeDirection.DOWN)
|
|
dir = Meta.MotionDirection.UP;
|
|
else if (direction & Clutter.SwipeDirection.LEFT)
|
|
dir = Meta.MotionDirection.RIGHT;
|
|
else if (direction & Clutter.SwipeDirection.RIGHT)
|
|
dir = Meta.MotionDirection.LEFT;
|
|
|
|
this.emit('activated', dir);
|
|
}
|
|
});
|
|
Signals.addSignalMethods(WorkspaceSwitchAction.prototype);
|
|
|
|
const AppSwitchAction = new Lang.Class({
|
|
Name: 'AppSwitchAction',
|
|
Extends: Clutter.GestureAction,
|
|
|
|
_init : function() {
|
|
this.parent();
|
|
this.set_n_touch_points(3);
|
|
|
|
global.display.connect('grab-op-begin', Lang.bind(this, function() {
|
|
this.cancel();
|
|
}));
|
|
},
|
|
|
|
vfunc_gesture_prepare : function(action, actor) {
|
|
if (Main.actionMode != Shell.ActionMode.NORMAL) {
|
|
this.cancel();
|
|
return false;
|
|
}
|
|
|
|
return this.get_n_current_points() <= 4;
|
|
},
|
|
|
|
vfunc_gesture_begin : function(action, actor) {
|
|
// in milliseconds
|
|
const LONG_PRESS_TIMEOUT = 250;
|
|
|
|
let nPoints = this.get_n_current_points();
|
|
let event = this.get_last_event (nPoints - 1);
|
|
|
|
if (nPoints == 3)
|
|
this._longPressStartTime = event.get_time();
|
|
else if (nPoints == 4) {
|
|
// Check whether the 4th finger press happens after a 3-finger long press,
|
|
// this only needs to be checked on the first 4th finger press
|
|
if (this._longPressStartTime != null &&
|
|
event.get_time() < this._longPressStartTime + LONG_PRESS_TIMEOUT)
|
|
this.cancel();
|
|
else {
|
|
this._longPressStartTime = null;
|
|
this.emit('activated');
|
|
}
|
|
}
|
|
|
|
return this.get_n_current_points() <= 4;
|
|
},
|
|
|
|
vfunc_gesture_progress : function(action, actor) {
|
|
const MOTION_THRESHOLD = 30;
|
|
|
|
if (this.get_n_current_points() == 3) {
|
|
for (let i = 0; i < this.get_n_current_points(); i++) {
|
|
[startX, startY] = this.get_press_coords(i);
|
|
[x, y] = this.get_motion_coords(i);
|
|
|
|
if (Math.abs(x - startX) > MOTION_THRESHOLD ||
|
|
Math.abs(y - startY) > MOTION_THRESHOLD)
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
});
|
|
Signals.addSignalMethods(AppSwitchAction.prototype);
|
|
|
|
const ResizePopup = new Lang.Class({
|
|
Name: 'ResizePopup',
|
|
|
|
_init: function() {
|
|
this._widget = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
|
this._label = new St.Label({ style_class: 'resize-popup',
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
x_expand: true, y_expand: true });
|
|
this._widget.add_child(this._label);
|
|
Main.uiGroup.add_actor(this._widget);
|
|
},
|
|
|
|
set: function(rect, displayW, displayH) {
|
|
/* Translators: This represents the size of a window. The first number is
|
|
* the width of the window and the second is the height. */
|
|
let text = _("%d x %d").format(displayW, displayH);
|
|
this._label.set_text(text);
|
|
|
|
this._widget.set_position(rect.x, rect.y);
|
|
this._widget.set_size(rect.width, rect.height);
|
|
},
|
|
|
|
destroy: function() {
|
|
this._widget.destroy();
|
|
this._widget = null;
|
|
},
|
|
});
|
|
|
|
const WindowManager = new Lang.Class({
|
|
Name: 'WindowManager',
|
|
|
|
_init : function() {
|
|
this._shellwm = global.window_manager;
|
|
|
|
this._minimizing = [];
|
|
this._unminimizing = [];
|
|
this._mapping = [];
|
|
this._resizing = [];
|
|
this._destroying = [];
|
|
this._movingWindow = null;
|
|
|
|
this._dimmedWindows = [];
|
|
|
|
this._skippedActors = [];
|
|
|
|
this._allowedKeybindings = {};
|
|
|
|
this._isWorkspacePrepended = false;
|
|
|
|
this._switchData = null;
|
|
this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone));
|
|
this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) {
|
|
this._minimizeWindowDone(shellwm, actor);
|
|
this._mapWindowDone(shellwm, actor);
|
|
this._destroyWindowDone(shellwm, actor);
|
|
this._sizeChangeWindowDone(shellwm, actor);
|
|
}));
|
|
|
|
this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
|
|
this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview));
|
|
this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview));
|
|
this._shellwm.connect('show-window-menu', Lang.bind(this, this._showWindowMenu));
|
|
this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
|
|
this._shellwm.connect('unminimize', Lang.bind(this, this._unminimizeWindow));
|
|
this._shellwm.connect('size-change', Lang.bind(this, this._sizeChangeWindow));
|
|
this._shellwm.connect('size-changed', Lang.bind(this, this._sizeChangedWindow));
|
|
this._shellwm.connect('map', Lang.bind(this, this._mapWindow));
|
|
this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
|
|
this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding));
|
|
this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange));
|
|
global.screen.connect('restacked', Lang.bind(this, this._syncStacking));
|
|
|
|
this._workspaceSwitcherPopup = null;
|
|
this._tilePreview = null;
|
|
|
|
this.allowKeybinding('switch-to-session-1', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-2', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-3', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-4', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-5', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-6', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-7', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-8', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-9', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-10', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-11', Shell.ActionMode.ALL);
|
|
this.allowKeybinding('switch-to-session-12', Shell.ActionMode.ALL);
|
|
|
|
this.setCustomKeybindingHandler('switch-to-workspace-left',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-right',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-up',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-down',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-last',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-left',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-right',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-up',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-down',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-1',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-2',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-3',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-4',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-5',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-6',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-7',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-8',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-9',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-10',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-11',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-to-workspace-12',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-1',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-2',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-3',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-4',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-5',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-6',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-7',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-8',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-9',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-10',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-11',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-12',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('move-to-workspace-last',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._showWorkspaceSwitcher));
|
|
this.setCustomKeybindingHandler('switch-applications',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-group',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-applications-backward',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-group-backward',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-windows',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-windows-backward',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('cycle-windows',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('cycle-windows-backward',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('cycle-group',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('cycle-group-backward',
|
|
Shell.ActionMode.NORMAL,
|
|
Lang.bind(this, this._startSwitcher));
|
|
this.setCustomKeybindingHandler('switch-panels',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW |
|
|
Shell.ActionMode.LOCK_SCREEN |
|
|
Shell.ActionMode.UNLOCK_SCREEN |
|
|
Shell.ActionMode.LOGIN_SCREEN,
|
|
Lang.bind(this, this._startA11ySwitcher));
|
|
this.setCustomKeybindingHandler('switch-panels-backward',
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW |
|
|
Shell.ActionMode.LOCK_SCREEN |
|
|
Shell.ActionMode.UNLOCK_SCREEN |
|
|
Shell.ActionMode.LOGIN_SCREEN,
|
|
Lang.bind(this, this._startA11ySwitcher));
|
|
|
|
this.addKeybinding('pause-resume-tweens',
|
|
new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
|
|
Meta.KeyBindingFlags.NONE,
|
|
Shell.ActionMode.ALL,
|
|
Lang.bind(this, this._toggleTweens));
|
|
|
|
this.addKeybinding('open-application-menu',
|
|
new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
|
|
Meta.KeyBindingFlags.NONE,
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.POPUP,
|
|
Lang.bind(this, this._toggleAppMenu));
|
|
|
|
this.addKeybinding('toggle-message-tray',
|
|
new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
|
|
Meta.KeyBindingFlags.NONE,
|
|
Shell.ActionMode.NORMAL |
|
|
Shell.ActionMode.OVERVIEW |
|
|
Shell.ActionMode.POPUP,
|
|
Lang.bind(this, this._toggleCalendar));
|
|
|
|
global.display.connect('show-resize-popup', Lang.bind(this, this._showResizePopup));
|
|
global.display.connect('show-pad-osd', Lang.bind(this, this._showPadOsd));
|
|
|
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
|
for (let i = 0; i < this._dimmedWindows.length; i++)
|
|
this._undimWindow(this._dimmedWindows[i]);
|
|
}));
|
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
|
for (let i = 0; i < this._dimmedWindows.length; i++)
|
|
this._dimWindow(this._dimmedWindows[i]);
|
|
}));
|
|
|
|
this._windowMenuManager = new WindowMenu.WindowMenuManager();
|
|
|
|
if (Main.sessionMode.hasWorkspaces)
|
|
this._workspaceTracker = new WorkspaceTracker(this);
|
|
|
|
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
|
false, -1, 1);
|
|
|
|
let gesture = new WorkspaceSwitchAction();
|
|
gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace));
|
|
global.stage.add_action(gesture);
|
|
|
|
// This is not a normal Clutter.GestureAction, doesn't need add_action()
|
|
gesture = new TouchpadWorkspaceSwitchAction(global.stage);
|
|
gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace));
|
|
|
|
gesture = new AppSwitchAction();
|
|
gesture.connect('activated', Lang.bind(this, this._switchApp));
|
|
global.stage.add_action(gesture);
|
|
},
|
|
|
|
_showPadOsd: function (display, device, settings, imagePath, editionMode, monitorIndex) {
|
|
this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
|
|
this._currentPadOsd.connect('closed', Lang.bind(this, function() { this._currentPadOsd = null }));
|
|
|
|
return this._currentPadOsd.actor;
|
|
},
|
|
|
|
_actionSwitchWorkspace: function(action, direction) {
|
|
let newWs = global.screen.get_active_workspace().get_neighbor(direction);
|
|
this.actionMoveWorkspace(newWs);
|
|
},
|
|
|
|
_lookupIndex: function (windows, metaWindow) {
|
|
for (let i = 0; i < windows.length; i++) {
|
|
if (windows[i].metaWindow == metaWindow) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
_switchApp : function () {
|
|
let windows = global.get_window_actors().filter(Lang.bind(this, function(actor) {
|
|
let win = actor.metaWindow;
|
|
return (!win.is_override_redirect() &&
|
|
win.located_on_workspace(global.screen.get_active_workspace()));
|
|
}));
|
|
|
|
if (windows.length == 0)
|
|
return;
|
|
|
|
let focusWindow = global.display.focus_window;
|
|
let nextWindow;
|
|
|
|
if (focusWindow == null)
|
|
nextWindow = windows[0].metaWindow;
|
|
else {
|
|
let index = this._lookupIndex (windows, focusWindow) + 1;
|
|
|
|
if (index >= windows.length)
|
|
index = 0;
|
|
|
|
nextWindow = windows[index].metaWindow;
|
|
}
|
|
|
|
Main.activateWindow(nextWindow);
|
|
},
|
|
|
|
insertWorkspace: function(pos) {
|
|
if (!Meta.prefs_get_dynamic_workspaces())
|
|
return;
|
|
|
|
global.screen.append_new_workspace(false, global.get_current_time());
|
|
|
|
let windows = global.get_window_actors().map(function(winActor) {
|
|
return winActor.meta_window;
|
|
});
|
|
|
|
// To create a new workspace, we slide all the windows on workspaces
|
|
// below us to the next workspace, leaving a blank workspace for us
|
|
// to recycle.
|
|
windows.forEach(function(window) {
|
|
// If the window is attached to an ancestor, we don't need/want
|
|
// to move it
|
|
if (window.get_transient_for() != null)
|
|
return;
|
|
// Same for OR windows
|
|
if (window.is_override_redirect())
|
|
return;
|
|
// Windows on workspaces below pos don't need moving
|
|
let index = window.get_workspace().index();
|
|
if (index < pos)
|
|
return;
|
|
window.change_workspace_by_index(index + 1, true);
|
|
});
|
|
|
|
// If the new workspace was inserted before the active workspace,
|
|
// activate the workspace to which its windows went
|
|
let activeIndex = global.screen.get_active_workspace_index();
|
|
if (activeIndex >= pos) {
|
|
let newWs = global.screen.get_workspace_by_index(activeIndex + 1);
|
|
this._blockAnimations = true;
|
|
newWs.activate(global.get_current_time());
|
|
this._blockAnimations = false;
|
|
}
|
|
},
|
|
|
|
|
|
keepWorkspaceAlive: function(workspace, duration) {
|
|
if (!this._workspaceTracker)
|
|
return;
|
|
|
|
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
|
|
},
|
|
|
|
skipNextEffect: function(actor) {
|
|
this._skippedActors.push(actor);
|
|
},
|
|
|
|
setCustomKeybindingHandler: function(name, modes, handler) {
|
|
if (Meta.keybindings_set_custom_handler(name, handler))
|
|
this.allowKeybinding(name, modes);
|
|
},
|
|
|
|
addKeybinding: function(name, settings, flags, modes, handler) {
|
|
let action = global.display.add_keybinding(name, settings, flags, handler);
|
|
if (action != Meta.KeyBindingAction.NONE)
|
|
this.allowKeybinding(name, modes);
|
|
return action;
|
|
},
|
|
|
|
removeKeybinding: function(name) {
|
|
if (global.display.remove_keybinding(name))
|
|
this.allowKeybinding(name, Shell.ActionMode.NONE);
|
|
},
|
|
|
|
allowKeybinding: function(name, modes) {
|
|
this._allowedKeybindings[name] = modes;
|
|
},
|
|
|
|
_shouldAnimate: function() {
|
|
return !(Main.overview.visible || this._blockAnimations);
|
|
},
|
|
|
|
_shouldAnimateActor: function(actor, types) {
|
|
if (this._removeEffect(this._skippedActors, actor))
|
|
return false;
|
|
|
|
if (!this._shouldAnimate())
|
|
return false;
|
|
|
|
let type = actor.meta_window.get_window_type();
|
|
return types.indexOf(type) >= 0;
|
|
},
|
|
|
|
_removeEffect : function(list, actor) {
|
|
let idx = list.indexOf(actor);
|
|
if (idx != -1) {
|
|
list.splice(idx, 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
_minimizeWindow : function(shellwm, actor) {
|
|
let types = [Meta.WindowType.NORMAL,
|
|
Meta.WindowType.MODAL_DIALOG,
|
|
Meta.WindowType.DIALOG];
|
|
if (!this._shouldAnimateActor(actor, types)) {
|
|
shellwm.completed_minimize(actor);
|
|
return;
|
|
}
|
|
|
|
actor.set_scale(1.0, 1.0);
|
|
|
|
this._minimizing.push(actor);
|
|
|
|
if (actor.meta_window.is_monitor_sized()) {
|
|
Tweener.addTween(actor,
|
|
{ opacity: 0,
|
|
time: MINIMIZE_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._minimizeWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._minimizeWindowOverwritten,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
} else {
|
|
let xDest, yDest, xScale, yScale;
|
|
let [success, geom] = actor.meta_window.get_icon_geometry();
|
|
if (success) {
|
|
xDest = geom.x;
|
|
yDest = geom.y;
|
|
xScale = geom.width / actor.width;
|
|
yScale = geom.height / actor.height;
|
|
} else {
|
|
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
|
xDest = monitor.x;
|
|
yDest = monitor.y;
|
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
|
xDest += monitor.width;
|
|
xScale = 0;
|
|
yScale = 0;
|
|
}
|
|
|
|
Tweener.addTween(actor,
|
|
{ scale_x: xScale,
|
|
scale_y: yScale,
|
|
x: xDest,
|
|
y: yDest,
|
|
time: MINIMIZE_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeInExpo',
|
|
onComplete: this._minimizeWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._minimizeWindowOverwritten,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
}
|
|
},
|
|
|
|
_minimizeWindowDone : function(shellwm, actor) {
|
|
if (this._removeEffect(this._minimizing, actor)) {
|
|
Tweener.removeTweens(actor);
|
|
actor.set_scale(1.0, 1.0);
|
|
actor.set_opacity(255);
|
|
actor.set_pivot_point(0, 0);
|
|
|
|
shellwm.completed_minimize(actor);
|
|
}
|
|
},
|
|
|
|
_minimizeWindowOverwritten : function(shellwm, actor) {
|
|
if (this._removeEffect(this._minimizing, actor)) {
|
|
shellwm.completed_minimize(actor);
|
|
}
|
|
},
|
|
|
|
_unminimizeWindow : function(shellwm, actor) {
|
|
let types = [Meta.WindowType.NORMAL,
|
|
Meta.WindowType.MODAL_DIALOG,
|
|
Meta.WindowType.DIALOG];
|
|
if (!this._shouldAnimateActor(actor, types)) {
|
|
shellwm.completed_unminimize(actor);
|
|
return;
|
|
}
|
|
|
|
this._unminimizing.push(actor);
|
|
|
|
if (actor.meta_window.is_monitor_sized()) {
|
|
actor.opacity = 0;
|
|
actor.set_scale(1.0, 1.0);
|
|
Tweener.addTween(actor,
|
|
{ opacity: 255,
|
|
time: MINIMIZE_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._unminimizeWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._unminimizeWindowOverwritten,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
} else {
|
|
let [success, geom] = actor.meta_window.get_icon_geometry();
|
|
if (success) {
|
|
actor.set_position(geom.x, geom.y);
|
|
actor.set_scale(geom.width / actor.width,
|
|
geom.height / actor.height);
|
|
} else {
|
|
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
|
actor.set_position(monitor.x, monitor.y);
|
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
|
actor.x += monitor.width;
|
|
actor.set_scale(0, 0);
|
|
}
|
|
|
|
let rect = actor.meta_window.get_frame_rect();
|
|
let [xDest, yDest] = [rect.x, rect.y];
|
|
|
|
actor.show();
|
|
Tweener.addTween(actor,
|
|
{ scale_x: 1.0,
|
|
scale_y: 1.0,
|
|
x: xDest,
|
|
y: yDest,
|
|
time: MINIMIZE_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeInExpo',
|
|
onComplete: this._unminimizeWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._unminimizeWindowOverwritten,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
}
|
|
},
|
|
|
|
_unminimizeWindowDone : function(shellwm, actor) {
|
|
if (this._removeEffect(this._unminimizing, actor)) {
|
|
Tweener.removeTweens(actor);
|
|
actor.set_scale(1.0, 1.0);
|
|
actor.set_opacity(255);
|
|
actor.set_pivot_point(0, 0);
|
|
|
|
shellwm.completed_unminimize(actor);
|
|
}
|
|
},
|
|
|
|
_unminimizeWindowOverwritten : function(shellwm, actor) {
|
|
if (this._removeEffect(this._unminimizing, actor)) {
|
|
shellwm.completed_unminimize(actor);
|
|
}
|
|
},
|
|
|
|
_sizeChangeWindow : function(shellwm, actor, whichChange, oldFrameRect, oldBufferRect) {
|
|
let types = [Meta.WindowType.NORMAL];
|
|
if (!this._shouldAnimateActor(actor, types)) {
|
|
shellwm.completed_size_change(actor);
|
|
return;
|
|
}
|
|
|
|
if ((whichChange == Meta.SizeChange.FULLSCREEN ||
|
|
whichChange == Meta.SizeChange.UNFULLSCREEN) &&
|
|
oldFrameRect.width > 0 && oldFrameRect.height > 0)
|
|
this._fullscreenAnimation(shellwm, actor, oldFrameRect, whichChange);
|
|
else
|
|
shellwm.completed_size_change(actor);
|
|
},
|
|
|
|
_fullscreenAnimation: function(shellwm, actor, oldFrameRect, change) {
|
|
// Position a clone of the window on top of the old position,
|
|
// while actor updates are frozen.
|
|
let actorContent = Shell.util_get_content_for_window_actor(actor, oldFrameRect);
|
|
let actorClone = new St.Widget({ content: actorContent });
|
|
actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
|
actorClone.set_position(oldFrameRect.x, oldFrameRect.y);
|
|
actorClone.set_size(oldFrameRect.width, oldFrameRect.height);
|
|
Main.uiGroup.add_actor(actorClone);
|
|
|
|
let rect = change == Meta.SizeChange.FULLSCREEN ? oldFrameRect : null;
|
|
|
|
if (this._clearFullscreenInfo(actor))
|
|
this._shellwm.completed_size_change(actor);
|
|
|
|
actor.__fullscreenInfo = { clone: actorClone,
|
|
oldRect: rect };
|
|
},
|
|
|
|
_sizeChangedWindow: function(shellwm, actor) {
|
|
if (!actor.__fullscreenInfo)
|
|
return;
|
|
|
|
let actorClone = actor.__fullscreenInfo.clone;
|
|
let targetRect = actor.meta_window.get_frame_rect();
|
|
|
|
let scaleX = targetRect.width / actorClone.width;
|
|
let scaleY = targetRect.height / actorClone.height;
|
|
|
|
this._resizing.push(actor);
|
|
|
|
// Now scale and fade out the clone
|
|
Tweener.addTween(actorClone,
|
|
{ x: targetRect.x,
|
|
y: targetRect.y,
|
|
scale_x: scaleX,
|
|
scale_y: scaleY,
|
|
opacity: 0,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
|
|
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
|
let oldRect = actor.__fullscreenInfo.oldRect;
|
|
if (oldRect) {
|
|
actor.translation_x = oldRect.x - monitor.x;
|
|
actor.translation_y = oldRect.y - monitor.y;
|
|
} else {
|
|
actor.translation_x = -(targetRect.x - monitor.x);
|
|
actor.translation_y = -(targetRect.y - monitor.y);
|
|
}
|
|
|
|
// Now set scale the actor to size it as the clone.
|
|
actor.scale_x = 1 / scaleX;
|
|
actor.scale_y = 1 / scaleY;
|
|
|
|
// Scale it to its actual new size
|
|
Tweener.addTween(actor,
|
|
{ scale_x: 1.0,
|
|
scale_y: 1.0,
|
|
translation_x: 0,
|
|
translation_y: 0,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._sizeChangeWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._sizeChangeWindowOverwritten,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
|
|
// Now unfreeze actor updates, to get it to the new size.
|
|
// It's important that we don't wait until the animation is completed to
|
|
// do this, otherwise our scale will be applied to the old texture size.
|
|
shellwm.completed_size_change(actor);
|
|
},
|
|
|
|
_clearFullscreenInfo: function(actor) {
|
|
if (actor.__fullscreenInfo) {
|
|
actor.__fullscreenInfo.clone.destroy();
|
|
delete actor.__fullscreenInfo;
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
_sizeChangeWindowDone: function(shellwm, actor) {
|
|
if (this._removeEffect(this._resizing, actor)) {
|
|
Tweener.removeTweens(actor);
|
|
actor.scale_x = 1.0;
|
|
actor.scale_y = 1.0;
|
|
actor.translation_x = 0;
|
|
actor.translation_y = 0;
|
|
this._clearFullscreenInfo(actor);
|
|
}
|
|
},
|
|
|
|
_sizeChangeWindowOverwritten: function(shellwm, actor) {
|
|
if (this._removeEffect(this._resizing, actor))
|
|
this._clearFullscreenInfo(actor);
|
|
},
|
|
|
|
_hasAttachedDialogs: function(window, ignoreWindow) {
|
|
var count = 0;
|
|
window.foreach_transient(function(win) {
|
|
if (win != ignoreWindow &&
|
|
win.is_attached_dialog() &&
|
|
win.get_transient_for() == window) {
|
|
count++;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return count != 0;
|
|
},
|
|
|
|
_checkDimming: function(window, ignoreWindow) {
|
|
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
|
|
|
|
if (shouldDim && !window._dimmed) {
|
|
window._dimmed = true;
|
|
this._dimmedWindows.push(window);
|
|
this._dimWindow(window);
|
|
} else if (!shouldDim && window._dimmed) {
|
|
window._dimmed = false;
|
|
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
|
return win != window;
|
|
});
|
|
this._undimWindow(window);
|
|
}
|
|
},
|
|
|
|
_dimWindow: function(window) {
|
|
let actor = window.get_compositor_private();
|
|
if (!actor)
|
|
return;
|
|
let dimmer = getWindowDimmer(actor);
|
|
if (!dimmer)
|
|
return;
|
|
if (this._shouldAnimate())
|
|
Tweener.addTween(dimmer,
|
|
{ dimFactor: 1.0,
|
|
time: DIM_TIME,
|
|
transition: 'linear'
|
|
});
|
|
else
|
|
dimmer.dimFactor = 1.0;
|
|
},
|
|
|
|
_undimWindow: function(window) {
|
|
let actor = window.get_compositor_private();
|
|
if (!actor)
|
|
return;
|
|
let dimmer = getWindowDimmer(actor);
|
|
if (!dimmer)
|
|
return;
|
|
if (this._shouldAnimate())
|
|
Tweener.addTween(dimmer,
|
|
{ dimFactor: 0.0,
|
|
time: UNDIM_TIME,
|
|
transition: 'linear' });
|
|
else
|
|
dimmer.dimFactor = 0.0;
|
|
},
|
|
|
|
_mapWindow : function(shellwm, actor) {
|
|
actor._windowType = actor.meta_window.get_window_type();
|
|
actor._notifyWindowTypeSignalId = actor.meta_window.connect('notify::window-type', Lang.bind(this, function () {
|
|
let type = actor.meta_window.get_window_type();
|
|
if (type == actor._windowType)
|
|
return;
|
|
if (type == Meta.WindowType.MODAL_DIALOG ||
|
|
actor._windowType == Meta.WindowType.MODAL_DIALOG) {
|
|
let parent = actor.get_meta_window().get_transient_for();
|
|
if (parent)
|
|
this._checkDimming(parent);
|
|
}
|
|
|
|
actor._windowType = type;
|
|
}));
|
|
actor.meta_window.connect('unmanaged', Lang.bind(this, function(window) {
|
|
let parent = window.get_transient_for();
|
|
if (parent)
|
|
this._checkDimming(parent);
|
|
}));
|
|
|
|
if (actor.meta_window.is_attached_dialog())
|
|
this._checkDimming(actor.get_meta_window().get_transient_for());
|
|
|
|
let types = [Meta.WindowType.NORMAL,
|
|
Meta.WindowType.DIALOG,
|
|
Meta.WindowType.MODAL_DIALOG];
|
|
if (!this._shouldAnimateActor(actor, types)) {
|
|
shellwm.completed_map(actor);
|
|
return;
|
|
}
|
|
|
|
switch (actor._windowType) {
|
|
case Meta.WindowType.NORMAL:
|
|
actor.set_pivot_point(0.5, 1.0);
|
|
actor.scale_x = 0.01;
|
|
actor.scale_y = 0.05;
|
|
actor.opacity = 0;
|
|
actor.show();
|
|
this._mapping.push(actor);
|
|
|
|
Tweener.addTween(actor,
|
|
{ opacity: 255,
|
|
scale_x: 1,
|
|
scale_y: 1,
|
|
time: SHOW_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutExpo',
|
|
onComplete: this._mapWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._mapWindowOverwrite,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
break;
|
|
case Meta.WindowType.MODAL_DIALOG:
|
|
case Meta.WindowType.DIALOG:
|
|
actor.set_pivot_point(0.5, 0.5);
|
|
actor.scale_y = 0;
|
|
actor.opacity = 0;
|
|
actor.show();
|
|
this._mapping.push(actor);
|
|
|
|
Tweener.addTween(actor,
|
|
{ opacity: 255,
|
|
scale_x: 1,
|
|
scale_y: 1,
|
|
time: DIALOG_SHOW_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._mapWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._mapWindowOverwrite,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
break;
|
|
default:
|
|
shellwm.completed_map(actor);
|
|
return;
|
|
}
|
|
},
|
|
|
|
_mapWindowDone : function(shellwm, actor) {
|
|
if (this._removeEffect(this._mapping, actor)) {
|
|
Tweener.removeTweens(actor);
|
|
actor.opacity = 255;
|
|
actor.set_pivot_point(0, 0);
|
|
actor.scale_y = 1;
|
|
actor.scale_x = 1;
|
|
actor.translation_y = 0;
|
|
actor.translation_x = 0;
|
|
shellwm.completed_map(actor);
|
|
}
|
|
},
|
|
|
|
_mapWindowOverwrite : function(shellwm, actor) {
|
|
if (this._removeEffect(this._mapping, actor)) {
|
|
shellwm.completed_map(actor);
|
|
}
|
|
},
|
|
|
|
_destroyWindow : function(shellwm, actor) {
|
|
let window = actor.meta_window;
|
|
if (actor._notifyWindowTypeSignalId) {
|
|
window.disconnect(actor._notifyWindowTypeSignalId);
|
|
actor._notifyWindowTypeSignalId = 0;
|
|
}
|
|
if (window._dimmed) {
|
|
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
|
return win != window;
|
|
});
|
|
}
|
|
|
|
if (window.is_attached_dialog())
|
|
this._checkDimming(window.get_transient_for(), window);
|
|
|
|
let types = [Meta.WindowType.NORMAL,
|
|
Meta.WindowType.DIALOG,
|
|
Meta.WindowType.MODAL_DIALOG];
|
|
if (!this._shouldAnimateActor(actor, types)) {
|
|
shellwm.completed_destroy(actor);
|
|
return;
|
|
}
|
|
|
|
switch (actor._windowType) {
|
|
case Meta.WindowType.NORMAL:
|
|
actor.set_pivot_point(0.5, 0.5);
|
|
this._destroying.push(actor);
|
|
|
|
Tweener.addTween(actor,
|
|
{ opacity: 0,
|
|
scale_x: 0.8,
|
|
scale_y: 0.8,
|
|
time: DESTROY_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._destroyWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._destroyWindowDone,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
break;
|
|
case Meta.WindowType.MODAL_DIALOG:
|
|
case Meta.WindowType.DIALOG:
|
|
actor.set_pivot_point(0.5, 0.5);
|
|
this._destroying.push(actor);
|
|
|
|
if (window.is_attached_dialog()) {
|
|
let parent = window.get_transient_for();
|
|
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
|
|
Tweener.removeTweens(actor);
|
|
this._destroyWindowDone(shellwm, actor);
|
|
}));
|
|
}
|
|
|
|
Tweener.addTween(actor,
|
|
{ scale_y: 0,
|
|
time: DIALOG_DESTROY_WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._destroyWindowDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm, actor],
|
|
onOverwrite: this._destroyWindowDone,
|
|
onOverwriteScope: this,
|
|
onOverwriteParams: [shellwm, actor]
|
|
});
|
|
break;
|
|
default:
|
|
shellwm.completed_destroy(actor);
|
|
return;
|
|
}
|
|
},
|
|
|
|
_destroyWindowDone : function(shellwm, actor) {
|
|
if (this._removeEffect(this._destroying, actor)) {
|
|
let parent = actor.get_meta_window().get_transient_for();
|
|
if (parent && actor._parentDestroyId) {
|
|
parent.disconnect(actor._parentDestroyId);
|
|
actor._parentDestroyId = 0;
|
|
}
|
|
shellwm.completed_destroy(actor);
|
|
}
|
|
},
|
|
|
|
_filterKeybinding: function(shellwm, binding) {
|
|
if (Main.actionMode == Shell.ActionMode.NONE)
|
|
return true;
|
|
|
|
// There's little sense in implementing a keybinding in mutter and
|
|
// not having it work in NORMAL mode; handle this case generically
|
|
// so we don't have to explicitly allow all builtin keybindings in
|
|
// NORMAL mode.
|
|
if (Main.actionMode == Shell.ActionMode.NORMAL &&
|
|
binding.is_builtin())
|
|
return false;
|
|
|
|
return !(this._allowedKeybindings[binding.get_name()] & Main.actionMode);
|
|
},
|
|
|
|
_syncStacking: function() {
|
|
if (this._switchData == null)
|
|
return;
|
|
|
|
// Update stacking of windows in inGroup (aka the workspace we are
|
|
// switching to). Windows in outGroup are about to be hidden anyway,
|
|
// so we just ignore them here.
|
|
let windows = global.get_window_actors();
|
|
let sibling = null;
|
|
for (let i = 0; i < windows.length; i++) {
|
|
if (windows[i].get_parent() != this._switchData.inGroup)
|
|
continue;
|
|
|
|
this._switchData.inGroup.set_child_above_sibling(windows[i], sibling);
|
|
sibling = windows[i];
|
|
}
|
|
},
|
|
|
|
_switchWorkspace : function(shellwm, from, to, direction) {
|
|
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
|
|
shellwm.completed_switch_workspace();
|
|
return;
|
|
}
|
|
|
|
let windows = global.get_window_actors();
|
|
|
|
/* @direction is the direction that the "camera" moves, so the
|
|
* screen contents have to move one screen's worth in the
|
|
* opposite direction.
|
|
*/
|
|
let xDest = 0, yDest = 0;
|
|
|
|
if (direction == Meta.MotionDirection.UP ||
|
|
direction == Meta.MotionDirection.UP_LEFT ||
|
|
direction == Meta.MotionDirection.UP_RIGHT)
|
|
yDest = global.screen_height - Main.panel.actor.height;
|
|
else if (direction == Meta.MotionDirection.DOWN ||
|
|
direction == Meta.MotionDirection.DOWN_LEFT ||
|
|
direction == Meta.MotionDirection.DOWN_RIGHT)
|
|
yDest = -global.screen_height + Main.panel.actor.height;
|
|
|
|
if (direction == Meta.MotionDirection.LEFT ||
|
|
direction == Meta.MotionDirection.UP_LEFT ||
|
|
direction == Meta.MotionDirection.DOWN_LEFT)
|
|
xDest = global.screen_width;
|
|
else if (direction == Meta.MotionDirection.RIGHT ||
|
|
direction == Meta.MotionDirection.UP_RIGHT ||
|
|
direction == Meta.MotionDirection.DOWN_RIGHT)
|
|
xDest = -global.screen_width;
|
|
|
|
let switchData = {};
|
|
this._switchData = switchData;
|
|
switchData.inGroup = new Clutter.Actor();
|
|
switchData.outGroup = new Clutter.Actor();
|
|
switchData.movingWindowBin = new Clutter.Actor();
|
|
switchData.windows = [];
|
|
|
|
let wgroup = global.window_group;
|
|
wgroup.add_actor(switchData.inGroup);
|
|
wgroup.add_actor(switchData.outGroup);
|
|
wgroup.add_actor(switchData.movingWindowBin);
|
|
|
|
for (let i = 0; i < windows.length; i++) {
|
|
let actor = windows[i];
|
|
let window = actor.get_meta_window();
|
|
|
|
if (!window.showing_on_its_workspace())
|
|
continue;
|
|
|
|
if (window.is_on_all_workspaces())
|
|
continue;
|
|
|
|
let record = { window: actor,
|
|
parent: actor.get_parent() };
|
|
|
|
if (this._movingWindow && window == this._movingWindow) {
|
|
switchData.movingWindow = record;
|
|
switchData.windows.push(switchData.movingWindow);
|
|
actor.reparent(switchData.movingWindowBin);
|
|
} else if (window.get_workspace().index() == from) {
|
|
switchData.windows.push(record);
|
|
actor.reparent(switchData.outGroup);
|
|
} else if (window.get_workspace().index() == to) {
|
|
switchData.windows.push(record);
|
|
actor.reparent(switchData.inGroup);
|
|
actor.show();
|
|
}
|
|
}
|
|
|
|
switchData.inGroup.set_position(-xDest, -yDest);
|
|
switchData.inGroup.raise_top();
|
|
|
|
switchData.movingWindowBin.raise_top();
|
|
|
|
Tweener.addTween(switchData.outGroup,
|
|
{ x: xDest,
|
|
y: yDest,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: this._switchWorkspaceDone,
|
|
onCompleteScope: this,
|
|
onCompleteParams: [shellwm]
|
|
});
|
|
Tweener.addTween(switchData.inGroup,
|
|
{ x: 0,
|
|
y: 0,
|
|
time: WINDOW_ANIMATION_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
},
|
|
|
|
_switchWorkspaceDone : function(shellwm) {
|
|
let switchData = this._switchData;
|
|
if (!switchData)
|
|
return;
|
|
this._switchData = null;
|
|
|
|
for (let i = 0; i < switchData.windows.length; i++) {
|
|
let w = switchData.windows[i];
|
|
if (w.window.is_destroyed()) // Window gone
|
|
continue;
|
|
if (w.window.get_parent() == switchData.outGroup) {
|
|
w.window.reparent(w.parent);
|
|
w.window.hide();
|
|
} else
|
|
w.window.reparent(w.parent);
|
|
}
|
|
Tweener.removeTweens(switchData.inGroup);
|
|
Tweener.removeTweens(switchData.outGroup);
|
|
switchData.inGroup.destroy();
|
|
switchData.outGroup.destroy();
|
|
switchData.movingWindowBin.destroy();
|
|
|
|
if (this._movingWindow)
|
|
this._movingWindow = null;
|
|
|
|
shellwm.completed_switch_workspace();
|
|
},
|
|
|
|
_showTilePreview: function(shellwm, window, tileRect, monitorIndex) {
|
|
if (!this._tilePreview)
|
|
this._tilePreview = new TilePreview();
|
|
this._tilePreview.show(window, tileRect, monitorIndex);
|
|
},
|
|
|
|
_hideTilePreview: function(shellwm) {
|
|
if (!this._tilePreview)
|
|
return;
|
|
this._tilePreview.hide();
|
|
},
|
|
|
|
_showWindowMenu: function(shellwm, window, menu, rect) {
|
|
this._windowMenuManager.showWindowMenuForWindow(window, menu, rect);
|
|
},
|
|
|
|
_startSwitcher: function(display, screen, window, binding) {
|
|
let constructor = null;
|
|
switch (binding.get_name()) {
|
|
case 'switch-applications':
|
|
case 'switch-applications-backward':
|
|
case 'switch-group':
|
|
case 'switch-group-backward':
|
|
constructor = AltTab.AppSwitcherPopup;
|
|
break;
|
|
case 'switch-windows':
|
|
case 'switch-windows-backward':
|
|
constructor = AltTab.WindowSwitcherPopup;
|
|
break;
|
|
case 'cycle-windows':
|
|
case 'cycle-windows-backward':
|
|
constructor = AltTab.WindowCyclerPopup;
|
|
break;
|
|
case 'cycle-group':
|
|
case 'cycle-group-backward':
|
|
constructor = AltTab.GroupCyclerPopup;
|
|
break;
|
|
}
|
|
|
|
if (!constructor)
|
|
return;
|
|
|
|
/* prevent a corner case where both popups show up at once */
|
|
if (this._workspaceSwitcherPopup != null)
|
|
this._workspaceSwitcherPopup.destroy();
|
|
|
|
let tabPopup = new constructor();
|
|
|
|
if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
|
|
tabPopup.destroy();
|
|
},
|
|
|
|
_startA11ySwitcher : function(display, screen, window, binding) {
|
|
Main.ctrlAltTabManager.popup(binding.is_reversed(), binding.get_name(), binding.get_mask());
|
|
},
|
|
|
|
_toggleAppMenu : function(display, screen, window, event, binding) {
|
|
Main.panel.toggleAppMenu();
|
|
},
|
|
|
|
_toggleCalendar: function(display, screen, window, event, binding) {
|
|
Main.panel.toggleCalendar();
|
|
},
|
|
|
|
_toggleTweens: function() {
|
|
this._tweensPaused = !this._tweensPaused;
|
|
const OrigTweener = imports.tweener.tweener;
|
|
if (this._tweensPaused)
|
|
OrigTweener.pauseAllTweens();
|
|
else
|
|
OrigTweener.resumeAllTweens();
|
|
},
|
|
|
|
_showWorkspaceSwitcher : function(display, screen, window, binding) {
|
|
if (!Main.sessionMode.hasWorkspaces)
|
|
return;
|
|
|
|
if (screen.n_workspaces == 1)
|
|
return;
|
|
|
|
let [action,,,target] = binding.get_name().split('-');
|
|
let newWs;
|
|
let direction;
|
|
|
|
if (action == 'move') {
|
|
// "Moving" a window to another workspace doesn't make sense when
|
|
// it cannot be unstuck, and is potentially confusing if a new
|
|
// workspaces is added at the start/end
|
|
if (window.is_always_on_all_workspaces() ||
|
|
(Meta.prefs_get_workspaces_only_on_primary() &&
|
|
window.get_monitor() != Main.layoutManager.primaryIndex))
|
|
return;
|
|
}
|
|
|
|
if (target == 'last') {
|
|
direction = Meta.MotionDirection.DOWN;
|
|
newWs = screen.get_workspace_by_index(screen.n_workspaces - 1);
|
|
} else if (isNaN(target)) {
|
|
// Prepend a new workspace dynamically
|
|
if (screen.get_active_workspace_index() == 0 &&
|
|
action == 'move' && target == 'up' && this._isWorkspacePrepended == false) {
|
|
this.insertWorkspace(0);
|
|
this._isWorkspacePrepended = true;
|
|
}
|
|
|
|
direction = Meta.MotionDirection[target.toUpperCase()];
|
|
newWs = screen.get_active_workspace().get_neighbor(direction);
|
|
} else if (target > 0) {
|
|
target--;
|
|
newWs = screen.get_workspace_by_index(target);
|
|
|
|
if (screen.get_active_workspace().index() > target)
|
|
direction = Meta.MotionDirection.UP;
|
|
else
|
|
direction = Meta.MotionDirection.DOWN;
|
|
}
|
|
|
|
if (direction != Meta.MotionDirection.UP &&
|
|
direction != Meta.MotionDirection.DOWN)
|
|
return;
|
|
|
|
if (action == 'switch')
|
|
this.actionMoveWorkspace(newWs);
|
|
else
|
|
this.actionMoveWindow(window, newWs);
|
|
|
|
if (!Main.overview.visible) {
|
|
if (this._workspaceSwitcherPopup == null) {
|
|
this._workspaceTracker.blockUpdates();
|
|
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
|
|
this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() {
|
|
this._workspaceTracker.unblockUpdates();
|
|
this._workspaceSwitcherPopup = null;
|
|
this._isWorkspacePrepended = false;
|
|
}));
|
|
}
|
|
this._workspaceSwitcherPopup.display(direction, newWs.index());
|
|
}
|
|
},
|
|
|
|
actionMoveWorkspace: function(workspace) {
|
|
if (!Main.sessionMode.hasWorkspaces)
|
|
return;
|
|
|
|
let activeWorkspace = global.screen.get_active_workspace();
|
|
|
|
if (activeWorkspace != workspace)
|
|
workspace.activate(global.get_current_time());
|
|
},
|
|
|
|
actionMoveWindow: function(window, workspace) {
|
|
if (!Main.sessionMode.hasWorkspaces)
|
|
return;
|
|
|
|
let activeWorkspace = global.screen.get_active_workspace();
|
|
|
|
if (activeWorkspace != workspace) {
|
|
// This won't have any effect for "always sticky" windows
|
|
// (like desktop windows or docks)
|
|
|
|
this._movingWindow = window;
|
|
window.change_workspace(workspace);
|
|
|
|
global.display.clear_mouse_mode();
|
|
workspace.activate_with_focus (window, global.get_current_time());
|
|
}
|
|
},
|
|
|
|
_confirmDisplayChange: function() {
|
|
let dialog = new DisplayChangeDialog(this._shellwm);
|
|
dialog.open();
|
|
},
|
|
|
|
_showResizePopup: function(display, show, rect, displayW, displayH) {
|
|
if (show) {
|
|
if (!this._resizePopup)
|
|
this._resizePopup = new ResizePopup();
|
|
|
|
this._resizePopup.set(rect, displayW, displayH);
|
|
} else {
|
|
if (this._resizePopup) {
|
|
this._resizePopup.destroy();
|
|
this._resizePopup = null;
|
|
}
|
|
}
|
|
},
|
|
});
|