2010-01-22 02:33:48 +00:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
|
|
const Lang = imports.lang;
|
|
|
|
const Mainloop = imports.mainloop;
|
|
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const St = imports.gi.St;
|
|
|
|
const Signals = imports.signals;
|
2010-05-13 18:29:00 +00:00
|
|
|
const Gettext = imports.gettext.domain('gnome-shell');
|
|
|
|
const _ = Gettext.gettext;
|
2010-01-22 02:33:48 +00:00
|
|
|
|
|
|
|
const DND = imports.ui.dnd;
|
|
|
|
const Main = imports.ui.main;
|
|
|
|
const Overview = imports.ui.overview;
|
|
|
|
const Tweener = imports.ui.tweener;
|
|
|
|
const Workspace = imports.ui.workspace;
|
|
|
|
|
|
|
|
const WORKSPACE_SWITCH_TIME = 0.25;
|
|
|
|
// Note that mutter has a compile-time limit of 36
|
|
|
|
const MAX_WORKSPACES = 16;
|
|
|
|
|
2010-03-16 15:51:24 +00:00
|
|
|
const WORKSPACE_DRAGGING_SCALE = 0.85;
|
2010-10-04 18:04:23 +00:00
|
|
|
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
const CONTROLS_POP_IN_FRACTION = 0.8;
|
|
|
|
const CONTROLS_POP_IN_TIME = 0.1;
|
2010-11-12 09:28:28 +00:00
|
|
|
|
2010-11-12 21:52:44 +00:00
|
|
|
const INDICATOR_HOVER_SCALE = 1.1;
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
|
|
|
|
function WorkspacesView(width, height, x, y, workspaces) {
|
2010-02-14 23:32:57 +00:00
|
|
|
this._init(width, height, x, y, workspaces);
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
WorkspacesView.prototype = {
|
2010-02-14 23:32:57 +00:00
|
|
|
_init: function(width, height, x, y, workspaces) {
|
2010-11-12 09:28:28 +00:00
|
|
|
this.actor = new St.Group({ style_class: 'workspaces single' });
|
|
|
|
this.actor.set_clip(x, y, width, height);
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2010-02-18 15:43:58 +00:00
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
|
2010-02-10 06:00:02 +00:00
|
|
|
this.actor.connect('style-changed', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
let node = this.actor.get_theme_node();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 21:38:36 +00:00
|
|
|
this._spacing = node.get_length('spacing');
|
2010-11-12 09:28:28 +00:00
|
|
|
this._computeWorkspacePositions();
|
2010-02-10 06:00:02 +00:00
|
|
|
}));
|
2010-01-22 02:33:48 +00:00
|
|
|
|
|
|
|
this._width = width;
|
|
|
|
this._height = height;
|
|
|
|
this._x = x;
|
|
|
|
this._y = y;
|
2010-02-10 06:00:02 +00:00
|
|
|
this._spacing = 0;
|
2010-11-12 09:28:28 +00:00
|
|
|
this._activeWorkspaceX = 0; // x offset of active ws while dragging
|
|
|
|
this._activeWorkspaceY = 0; // y offset of active ws while dragging
|
|
|
|
this._lostWorkspaces = [];
|
|
|
|
this._animating = false; // tweening
|
|
|
|
this._scrolling = false; // dragging desktop
|
|
|
|
this._animatingScroll = false; // programatically updating the adjustment
|
|
|
|
this._inDrag = false; // dragging a window
|
|
|
|
this._lastMotionTime = -1; // used to track "stopping" while dragging workspaces
|
2010-01-22 02:33:48 +00:00
|
|
|
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
2010-02-14 23:32:57 +00:00
|
|
|
this._workspaces = workspaces;
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2010-02-14 23:32:57 +00:00
|
|
|
// Add workspace actors
|
2010-11-15 20:58:27 +00:00
|
|
|
for (let w = 0; w < global.screen.n_workspaces; w++)
|
2010-03-22 00:39:49 +00:00
|
|
|
this._workspaces[w].actor.reparent(this.actor);
|
2010-01-22 02:33:48 +00:00
|
|
|
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
|
|
|
|
|
|
|
// Position/scale the desktop windows and their children after the
|
|
|
|
// workspaces have been created. This cannot be done first because
|
|
|
|
// window movement depends on the Workspaces object being accessible
|
|
|
|
// as an Overview member.
|
|
|
|
this._overviewShowingId =
|
|
|
|
Main.overview.connect('showing',
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
this._onRestacked();
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++)
|
2010-02-14 23:32:57 +00:00
|
|
|
this._workspaces[w].zoomToOverview();
|
2010-01-22 02:33:48 +00:00
|
|
|
}));
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
|
|
|
lower: 0,
|
|
|
|
page_increment: 1,
|
|
|
|
page_size: 1,
|
|
|
|
step_increment: 0,
|
|
|
|
upper: this._workspaces.length });
|
|
|
|
this._scrollAdjustment.connect('notify::value',
|
|
|
|
Lang.bind(this, this._onScroll));
|
|
|
|
|
|
|
|
this._dragIndex = -1;
|
|
|
|
|
|
|
|
this._buttonPressId = 0;
|
|
|
|
this._capturedEventId = 0;
|
|
|
|
this._timeoutId = 0;
|
|
|
|
|
|
|
|
this._windowSelectionAppId = null;
|
|
|
|
this._highlightWindow = null;
|
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
this._switchWorkspaceNotifyId =
|
|
|
|
global.window_manager.connect('switch-workspace',
|
|
|
|
Lang.bind(this, this._activeWorkspaceChanged));
|
|
|
|
this._restackedNotifyId =
|
|
|
|
global.screen.connect('restacked',
|
|
|
|
Lang.bind(this, this._onRestacked));
|
2010-11-12 09:28:28 +00:00
|
|
|
|
|
|
|
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
|
|
|
Lang.bind(this, this._dragBegin));
|
|
|
|
this._itemDragEndId = Main.overview.connect('item-drag-end',
|
|
|
|
Lang.bind(this, this._dragEnd));
|
2010-11-15 20:58:27 +00:00
|
|
|
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
|
|
|
Lang.bind(this, this._dragBegin));
|
|
|
|
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
|
|
|
Lang.bind(this, this._dragEnd));
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
|
|
if (this._workspaces[i].containsMetaWindow(metaWindow))
|
|
|
|
return this._workspaces[i];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
setHighlightWindow: function (metaWindow) {
|
|
|
|
// Looping over all workspaces is easier than keeping track of the last
|
|
|
|
// highlighted window while trying to handle the window or workspace possibly
|
|
|
|
// going away.
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
|
|
this._workspaces[i].setHighlightWindow(null);
|
|
|
|
}
|
|
|
|
if (metaWindow != null) {
|
|
|
|
let workspace = this._lookupWorkspaceForMetaWindow(metaWindow);
|
|
|
|
workspace.setHighlightWindow(metaWindow);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-07-15 14:21:32 +00:00
|
|
|
getActiveWorkspace: function() {
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
return this._workspaces[active];
|
|
|
|
},
|
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
_clearApplicationWindowSelection: function(reposition) {
|
|
|
|
if (this._windowSelectionAppId == null)
|
|
|
|
return;
|
|
|
|
this._windowSelectionAppId = null;
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
|
|
this._workspaces[i].setLightboxMode(false);
|
|
|
|
this._workspaces[i].setShowOnlyWindows(null, reposition);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* setApplicationWindowSelection:
|
|
|
|
* @appid: Application identifier string
|
|
|
|
*
|
|
|
|
* Enter a mode which shows only the windows owned by the
|
|
|
|
* given application, and allow highlighting of a specific
|
|
|
|
* window with setHighlightWindow().
|
|
|
|
*/
|
|
|
|
setApplicationWindowSelection: function (appId) {
|
|
|
|
if (appId == null) {
|
|
|
|
this._clearApplicationWindowSelection(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (appId == this._windowSelectionAppId)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._windowSelectionAppId = appId;
|
|
|
|
|
|
|
|
let appSys = Shell.AppSystem.get_default();
|
|
|
|
|
|
|
|
let showOnlyWindows = {};
|
|
|
|
let app = appSys.get_app(appId);
|
|
|
|
let windows = app.get_windows();
|
|
|
|
for (let i = 0; i < windows.length; i++) {
|
|
|
|
showOnlyWindows[windows[i]] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
|
|
this._workspaces[i].setLightboxMode(true);
|
|
|
|
this._workspaces[i].setShowOnlyWindows(showOnlyWindows, true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
hide: function() {
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
|
|
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
|
|
|
|
2010-02-17 19:05:06 +00:00
|
|
|
if (this._windowSelectionAppId != null)
|
|
|
|
this._clearApplicationWindowSelection(false);
|
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
activeWorkspace.actor.raise_top();
|
|
|
|
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++)
|
|
|
|
this._workspaces[w].zoomFromOverview();
|
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function() {
|
2010-02-18 15:43:58 +00:00
|
|
|
this.actor.destroy();
|
|
|
|
},
|
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
getScale: function() {
|
|
|
|
return this._workspaces[0].scale;
|
|
|
|
},
|
|
|
|
|
|
|
|
_onRestacked: function() {
|
2010-10-19 18:55:43 +00:00
|
|
|
let stack = global.get_window_actors();
|
2010-01-22 02:33:48 +00:00
|
|
|
let stackIndices = {};
|
|
|
|
|
|
|
|
for (let i = 0; i < stack.length; i++) {
|
|
|
|
// Use the stable sequence for an integer to use as a hash key
|
|
|
|
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++)
|
|
|
|
this._workspaces[i].syncStacking(stackIndices);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Handles a drop onto the (+) button; assumes the new workspace
|
|
|
|
// has already been added
|
|
|
|
acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
|
|
|
return this._workspaces[this._workspaces.length - 1].acceptDrop(source, dropActor, x, y, time);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Get the grid position of the active workspace.
|
|
|
|
getActiveWorkspacePosition: function() {
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
|
|
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
return [activeWorkspace.x, activeWorkspace.y];
|
2010-01-23 11:24:49 +00:00
|
|
|
},
|
|
|
|
|
2010-02-12 20:12:39 +00:00
|
|
|
canAddWorkspace: function() {
|
|
|
|
return global.screen.n_workspaces < MAX_WORKSPACES;
|
|
|
|
},
|
|
|
|
|
|
|
|
addWorkspace: function() {
|
2010-11-12 09:28:28 +00:00
|
|
|
let ws = null;
|
2010-05-13 18:29:00 +00:00
|
|
|
if (!this.canAddWorkspace()) {
|
2010-07-22 15:49:43 +00:00
|
|
|
Main.overview.shellInfo.setMessage(_("Can't add a new workspace because maximum workspaces limit has been reached."));
|
2010-11-12 09:28:28 +00:00
|
|
|
} else {
|
|
|
|
let currentTime = global.get_current_time();
|
|
|
|
ws = global.screen.append_new_workspace(false, currentTime);
|
|
|
|
ws.activate(currentTime);
|
2010-05-13 18:29:00 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
return ws;
|
2010-02-12 20:12:39 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
canRemoveWorkspace: function() {
|
2010-05-13 18:29:00 +00:00
|
|
|
return this._getWorkspaceIndexToRemove() > 0;
|
2010-02-12 20:12:39 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
removeWorkspace: function() {
|
2010-05-13 18:29:00 +00:00
|
|
|
if (!this.canRemoveWorkspace()) {
|
2010-07-22 15:49:43 +00:00
|
|
|
Main.overview.shellInfo.setMessage(_("Can't remove the first workspace."));
|
2010-05-13 18:29:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let index = this._getWorkspaceIndexToRemove();
|
|
|
|
let metaWorkspace = this._workspaces[index].metaWorkspace;
|
|
|
|
global.screen.remove_workspace(metaWorkspace,
|
|
|
|
global.get_current_time());
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
2010-09-10 02:00:28 +00:00
|
|
|
_handleDragOverNewWorkspace: function(source, dropActor, x, y, time) {
|
|
|
|
if (source instanceof Workspace.WindowClone)
|
|
|
|
return DND.DragMotionResult.MOVE_DROP;
|
|
|
|
if (source.shellWorkspaceLaunch)
|
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
},
|
|
|
|
|
2010-03-22 20:41:26 +00:00
|
|
|
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
2010-05-13 18:29:00 +00:00
|
|
|
let ws = this.addWorkspace();
|
|
|
|
if (ws == null)
|
|
|
|
return false;
|
2010-03-22 20:41:26 +00:00
|
|
|
return this.acceptNewWorkspaceDrop(source, dropActor, x, y, time);
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
// Compute the position, scale and opacity of the workspaces, but don't
|
|
|
|
// actually change the actors to match
|
|
|
|
_computeWorkspacePositions: function() {
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
let scale = this._width / global.screen_width;
|
2010-03-16 15:51:24 +00:00
|
|
|
if (this._inDrag)
|
2010-03-22 23:01:44 +00:00
|
|
|
scale *= WORKSPACE_DRAGGING_SCALE;
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2010-02-23 04:44:09 +00:00
|
|
|
this._setWorkspaceDraggable(active, true);
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
let _width = this._workspaces[0].actor.width * scale;
|
|
|
|
let _height = this._workspaces[0].actor.height * scale;
|
|
|
|
|
|
|
|
this._activeWorkspaceX = (this._width - _width) / 2;
|
|
|
|
this._activeWorkspaceY = (this._height - _height) / 2;
|
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
|
|
let workspace = this._workspaces[w];
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
workspace.opacity = (this._inDrag && w != active) ? 200 : 255;
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-01-22 02:33:48 +00:00
|
|
|
workspace.scale = scale;
|
2010-11-12 12:32:06 +00:00
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
|
|
|
workspace.x = this._x + this._activeWorkspaceX
|
|
|
|
- (w - active) * (_width + this._spacing);
|
|
|
|
} else {
|
|
|
|
workspace.x = this._x + this._activeWorkspaceX
|
|
|
|
+ (w - active) * (_width + this._spacing);
|
|
|
|
}
|
2010-11-12 09:28:28 +00:00
|
|
|
workspace.y = this._y + this._activeWorkspaceY;
|
2010-02-15 13:29:34 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-02-10 05:56:36 +00:00
|
|
|
_scrollToActive: function(showAnimation) {
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
|
2010-06-08 00:43:32 +00:00
|
|
|
this._computeWorkspacePositions();
|
2010-03-16 15:51:24 +00:00
|
|
|
this._updateWorkspaceActors(showAnimation);
|
2010-07-11 12:41:17 +00:00
|
|
|
this._updateScrollAdjustment(active, showAnimation);
|
2010-02-10 05:56:36 +00:00
|
|
|
},
|
|
|
|
|
2010-02-23 04:44:09 +00:00
|
|
|
// _setWorkspaceDraggable:
|
|
|
|
// @index: workspace index
|
|
|
|
// @draggable: whether workspace @index should be draggable
|
|
|
|
//
|
|
|
|
// If @draggable is %true, set up workspace @index to allow switching
|
|
|
|
// workspaces by dragging the desktop - if a draggable workspace has
|
|
|
|
// been set up before, it will be reset before the new one is made
|
|
|
|
// draggable.
|
|
|
|
// If @draggable is %false, workspace @index is reset to no longer allow
|
|
|
|
// dragging.
|
|
|
|
_setWorkspaceDraggable: function(index, draggable) {
|
|
|
|
if (index < 0 || index >= global.n_workspaces)
|
|
|
|
return;
|
|
|
|
|
2010-07-15 14:21:32 +00:00
|
|
|
let dragActor = this._workspaces[index].actor;
|
2010-02-23 04:44:09 +00:00
|
|
|
|
|
|
|
if (draggable) {
|
|
|
|
this._workspaces[index].actor.reactive = true;
|
|
|
|
|
|
|
|
// reset old draggable workspace
|
|
|
|
if (this._dragIndex > -1)
|
|
|
|
this._setWorkspaceDraggable(this._dragIndex, false);
|
|
|
|
|
|
|
|
this._dragIndex = index;
|
|
|
|
this._buttonPressId = dragActor.connect('button-press-event',
|
|
|
|
Lang.bind(this, this._onButtonPress));
|
|
|
|
} else {
|
|
|
|
this._dragIndex = -1;
|
|
|
|
|
|
|
|
if (this._buttonPressId > 0) {
|
|
|
|
if (dragActor.get_stage())
|
|
|
|
dragActor.disconnect(this._buttonPressId);
|
|
|
|
this._buttonPressId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._capturedEventId > 0) {
|
|
|
|
global.stage.disconnect(this._capturedEventId);
|
|
|
|
this._capturedEventId = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// start dragging the active workspace
|
|
|
|
_onButtonPress: function(actor, event) {
|
2010-07-15 14:21:32 +00:00
|
|
|
if (actor != event.get_source())
|
|
|
|
return;
|
|
|
|
|
2010-02-23 04:44:09 +00:00
|
|
|
if (this._dragIndex == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let [stageX, stageY] = event.get_coords();
|
|
|
|
this._dragStartX = this._dragX = stageX;
|
|
|
|
this._scrolling = true;
|
|
|
|
this._capturedEventId = global.stage.connect('captured-event',
|
|
|
|
Lang.bind(this, this._onCapturedEvent));
|
|
|
|
},
|
|
|
|
|
|
|
|
// handle captured events while dragging a workspace
|
|
|
|
_onCapturedEvent: function(actor, event) {
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
let stageX, stageY;
|
|
|
|
|
|
|
|
switch (event.type()) {
|
|
|
|
case Clutter.EventType.BUTTON_RELEASE:
|
|
|
|
this._scrolling = false;
|
|
|
|
|
|
|
|
[stageX, stageY] = event.get_coords();
|
|
|
|
|
|
|
|
// default to snapping back to the original workspace
|
|
|
|
let activate = this._dragIndex;
|
|
|
|
let last = global.screen.n_workspaces - 1;
|
|
|
|
|
2010-05-10 19:28:26 +00:00
|
|
|
// If the user has moved more than half a workspace, we want to "settle"
|
|
|
|
// to the new workspace even if the user stops dragging rather "throws"
|
|
|
|
// by releasing during the drag.
|
2010-07-11 12:41:17 +00:00
|
|
|
let noStop = Math.abs(activate - this._scrollAdjustment.value) > 0.5;
|
2010-05-10 19:28:26 +00:00
|
|
|
|
2010-11-12 12:32:06 +00:00
|
|
|
let difference = stageX > this._dragStartX ? -1 : 1;
|
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
|
|
difference *= -1;
|
|
|
|
|
2010-05-10 19:28:26 +00:00
|
|
|
// We detect if the user is stopped by comparing the timestamp of the button
|
|
|
|
// release with the timestamp of the last motion. Experimentally, a difference
|
|
|
|
// of 0 or 1 millisecond indicates that the mouse is in motion, a larger
|
|
|
|
// difference indicates that the mouse is stopped.
|
|
|
|
if ((this._lastMotionTime > 0 && this._lastMotionTime > event.get_time() - 2) || noStop) {
|
2010-11-12 12:32:06 +00:00
|
|
|
if (activate + difference >= 0 &&
|
|
|
|
activate + difference <= last)
|
|
|
|
activate += difference;
|
2010-05-10 19:28:26 +00:00
|
|
|
}
|
2010-02-23 04:44:09 +00:00
|
|
|
|
|
|
|
if (activate != active) {
|
2010-02-14 23:32:57 +00:00
|
|
|
let workspace = this._workspaces[activate].metaWorkspace;
|
2010-02-23 04:44:09 +00:00
|
|
|
workspace.activate(global.get_current_time());
|
|
|
|
} else {
|
|
|
|
this._scrollToActive(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stageX == this._dragStartX)
|
|
|
|
// no motion? It's a click!
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Clutter.EventType.MOTION:
|
|
|
|
[stageX, stageY] = event.get_coords();
|
|
|
|
let dx = this._dragX - stageX;
|
|
|
|
let primary = global.get_primary_monitor();
|
|
|
|
|
2010-11-12 12:32:06 +00:00
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
|
|
this._scrollAdjustment.value -= (dx / primary.width);
|
|
|
|
else
|
|
|
|
this._scrollAdjustment.value += (dx / primary.width);
|
2010-02-23 04:44:09 +00:00
|
|
|
this._dragX = stageX;
|
2010-05-10 19:28:26 +00:00
|
|
|
this._lastMotionTime = event.get_time();
|
2010-02-23 04:44:09 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
// Update workspace actors parameters to the values calculated in
|
|
|
|
// _computeWorkspacePositions()
|
|
|
|
// @showAnimation: iff %true, transition between states
|
2010-03-16 15:51:24 +00:00
|
|
|
_updateWorkspaceActors: function(showAnimation) {
|
2010-02-10 05:56:36 +00:00
|
|
|
let active = global.screen.get_active_workspace_index();
|
2010-03-22 23:01:44 +00:00
|
|
|
let targetWorkspaceNewX = this._x + this._activeWorkspaceX;
|
2010-11-12 09:28:28 +00:00
|
|
|
let targetWorkspaceCurrentX = this._workspaces[active].x;
|
2010-03-22 23:01:44 +00:00
|
|
|
let dx = targetWorkspaceNewX - targetWorkspaceCurrentX;
|
2010-02-10 05:56:36 +00:00
|
|
|
|
2010-02-23 04:44:09 +00:00
|
|
|
this._setWorkspaceDraggable(active, true);
|
2010-03-22 23:01:44 +00:00
|
|
|
this._animating = showAnimation;
|
|
|
|
|
2010-02-10 05:56:36 +00:00
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
|
|
let workspace = this._workspaces[w];
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
Tweener.removeTweens(workspace.actor);
|
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
workspace.x += dx;
|
2010-02-10 05:56:36 +00:00
|
|
|
|
|
|
|
if (showAnimation) {
|
2010-11-12 09:28:28 +00:00
|
|
|
let params = { x: workspace.x,
|
|
|
|
y: workspace.y,
|
2010-10-04 18:04:23 +00:00
|
|
|
scale_x: workspace.scale,
|
|
|
|
scale_y: workspace.scale,
|
|
|
|
opacity: workspace.opacity,
|
|
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
|
|
transition: 'easeOutQuad'
|
|
|
|
};
|
|
|
|
// we have to call _updateVisibility() once before the
|
|
|
|
// animation and once afterwards - it does not really
|
|
|
|
// matter which tween we use, so we pick the first one ...
|
|
|
|
if (w == 0) {
|
|
|
|
this._updateVisibility();
|
|
|
|
params.onComplete = Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this._animating = false;
|
|
|
|
this._updateVisibility();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Tweener.addTween(workspace.actor, params);
|
2010-01-22 02:33:48 +00:00
|
|
|
} else {
|
2010-03-16 15:51:24 +00:00
|
|
|
workspace.actor.set_scale(workspace.scale, workspace.scale);
|
2010-11-12 09:28:28 +00:00
|
|
|
workspace.actor.set_position(workspace.x, workspace.y);
|
2010-03-16 15:51:24 +00:00
|
|
|
workspace.actor.opacity = workspace.opacity;
|
2010-10-04 18:04:23 +00:00
|
|
|
if (w == 0)
|
|
|
|
this._updateVisibility();
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
2010-03-16 15:51:24 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
for (let l = 0; l < this._lostWorkspaces.length; l++) {
|
|
|
|
let workspace = this._lostWorkspaces[l];
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
Tweener.removeTweens(workspace.actor);
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-11-12 09:28:28 +00:00
|
|
|
workspace.x += dx;
|
2010-03-22 23:01:44 +00:00
|
|
|
workspace.actor.show();
|
|
|
|
workspace.hideWindowsOverlays();
|
|
|
|
|
|
|
|
if (showAnimation) {
|
|
|
|
Tweener.addTween(workspace.actor,
|
2010-11-12 09:28:28 +00:00
|
|
|
{ x: workspace.x,
|
2010-03-22 23:01:44 +00:00
|
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
|
|
transition: 'easeOutQuad',
|
|
|
|
onComplete: Lang.bind(this,
|
|
|
|
this._cleanWorkspaces)
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this._cleanWorkspaces();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2010-02-11 14:52:49 +00:00
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
_updateVisibility: function() {
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
2010-02-11 14:52:49 +00:00
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
|
|
let workspace = this._workspaces[w];
|
|
|
|
if (this._animating || this._scrolling) {
|
|
|
|
workspace.hideWindowsOverlays();
|
|
|
|
workspace.actor.show();
|
2010-02-11 14:52:49 +00:00
|
|
|
} else {
|
2010-03-22 23:01:44 +00:00
|
|
|
workspace.showWindowsOverlays();
|
|
|
|
if (this._inDrag)
|
|
|
|
workspace.actor.visible = (Math.abs(w - active) <= 1);
|
|
|
|
else
|
|
|
|
workspace.actor.visible = (w == active);
|
2010-02-11 14:52:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_cleanWorkspaces: function() {
|
|
|
|
if (this._lostWorkspaces.length == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (let l = 0; l < this._lostWorkspaces.length; l++)
|
|
|
|
this._lostWorkspaces[l].destroy();
|
|
|
|
this._lostWorkspaces = [];
|
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
this._computeWorkspacePositions();
|
|
|
|
this._updateWorkspaceActors(false);
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
2010-07-11 12:41:17 +00:00
|
|
|
_updateScrollAdjustment: function(index, showAnimation) {
|
|
|
|
if (this._scrolling)
|
2010-02-10 05:56:36 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
this._animatingScroll = true;
|
|
|
|
|
|
|
|
if (showAnimation) {
|
2010-07-11 12:41:17 +00:00
|
|
|
Tweener.addTween(this._scrollAdjustment, {
|
2010-02-10 05:56:36 +00:00
|
|
|
value: index,
|
|
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
|
|
transition: 'easeOutQuad',
|
|
|
|
onComplete: Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this._animatingScroll = false;
|
|
|
|
})
|
|
|
|
});
|
|
|
|
} else {
|
2010-07-11 12:41:17 +00:00
|
|
|
this._scrollAdjustment.value = index;
|
2010-02-10 05:56:36 +00:00
|
|
|
this._animatingScroll = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-02-14 23:32:57 +00:00
|
|
|
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) {
|
2010-05-19 17:26:41 +00:00
|
|
|
let active = global.screen.get_active_workspace_index();
|
2010-01-22 02:33:48 +00:00
|
|
|
|
2010-03-22 19:52:50 +00:00
|
|
|
for (let l = 0; l < lostWorkspaces.length; l++)
|
|
|
|
lostWorkspaces[l].disconnectAll();
|
|
|
|
|
2010-07-11 12:41:17 +00:00
|
|
|
Tweener.addTween(this._scrollAdjustment,
|
|
|
|
{ upper: newNumWorkspaces,
|
|
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
|
|
transition: 'easeOutQuad'
|
|
|
|
});
|
2010-01-22 02:33:48 +00:00
|
|
|
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
2010-11-15 20:58:27 +00:00
|
|
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
2010-03-22 00:39:49 +00:00
|
|
|
this.actor.add_actor(this._workspaces[w].actor);
|
2010-02-14 23:32:57 +00:00
|
|
|
|
2010-03-22 23:01:44 +00:00
|
|
|
this._computeWorkspacePositions();
|
|
|
|
this._updateWorkspaceActors(false);
|
2010-01-22 02:33:48 +00:00
|
|
|
} else {
|
2010-02-14 23:32:57 +00:00
|
|
|
this._lostWorkspaces = lostWorkspaces;
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
|
|
|
|
2010-05-08 21:45:09 +00:00
|
|
|
this._scrollToActive(true);
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_activeWorkspaceChanged: function(wm, from, to, direction) {
|
2010-02-10 05:56:36 +00:00
|
|
|
if (this._scrolling)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._scrollToActive(true);
|
2010-01-22 02:33:48 +00:00
|
|
|
},
|
|
|
|
|
2010-03-16 15:51:24 +00:00
|
|
|
_onDestroy: function() {
|
2010-11-12 09:28:28 +00:00
|
|
|
Main.overview.disconnect(this._overviewShowingId);
|
|
|
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
|
|
|
global.screen.disconnect(this._restackedNotifyId);
|
|
|
|
|
2010-02-23 04:44:09 +00:00
|
|
|
this._setWorkspaceDraggable(this._dragIndex, false);
|
2010-03-16 15:51:24 +00:00
|
|
|
if (this._timeoutId) {
|
|
|
|
Mainloop.source_remove(this._timeoutId);
|
|
|
|
this._timeoutId = 0;
|
|
|
|
}
|
2010-05-08 14:06:28 +00:00
|
|
|
if (this._itemDragBeginId > 0) {
|
|
|
|
Main.overview.disconnect(this._itemDragBeginId);
|
|
|
|
this._itemDragBeginId = 0;
|
|
|
|
}
|
|
|
|
if (this._itemDragEndId > 0) {
|
|
|
|
Main.overview.disconnect(this._itemDragEndId);
|
|
|
|
this._itemDragEndId = 0;
|
|
|
|
}
|
2010-11-15 20:58:27 +00:00
|
|
|
if (this._windowDragBeginId > 0) {
|
|
|
|
Main.overview.disconnect(this._windowDragBeginId);
|
|
|
|
this._windowDragBeginId = 0;
|
|
|
|
}
|
|
|
|
if (this._windowDragEndId > 0) {
|
|
|
|
Main.overview.disconnect(this._windowDragEndId);
|
|
|
|
this._windowDragEndId = 0;
|
2010-03-22 19:52:50 +00:00
|
|
|
}
|
2010-03-16 15:51:24 +00:00
|
|
|
},
|
|
|
|
|
2010-05-08 14:06:28 +00:00
|
|
|
_dragBegin: function() {
|
2010-07-11 12:41:17 +00:00
|
|
|
if (this._scrolling)
|
2010-03-16 15:51:24 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
this._inDrag = true;
|
2010-03-22 23:01:44 +00:00
|
|
|
this._computeWorkspacePositions();
|
2010-03-16 15:51:24 +00:00
|
|
|
this._updateWorkspaceActors(true);
|
|
|
|
|
2010-06-06 15:24:51 +00:00
|
|
|
this._dragMonitor = {
|
|
|
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
|
|
|
};
|
|
|
|
DND.addDragMonitor(this._dragMonitor);
|
2010-03-16 15:51:24 +00:00
|
|
|
},
|
|
|
|
|
2010-06-06 15:24:51 +00:00
|
|
|
_onDragMotion: function(dragEvent) {
|
2010-06-02 16:38:31 +00:00
|
|
|
let primary = global.get_primary_monitor();
|
|
|
|
|
2010-06-06 15:24:51 +00:00
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
2010-11-12 12:32:06 +00:00
|
|
|
let leftWorkspace, rightWorkspace;
|
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
|
|
|
leftWorkspace = this._workspaces[activeWorkspaceIndex + 1];
|
|
|
|
rightWorkspace = this._workspaces[activeWorkspaceIndex - 1];
|
|
|
|
} else {
|
|
|
|
leftWorkspace = this._workspaces[activeWorkspaceIndex - 1];
|
|
|
|
rightWorkspace = this._workspaces[activeWorkspaceIndex + 1];
|
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
let hoverWorkspace = null;
|
|
|
|
|
|
|
|
// reactive monitor edges
|
|
|
|
let leftEdge = primary.x;
|
|
|
|
let switchLeft = (dragEvent.x <= leftEdge && leftWorkspace);
|
|
|
|
if (switchLeft && this._dragOverLastX != leftEdge) {
|
|
|
|
leftWorkspace.metaWorkspace.activate(global.get_current_time());
|
|
|
|
leftWorkspace.setReservedSlot(dragEvent.dragActor._delegate);
|
|
|
|
this._dragOverLastX = leftEdge;
|
|
|
|
|
2010-09-10 02:00:28 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2010-03-16 15:51:24 +00:00
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
let rightEdge = primary.x + primary.width - 1;
|
|
|
|
let switchRight = (dragEvent.x >= rightEdge && rightWorkspace);
|
|
|
|
if (switchRight && this._dragOverLastX != rightEdge) {
|
|
|
|
rightWorkspace.metaWorkspace.activate(global.get_current_time());
|
|
|
|
rightWorkspace.setReservedSlot(dragEvent.dragActor._delegate);
|
|
|
|
this._dragOverLastX = rightEdge;
|
|
|
|
|
2010-09-10 02:00:28 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2010-03-16 15:51:24 +00:00
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
this._dragOverLastX = dragEvent.x;
|
2010-09-10 02:00:28 +00:00
|
|
|
let result = DND.DragMotionResult.CONTINUE;
|
2010-03-16 15:51:24 +00:00
|
|
|
|
2010-06-06 15:24:51 +00:00
|
|
|
// check hover state of new workspace area / inactive workspaces
|
|
|
|
if (leftWorkspace) {
|
2010-07-16 17:15:29 +00:00
|
|
|
if (leftWorkspace.actor.contains(dragEvent.targetActor)) {
|
2010-06-06 15:24:51 +00:00
|
|
|
hoverWorkspace = leftWorkspace;
|
2010-06-08 00:43:32 +00:00
|
|
|
leftWorkspace.opacity = leftWorkspace.actor.opacity = 255;
|
2010-09-10 02:00:28 +00:00
|
|
|
result = leftWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
2010-06-06 15:24:51 +00:00
|
|
|
} else {
|
2010-06-08 00:43:32 +00:00
|
|
|
leftWorkspace.opacity = leftWorkspace.actor.opacity = 200;
|
2010-06-06 15:24:51 +00:00
|
|
|
}
|
2010-03-16 15:51:24 +00:00
|
|
|
}
|
|
|
|
|
2010-06-06 15:24:51 +00:00
|
|
|
if (rightWorkspace) {
|
2010-07-16 17:15:29 +00:00
|
|
|
if (rightWorkspace.actor.contains(dragEvent.targetActor)) {
|
2010-06-06 15:24:51 +00:00
|
|
|
hoverWorkspace = rightWorkspace;
|
2010-06-08 00:43:32 +00:00
|
|
|
rightWorkspace.opacity = rightWorkspace.actor.opacity = 255;
|
2010-09-10 02:00:28 +00:00
|
|
|
result = rightWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
|
2010-06-06 15:24:51 +00:00
|
|
|
} else {
|
2010-06-08 00:43:32 +00:00
|
|
|
rightWorkspace.opacity = rightWorkspace.actor.opacity = 200;
|
2010-06-06 15:24:51 +00:00
|
|
|
}
|
2010-03-16 15:51:24 +00:00
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
|
|
|
|
// handle delayed workspace switches
|
|
|
|
if (hoverWorkspace) {
|
2010-03-16 15:51:24 +00:00
|
|
|
if (!this._timeoutId)
|
2010-06-06 15:24:51 +00:00
|
|
|
this._timeoutId = Mainloop.timeout_add_seconds(1,
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
hoverWorkspace.metaWorkspace.activate(global.get_current_time());
|
|
|
|
hoverWorkspace.setReservedSlot(dragEvent.dragActor._delegate);
|
|
|
|
return false;
|
|
|
|
}));
|
2010-03-16 15:51:24 +00:00
|
|
|
} else {
|
|
|
|
if (this._timeoutId) {
|
|
|
|
Mainloop.source_remove(this._timeoutId);
|
|
|
|
this._timeoutId = 0;
|
|
|
|
}
|
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
|
2010-09-10 02:00:28 +00:00
|
|
|
return result;
|
2010-03-16 15:51:24 +00:00
|
|
|
},
|
|
|
|
|
2010-05-08 14:06:28 +00:00
|
|
|
_dragEnd: function() {
|
2010-03-16 15:51:24 +00:00
|
|
|
if (this._timeoutId) {
|
|
|
|
Mainloop.source_remove(this._timeoutId);
|
|
|
|
this._timeoutId = 0;
|
|
|
|
}
|
2010-06-06 15:24:51 +00:00
|
|
|
DND.removeMonitor(this._dragMonitor);
|
2010-03-16 15:51:24 +00:00
|
|
|
this._inDrag = false;
|
2010-03-22 23:01:44 +00:00
|
|
|
this._computeWorkspacePositions();
|
2010-03-16 15:51:24 +00:00
|
|
|
this._updateWorkspaceActors(true);
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++)
|
|
|
|
this._workspaces[i].setReservedSlot(null);
|
|
|
|
},
|
|
|
|
|
2010-07-11 12:41:17 +00:00
|
|
|
// sync the workspaces' positions to the value of the scroll adjustment
|
2010-02-10 05:56:36 +00:00
|
|
|
// and change the active workspace if appropriate
|
|
|
|
_onScroll: function(adj) {
|
|
|
|
if (this._animatingScroll)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
let current = Math.round(adj.value);
|
|
|
|
|
|
|
|
if (active != current) {
|
2010-02-14 23:32:57 +00:00
|
|
|
let metaWorkspace = this._workspaces[current].metaWorkspace;
|
2010-07-11 12:41:17 +00:00
|
|
|
metaWorkspace.activate(global.get_current_time());
|
2010-02-10 05:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let last = this._workspaces.length - 1;
|
|
|
|
let firstWorkspaceX = this._workspaces[0].actor.x;
|
|
|
|
let lastWorkspaceX = this._workspaces[last].actor.x;
|
|
|
|
let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
|
|
|
|
|
|
|
|
if (adj.upper == 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let currentX = firstWorkspaceX;
|
|
|
|
let newX = this._x - adj.value / (adj.upper - 1) * workspacesWidth;
|
|
|
|
|
|
|
|
let dx = newX - currentX;
|
|
|
|
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
|
|
this._workspaces[i]._hideAllOverlays();
|
2010-03-16 15:51:24 +00:00
|
|
|
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
|
2010-02-10 05:56:36 +00:00
|
|
|
this._workspaces[i].actor.x += dx;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_getWorkspaceIndexToRemove: function() {
|
|
|
|
return global.screen.get_active_workspace_index();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Signals.addSignalMethods(WorkspacesView.prototype);
|
|
|
|
|
|
|
|
|
|
|
|
function WorkspaceIndicatorPanel() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkspaceIndicatorPanel.prototype = {
|
|
|
|
_init: function() {
|
|
|
|
this.actor = new Shell.GenericContainer({ clip_to_allocation: true });
|
|
|
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
|
|
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
|
|
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
|
|
|
|
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
|
|
|
|
this._box = new St.BoxLayout({ style_class: 'workspace-indicator-panel' });
|
|
|
|
this.actor.add_actor(this._box);
|
|
|
|
|
|
|
|
this._switchWorkspaceNotifyId =
|
|
|
|
global.window_manager.connect('switch-workspace',
|
|
|
|
Lang.bind(this, this._updateActive));
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDestroy: function() {
|
|
|
|
if (this._switchWorkspaceNotifyId > 0)
|
|
|
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
|
|
|
this._switchWorkspaceNotifyId = 0;
|
|
|
|
this._workspaces = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
_allocate: function(actor, box, flags) {
|
|
|
|
let children = this._box.get_children();
|
|
|
|
|
|
|
|
// Don't display a single indicator
|
|
|
|
if (children.length == 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let availWidth = box.x2 - box.x1;
|
|
|
|
let availHeight = box.y2 - box.y1;
|
|
|
|
let [minWidth, natWidth] = this._box.get_preferred_width(-1);
|
2010-11-12 21:52:44 +00:00
|
|
|
let [minHeight, natHeight] = this._box.get_preferred_height(-1);
|
2010-10-04 14:42:11 +00:00
|
|
|
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
childBox.x1 = Math.floor((availWidth - natWidth) / 2);
|
|
|
|
childBox.x2 = childBox.x1 + natWidth;
|
2010-11-12 21:52:44 +00:00
|
|
|
childBox.y1 = Math.floor((availHeight - natHeight) / 2);
|
|
|
|
childBox.y2 = childBox.y2 + natHeight;
|
2010-10-04 14:42:11 +00:00
|
|
|
|
|
|
|
this._box.allocate(childBox, flags);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
|
|
let [minWidth, natWidth] = this._box.get_preferred_width(-1);
|
|
|
|
alloc.min_size = 0;
|
|
|
|
alloc.natural_size = natWidth;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
|
|
let [minHeight, natHeight] = this._box.get_preferred_height(-1);
|
2010-11-12 21:52:44 +00:00
|
|
|
alloc.min_size = minHeight * INDICATOR_HOVER_SCALE;
|
|
|
|
alloc.natural_size = natHeight * INDICATOR_HOVER_SCALE;
|
2010-10-04 14:42:11 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
updateWorkspaces: function(workspaces) {
|
|
|
|
this._workspaces = workspaces;
|
|
|
|
|
|
|
|
this._box.remove_all();
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
2010-11-12 21:52:44 +00:00
|
|
|
let actor = new St.Button({ style_class: 'workspace-indicator',
|
|
|
|
track_hover: true });
|
2010-10-04 14:42:11 +00:00
|
|
|
let workspace = this._workspaces[i];
|
|
|
|
let metaWorkspace = this._workspaces[i].metaWorkspace;
|
|
|
|
|
|
|
|
actor.connect('clicked', Lang.bind(this, function() {
|
|
|
|
metaWorkspace.activate(global.get_current_time());
|
|
|
|
}));
|
2010-11-12 21:52:44 +00:00
|
|
|
actor.connect('notify::hover', Lang.bind(this, function() {
|
|
|
|
if (actor.hover)
|
|
|
|
actor.set_scale_with_gravity(INDICATOR_HOVER_SCALE,
|
|
|
|
INDICATOR_HOVER_SCALE,
|
|
|
|
Clutter.Gravity.CENTER);
|
|
|
|
else
|
|
|
|
actor.set_scale(1.0, 1.0);
|
|
|
|
}));
|
2010-10-04 14:42:11 +00:00
|
|
|
|
|
|
|
actor._delegate = {
|
|
|
|
acceptDrop: Lang.bind(this,
|
|
|
|
function(source, actor, x, y, time) {
|
|
|
|
if (workspace.acceptDrop(source, actor, x, y, time)) {
|
|
|
|
metaWorkspace.activate(time);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}),
|
|
|
|
handleDragOver: Lang.bind(this,
|
|
|
|
function(source, actor, x, y, time) {
|
|
|
|
return workspace.handleDragOver(source, actor, x, y, time);
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
|
|
|
|
|
|
this._box.add(actor);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._updateActive();
|
|
|
|
},
|
|
|
|
|
|
|
|
_updateActive: function() {
|
|
|
|
let children = this._box.get_children();
|
|
|
|
let activeIndex = global.screen.get_active_workspace_index();
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
|
|
if (i == activeIndex)
|
|
|
|
children[i].add_style_class_name('active');
|
|
|
|
else
|
|
|
|
children[i].remove_style_class_name('active');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-02-10 05:56:36 +00:00
|
|
|
// handle scroll wheel events:
|
|
|
|
// activate the next or previous workspace and let the signal handler
|
|
|
|
// manage the animation
|
|
|
|
_onScrollEvent: function(actor, event) {
|
|
|
|
let direction = event.get_scroll_direction();
|
|
|
|
let current = global.screen.get_active_workspace_index();
|
|
|
|
let last = global.screen.n_workspaces - 1;
|
|
|
|
let activate = current;
|
2010-11-12 12:32:06 +00:00
|
|
|
|
|
|
|
let difference = direction == Clutter.ScrollDirection.UP ? -1 : 1;
|
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
|
|
difference *= -1;
|
|
|
|
|
|
|
|
if (activate + difference >= 0 && activate + difference <= last)
|
|
|
|
activate += difference;
|
2010-02-10 05:56:36 +00:00
|
|
|
|
|
|
|
if (activate != current) {
|
2010-02-14 23:32:57 +00:00
|
|
|
let metaWorkspace = this._workspaces[activate].metaWorkspace;
|
2010-02-10 05:56:36 +00:00
|
|
|
metaWorkspace.activate(global.get_current_time());
|
|
|
|
}
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
function WorkspaceControlsContainer(controls) {
|
|
|
|
this._init(controls);
|
|
|
|
}
|
2010-02-12 20:12:39 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
WorkspaceControlsContainer.prototype = {
|
|
|
|
_init: function(controls) {
|
|
|
|
this.actor = new Shell.GenericContainer({ clip_to_allocation: true });
|
|
|
|
this.actor.connect('get-preferred-width',
|
|
|
|
Lang.bind(this, this._getPreferredWidth));
|
|
|
|
this.actor.connect('get-preferred-height',
|
|
|
|
Lang.bind(this, this._getPreferredHeight));
|
|
|
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
|
|
|
|
|
|
|
this.actor.add_actor(controls);
|
|
|
|
|
|
|
|
this._controls = controls;
|
|
|
|
this._controls.reactive = true;
|
|
|
|
this._controls.track_hover = true;
|
|
|
|
this._controls.connect('notify::hover',
|
|
|
|
Lang.bind(this, this._onHoverChanged));
|
|
|
|
|
|
|
|
let workId = Main.initializeDeferredWork(this.actor,
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
Main.overview.connect('item-drag-begin',
|
|
|
|
Lang.bind(this, this.popOut));
|
|
|
|
Main.overview.connect('item-drag-end',
|
|
|
|
Lang.bind(this, this.popIn));
|
2010-11-15 20:58:27 +00:00
|
|
|
Main.overview.connect('window-drag-begin',
|
|
|
|
Lang.bind(this, this.popOut));
|
|
|
|
Main.overview.connect('window-drag-end',
|
|
|
|
Lang.bind(this, this.popIn));
|
2010-10-04 14:42:11 +00:00
|
|
|
this._controls.x = this._poppedInX();
|
|
|
|
}));
|
|
|
|
},
|
2010-02-15 13:29:34 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
|
|
let [minWidth, natWidth] = this._controls.get_preferred_width(-1);
|
|
|
|
alloc.min_size = minWidth;
|
|
|
|
alloc.natural_size = natWidth;
|
2010-02-14 23:32:57 +00:00
|
|
|
},
|
2010-02-12 20:12:39 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
|
|
let [minHeight, natHeight] = this._controls.get_preferred_height(-1);
|
|
|
|
alloc.min_size = minHeight;
|
|
|
|
alloc.natural_size = natHeight;
|
|
|
|
},
|
2010-02-15 13:29:34 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_allocate: function(actor, box, flags) {
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
childBox.x1 = this._controls.x;
|
|
|
|
childBox.x2 = this._controls.x + this._controls.width;
|
|
|
|
childBox.y1 = 0;
|
|
|
|
childBox.y2 = box.y2 - box.y1;
|
|
|
|
this._controls.allocate(childBox, flags);
|
|
|
|
},
|
2010-04-16 13:38:21 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_onHoverChanged: function() {
|
|
|
|
if (this._controls.hover)
|
|
|
|
this.popOut();
|
|
|
|
else
|
|
|
|
this.popIn();
|
2010-02-15 13:29:34 +00:00
|
|
|
},
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
_poppedInX: function() {
|
|
|
|
let x = CONTROLS_POP_IN_FRACTION * this._controls.width;
|
|
|
|
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
|
|
|
return -x;
|
|
|
|
return x;
|
2010-02-12 20:12:39 +00:00
|
|
|
},
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
popOut: function() {
|
|
|
|
Tweener.addTween(this._controls,
|
|
|
|
{ x: 0,
|
|
|
|
time: CONTROLS_POP_IN_TIME,
|
|
|
|
transition: 'easeOutQuad' });
|
2010-02-12 20:12:39 +00:00
|
|
|
},
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
popIn: function() {
|
|
|
|
Tweener.addTween(this._controls,
|
|
|
|
{ x: this._poppedInX(),
|
|
|
|
time: CONTROLS_POP_IN_TIME,
|
|
|
|
transition: 'easeOutQuad' });
|
2010-01-22 02:33:48 +00:00
|
|
|
}
|
|
|
|
};
|
2010-02-14 23:32:57 +00:00
|
|
|
|
2010-07-29 07:55:08 +00:00
|
|
|
function WorkspacesDisplay() {
|
|
|
|
this._init();
|
2010-02-14 23:32:57 +00:00
|
|
|
}
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
WorkspacesDisplay.prototype = {
|
2010-07-29 07:55:08 +00:00
|
|
|
_init: function() {
|
2010-10-04 14:42:11 +00:00
|
|
|
this.actor = new St.BoxLayout();
|
2010-02-14 23:32:57 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
let workspacesBox = new St.BoxLayout({ vertical: true });
|
|
|
|
this.actor.add(workspacesBox, { expand: true });
|
|
|
|
|
|
|
|
// placeholder for window previews
|
2010-07-29 07:55:08 +00:00
|
|
|
this._workspacesBin = new St.Bin();
|
|
|
|
workspacesBox.add(this._workspacesBin, { expand: true });
|
2010-10-04 14:42:11 +00:00
|
|
|
|
|
|
|
this._workspaceIndicatorPanel = new WorkspaceIndicatorPanel();
|
|
|
|
workspacesBox.add(this._workspaceIndicatorPanel.actor);
|
|
|
|
|
|
|
|
let controls = new St.BoxLayout({ vertical: true,
|
|
|
|
style_class: 'workspace-controls' });
|
|
|
|
this._controlsContainer = new WorkspaceControlsContainer(controls);
|
|
|
|
this.actor.add(this._controlsContainer.actor);
|
|
|
|
|
|
|
|
// Add/remove workspace buttons
|
|
|
|
this._removeButton = new St.Button({ label: '–', // n-dash
|
|
|
|
style_class: 'remove-workspace' });
|
|
|
|
this._removeButton.connect('clicked', Lang.bind(this, function() {
|
|
|
|
this.workspacesView.removeWorkspace();
|
|
|
|
}));
|
|
|
|
controls.add(this._removeButton);
|
|
|
|
|
|
|
|
this._addButton = new St.Button({ label: '+',
|
|
|
|
style_class: 'add-workspace' });
|
|
|
|
this._addButton.connect('clicked', Lang.bind(this, function() {
|
|
|
|
this.workspacesView.addWorkspace();
|
|
|
|
}));
|
|
|
|
this._addButton._delegate = this._addButton;
|
|
|
|
this._addButton._delegate.acceptDrop = Lang.bind(this,
|
|
|
|
function(source, actor, x, y, time) {
|
|
|
|
return this.workspacesView._acceptNewWorkspaceDrop(source, actor, x, y, time);
|
|
|
|
});
|
|
|
|
this._addButton._delegate.handleDragOver = Lang.bind(this,
|
|
|
|
function(source, actor, x, y, time) {
|
|
|
|
return this.workspacesView._handleDragOverNewWorkspace(source, actor, x, y, time);
|
|
|
|
});
|
|
|
|
controls.add(this._addButton, { expand: true });
|
|
|
|
|
|
|
|
this.workspacesView = null;
|
2010-07-29 07:55:08 +00:00
|
|
|
this._nWorkspacesNotifyId = 0;
|
2010-02-14 23:32:57 +00:00
|
|
|
},
|
|
|
|
|
2010-07-29 07:55:08 +00:00
|
|
|
show: function() {
|
|
|
|
this._workspaces = [];
|
|
|
|
for (let i = 0; i < global.screen.n_workspaces; i++) {
|
|
|
|
let metaWorkspace = global.screen.get_workspace_by_index(i);
|
|
|
|
this._workspaces[i] = new Workspace.Workspace(metaWorkspace);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._nWorkspacesNotifyId =
|
|
|
|
global.screen.connect('notify::n-workspaces',
|
|
|
|
Lang.bind(this, this._workspacesChanged));
|
|
|
|
|
|
|
|
let binAllocation = this._workspacesBin.allocation;
|
|
|
|
let binWidth = binAllocation.x2 - binAllocation.x1;
|
|
|
|
let binHeight = binAllocation.y2 - binAllocation.y1;
|
|
|
|
|
|
|
|
// Workspaces expect to have the same ratio as the screen, so take
|
|
|
|
// this into account when fitting the workspace into the bin
|
|
|
|
let width, height;
|
|
|
|
let binRatio = binWidth / binHeight;
|
|
|
|
let wsRatio = global.screen_width / global.screen_height;
|
|
|
|
if (wsRatio > binRatio) {
|
|
|
|
width = binWidth;
|
|
|
|
height = Math.floor(binWidth / wsRatio);
|
|
|
|
} else {
|
|
|
|
width = Math.floor(binHeight * wsRatio);
|
|
|
|
height = binHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Position workspaces as if they were parented to this._workspacesBin
|
|
|
|
let [x, y] = this._workspacesBin.get_transformed_position();
|
|
|
|
x = Math.floor(x + Math.abs(binWidth - width) / 2);
|
|
|
|
y = Math.floor(y + Math.abs(binHeight - height) / 2);
|
|
|
|
|
|
|
|
let newView = new WorkspacesView(width, height, x, y, this._workspaces);
|
2010-11-12 09:28:28 +00:00
|
|
|
|
2010-02-14 23:32:57 +00:00
|
|
|
if (this.workspacesView)
|
|
|
|
this.workspacesView.destroy();
|
|
|
|
this.workspacesView = newView;
|
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces);
|
2010-02-14 23:32:57 +00:00
|
|
|
|
2010-10-04 14:42:11 +00:00
|
|
|
this._nWorkspacesNotifyId =
|
|
|
|
global.screen.connect('notify::n-workspaces',
|
|
|
|
Lang.bind(this, this._workspacesChanged));
|
|
|
|
},
|
|
|
|
|
|
|
|
hide: function() {
|
|
|
|
if (this._nWorkspacesNotifyId > 0)
|
|
|
|
global.screen.disconnect(this._nWorkspacesNotifyId);
|
|
|
|
this.workspacesView.destroy();
|
|
|
|
this.workspacesView = null;
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
|
|
this._workspaces[w].disconnectAll();
|
|
|
|
this._workspaces[w].destroy();
|
|
|
|
}
|
2010-02-14 23:32:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_workspacesChanged: function() {
|
|
|
|
let oldNumWorkspaces = this._workspaces.length;
|
|
|
|
let newNumWorkspaces = global.screen.n_workspaces;
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
|
|
|
|
if (oldNumWorkspaces == newNumWorkspaces)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let lostWorkspaces = [];
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
|
|
// Assume workspaces are only added at the end
|
|
|
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
|
|
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
|
|
|
this._workspaces[w] = new Workspace.Workspace(metaWorkspace);
|
|
|
|
}
|
|
|
|
} 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 metaWorkspace = global.screen.get_workspace_by_index(w);
|
|
|
|
if (this._workspaces[w].metaWorkspace != metaWorkspace) {
|
|
|
|
removedIndex = w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lostWorkspaces = this._workspaces.splice(removedIndex,
|
|
|
|
removedNum);
|
|
|
|
|
|
|
|
// Don't let the user try to select this workspace as it's
|
|
|
|
// making its exit.
|
|
|
|
for (let l = 0; l < lostWorkspaces.length; l++)
|
|
|
|
lostWorkspaces[l].setReactive(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
|
|
|
|
newNumWorkspaces,
|
|
|
|
lostWorkspaces);
|
2010-10-04 14:42:11 +00:00
|
|
|
this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces);
|
2010-02-14 23:32:57 +00:00
|
|
|
}
|
|
|
|
};
|
2010-10-04 14:42:11 +00:00
|
|
|
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|