c02b57efc3
Rename WorkspacesViewSwitch to WorkspacesControls and let it manage all workspace controls. Do not destroy and recreate the controls bar actor on each view change, but add it to the overview once and let it update itself. https://bugzilla.gnome.org/show_bug.cgi?id=610189
1314 lines
49 KiB
JavaScript
1314 lines
49 KiB
JavaScript
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
const Big = imports.gi.Big;
|
|
const Clutter = imports.gi.Clutter;
|
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
|
const Gdk = imports.gi.Gdk;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Lang = imports.lang;
|
|
const Mainloop = imports.mainloop;
|
|
const Meta = imports.gi.Meta;
|
|
const Pango = imports.gi.Pango;
|
|
const Shell = imports.gi.Shell;
|
|
const St = imports.gi.St;
|
|
const Signals = imports.signals;
|
|
|
|
const DND = imports.ui.dnd;
|
|
const Lightbox = imports.ui.lightbox;
|
|
const Main = imports.ui.main;
|
|
const Overview = imports.ui.overview;
|
|
const Panel = imports.ui.panel;
|
|
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;
|
|
|
|
// The values here are also used for gconf, and the key and value
|
|
// names must match
|
|
const WorkspacesViewType = {
|
|
SINGLE: 'single',
|
|
GRID: 'grid'
|
|
};
|
|
const WORKSPACES_VIEW_KEY = 'overview/workspaces_view';
|
|
|
|
const WORKSPACE_DRAGGING_SCALE = 0.85;
|
|
const WORKSPACE_SHADOW_SCALE = (1 - WORKSPACE_DRAGGING_SCALE) / 2;
|
|
|
|
function GenericWorkspacesView(width, height, x, y, animate) {
|
|
this._init(width, height, x, y, animate);
|
|
}
|
|
|
|
GenericWorkspacesView.prototype = {
|
|
_init: function(width, height, x, y, animate) {
|
|
this.actor = new St.Bin({ style_class: "workspaces" });
|
|
this._actor = new Clutter.Group();
|
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
this.actor.add_actor(this._actor);
|
|
this.actor.connect('style-changed', Lang.bind(this,
|
|
function() {
|
|
let node = this.actor.get_theme_node();
|
|
let [a, spacing] = node.get_length('spacing', false);
|
|
this._spacing = spacing;
|
|
this._positionWorkspaces();
|
|
}));
|
|
|
|
this._width = width;
|
|
this._height = height;
|
|
this._x = x;
|
|
this._y = y;
|
|
this._spacing = 0;
|
|
|
|
this._windowSelectionAppId = null;
|
|
|
|
this._workspaces = [];
|
|
|
|
this._highlightWindow = null;
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
|
|
// Create and position workspace objects
|
|
for (let w = 0; w < global.screen.n_workspaces; w++) {
|
|
this._addWorkspaceActor(w);
|
|
}
|
|
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++)
|
|
this._workspaces[w].zoomToOverview(animate);
|
|
}));
|
|
|
|
// Track changes to the number of workspaces
|
|
this._nWorkspacesNotifyId =
|
|
global.screen.connect('notify::n-workspaces',
|
|
Lang.bind(this, this._workspacesChanged));
|
|
this._switchWorkspaceNotifyId =
|
|
global.window_manager.connect('switch-workspace',
|
|
Lang.bind(this, this._activeWorkspaceChanged));
|
|
this._restackedNotifyId =
|
|
global.screen.connect('restacked',
|
|
Lang.bind(this, this._onRestacked));
|
|
},
|
|
|
|
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
if (this._workspaces[i].containsMetaWindow(metaWindow))
|
|
return this._workspaces[i];
|
|
}
|
|
return null;
|
|
},
|
|
|
|
_lookupCloneForMetaWindow: function (metaWindow) {
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
let clone = this._workspaces[i].lookupCloneForMetaWindow(metaWindow);
|
|
if (clone)
|
|
return clone;
|
|
}
|
|
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);
|
|
}
|
|
},
|
|
|
|
_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];
|
|
|
|
if (this._windowSelectionAppId != null)
|
|
this._clearApplicationWindowSelection(false);
|
|
|
|
activeWorkspace.actor.raise_top();
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++)
|
|
this._workspaces[w].zoomFromOverview();
|
|
},
|
|
|
|
destroy: function() {
|
|
this.actor.destroy();
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
for (let w = 0; w < this._workspaces.length; w++)
|
|
this._workspaces[w].destroy();
|
|
this._workspaces = [];
|
|
|
|
Main.overview.disconnect(this._overviewShowingId);
|
|
global.screen.disconnect(this._nWorkspacesNotifyId);
|
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
|
global.screen.disconnect(this._restackedNotifyId);
|
|
},
|
|
|
|
getScale: function() {
|
|
return this._workspaces[0].scale;
|
|
},
|
|
|
|
_onRestacked: function() {
|
|
let stack = global.get_windows();
|
|
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];
|
|
|
|
return [activeWorkspace.gridX, activeWorkspace.gridY];
|
|
},
|
|
|
|
createControllerBar: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
canAddWorkspace: function() {
|
|
return global.screen.n_workspaces < MAX_WORKSPACES;
|
|
},
|
|
|
|
addWorkspace: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
canRemoveWorkspace: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
removeWorkspace: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
_positionWorkspaces: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
_workspacesChanged: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
_activeWorkspaceChanged: function() {
|
|
throw new Error("Not implemented");
|
|
},
|
|
|
|
_addWorkspaceActor: function() {
|
|
throw new Error("Not implemented");
|
|
}
|
|
};
|
|
|
|
function MosaicView(width, height, x, y, animate) {
|
|
this._init(width, height, x, y, animate);
|
|
}
|
|
|
|
MosaicView.prototype = {
|
|
__proto__: GenericWorkspacesView.prototype,
|
|
|
|
_init: function(width, height, x, y, animate) {
|
|
GenericWorkspacesView.prototype._init.call(this, width, height, x, y, animate);
|
|
|
|
this.actor.style_class = "workspaces mosaic";
|
|
this._actor.set_clip(x - Workspace.FRAME_SIZE,
|
|
y - Workspace.FRAME_SIZE,
|
|
width + 2 * Workspace.FRAME_SIZE,
|
|
height + 2 * Workspace.FRAME_SIZE);
|
|
this._workspaces[global.screen.get_active_workspace_index()].setSelected(true);
|
|
},
|
|
|
|
// Assign grid positions to workspaces. We can't just do a simple
|
|
// row-major or column-major numbering, because we don't want the
|
|
// existing workspaces to get rearranged when we add a row or
|
|
// column. So we alternate between adding to rows and adding to
|
|
// columns. (So, eg, when going from a 2x2 grid of 4 workspaces to
|
|
// a 3x2 grid of 5 workspaces, the 4 existing workspaces stay
|
|
// where they are, and the 5th one is added to the end of the
|
|
// first row.)
|
|
//
|
|
// FIXME: need to make the metacity internal layout agree with this!
|
|
_positionWorkspaces: function() {
|
|
let gridWidth = Math.ceil(Math.sqrt(this._workspaces.length));
|
|
let gridHeight = Math.ceil(this._workspaces.length / gridWidth);
|
|
|
|
// adjust vertical spacing so workspaces can preserve their aspect
|
|
// ratio without exceeding this._height
|
|
let verticalSpacing = this._spacing * this._height / this._width;
|
|
|
|
let wsWidth = (this._width - (gridWidth - 1) * this._spacing) / gridWidth;
|
|
let wsHeight = (this._height - (gridHeight - 1) * verticalSpacing) / gridHeight;
|
|
let scale = wsWidth / global.screen_width;
|
|
|
|
let span = 1, n = 0, row = 0, col = 0, horiz = true;
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
let workspace = this._workspaces[w];
|
|
|
|
workspace.gridRow = row;
|
|
workspace.gridCol = col;
|
|
|
|
workspace.gridX = this._x + workspace.gridCol * (wsWidth + this._spacing);
|
|
workspace.gridY = this._y + workspace.gridRow * (wsHeight + verticalSpacing);
|
|
workspace.scale = scale;
|
|
|
|
if (horiz) {
|
|
col++;
|
|
if (col == span) {
|
|
row = 0;
|
|
horiz = false;
|
|
}
|
|
} else {
|
|
row++;
|
|
if (row == span) {
|
|
col = 0;
|
|
horiz = true;
|
|
span++;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_workspacesChanged: function() {
|
|
let oldNumWorkspaces = this._workspaces.length;
|
|
let newNumWorkspaces = global.screen.n_workspaces;
|
|
|
|
if (oldNumWorkspaces == newNumWorkspaces)
|
|
return;
|
|
|
|
let oldScale = this._workspaces[0].scale;
|
|
let oldGridWidth = Math.ceil(Math.sqrt(oldNumWorkspaces));
|
|
let oldGridHeight = Math.ceil(oldNumWorkspaces / oldGridWidth);
|
|
let lostWorkspaces = [];
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
// Create new workspace groups
|
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
this._addWorkspaceActor(w);
|
|
}
|
|
|
|
} else {
|
|
// Truncate the list of workspaces
|
|
// FIXME: assumes that the workspaces are being removed from
|
|
// the end of the list, not the start/middle
|
|
lostWorkspaces = this._workspaces.splice(newNumWorkspaces);
|
|
}
|
|
|
|
// Figure out the new layout
|
|
this._positionWorkspaces();
|
|
let newScale = this._workspaces[0].scale;
|
|
let newGridWidth = Math.ceil(Math.sqrt(newNumWorkspaces));
|
|
let newGridHeight = Math.ceil(newNumWorkspaces / newGridWidth);
|
|
|
|
if (newGridWidth != oldGridWidth || newGridHeight != oldGridHeight) {
|
|
// We need to resize/move the existing workspaces/windows
|
|
let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces);
|
|
for (let w = 0; w < existingWorkspaces; w++)
|
|
this._workspaces[w].resizeToGrid(oldScale);
|
|
}
|
|
|
|
if (newScale != oldScale) {
|
|
// The workspace scale affects window size/positioning because we clamp
|
|
// window size to a 1:1 ratio and never scale them up
|
|
let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces);
|
|
for (let w = 0; w < existingWorkspaces; w++)
|
|
this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
|
|
}
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
// Slide new workspaces in from offscreen
|
|
// New workspaces can contain windows.
|
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
this._workspaces[w].positionWindows(0);
|
|
this._workspaces[w].slideIn(oldScale);
|
|
}
|
|
} else {
|
|
// Slide old workspaces out
|
|
for (let w = 0; w < lostWorkspaces.length; w++) {
|
|
let workspace = lostWorkspaces[w];
|
|
workspace.slideOut(function () { workspace.destroy(); });
|
|
}
|
|
|
|
// FIXME: deal with windows on the lost workspaces
|
|
}
|
|
|
|
// Reset the selection state; if we went from > 1 workspace to 1,
|
|
// this has the side effect of removing the frame border
|
|
let activeIndex = global.screen.get_active_workspace_index();
|
|
this._workspaces[activeIndex].setSelected(true);
|
|
},
|
|
|
|
_activeWorkspaceChanged: function(wm, from, to, direction) {
|
|
this._workspaces[from].setSelected(false);
|
|
this._workspaces[to].setSelected(true);
|
|
},
|
|
|
|
_addWorkspaceActor: function(workspaceNum) {
|
|
let workspace = new Workspace.Workspace(workspaceNum, this._actor);
|
|
this._workspaces[workspaceNum] = workspace;
|
|
this._actor.add_actor(workspace.actor);
|
|
},
|
|
|
|
createControllerBar: function() {
|
|
return null;
|
|
},
|
|
|
|
addWorkspace: function() {
|
|
global.screen.append_new_workspace(false, global.get_current_time());
|
|
},
|
|
|
|
canRemoveWorkspace: function() {
|
|
return global.screen.n_workspaces > 1;
|
|
},
|
|
|
|
removeWorkspace: function() {
|
|
let last = this._workspaces.length - 1;
|
|
global.screen.remove_workspace(this._workspaces[last]._metaWorkspace,
|
|
global.get_current_time());
|
|
},
|
|
|
|
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
|
this._addNewWorkspace();
|
|
return this.acceptNewWorkspaceDrop(source, dropActor, x, y, time);
|
|
}
|
|
};
|
|
|
|
function NewWorkspaceArea() {
|
|
this._init();
|
|
}
|
|
|
|
NewWorkspaceArea.prototype = {
|
|
_init: function() {
|
|
let width = Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE);
|
|
this.actor = new Clutter.Group({ width: width,
|
|
height: global.screen_height,
|
|
x: global.screen_width });
|
|
|
|
this._child1 = new St.Bin({ style_class: 'new-workspace-area',
|
|
width: width,
|
|
height: global.screen_height });
|
|
this._child2 = new St.Bin({ style_class: 'new-workspace-area-internal',
|
|
width: width,
|
|
height: global.screen_height,
|
|
reactive: true });
|
|
this.actor.add_actor(this._child1);
|
|
this.actor.add_actor(this._child2);
|
|
},
|
|
|
|
setStyle: function(isHover) {
|
|
this._child1.set_style_pseudo_class(isHover ? 'hover' : null);
|
|
}
|
|
};
|
|
|
|
function SingleView(width, height, x, y, animate) {
|
|
this._init(width, height, x, y, animate);
|
|
}
|
|
|
|
SingleView.prototype = {
|
|
__proto__: GenericWorkspacesView.prototype,
|
|
|
|
_init: function(width, height, x, y, animate) {
|
|
this._newWorkspaceArea = new NewWorkspaceArea();
|
|
this._leftShadow = new St.Bin({ style_class: 'left-workspaces-shadow',
|
|
width: Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE),
|
|
height: global.screen_height,
|
|
x: global.screen_width })
|
|
this._rightShadow = new St.Bin({ style_class: 'right-workspaces-shadow',
|
|
width: Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE),
|
|
height: global.screen_height,
|
|
x: global.screen_width })
|
|
|
|
GenericWorkspacesView.prototype._init.call(this, width, height, x, y, animate);
|
|
|
|
this._actor.add_actor(this._newWorkspaceArea.actor);
|
|
this._actor.add_actor(this._leftShadow);
|
|
this._actor.add_actor(this._rightShadow);
|
|
|
|
this.actor.style_class = "workspaces single";
|
|
this._actor.set_clip(x, y, width, height);
|
|
this._indicatorsPanel = null;
|
|
this._indicatorsPanelWidth = 0;
|
|
this._scroll = null;
|
|
this._lostWorkspaces = [];
|
|
this._scrolling = false;
|
|
this._animatingScroll = false;
|
|
|
|
let primary = global.get_primary_monitor();
|
|
this._dropGroup = new Clutter.Group({ x: 0, y: 0,
|
|
width: primary.width,
|
|
height: primary.height });
|
|
this._dropGroup._delegate = this;
|
|
global.stage.add_actor(this._dropGroup);
|
|
this._dropGroup.lower_bottom();
|
|
this._timeoutId = 0;
|
|
},
|
|
|
|
_positionWorkspaces: function() {
|
|
let scale;
|
|
|
|
if (this._inDrag)
|
|
scale = this._width * WORKSPACE_DRAGGING_SCALE / global.screen_width;
|
|
else
|
|
scale = this._width / global.screen_width;
|
|
let active = global.screen.get_active_workspace_index();
|
|
let _width = this._workspaces[0].actor.width * scale;
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
let workspace = this._workspaces[w];
|
|
|
|
if (this._inDrag)
|
|
workspace.opacity = 200;
|
|
else
|
|
workspace.opacity = 255;
|
|
if (active == w)
|
|
workspace.opacity = 255;
|
|
|
|
workspace.gridRow = 0;
|
|
workspace.gridCol = 0;
|
|
|
|
workspace.scale = scale;
|
|
workspace.gridX = this._x + (this._width - _width) / 2 + (w - active) * (_width + this._spacing);
|
|
workspace.gridY = this._y + (this._height - workspace.actor.height * scale) / 2;
|
|
|
|
workspace.setSelected(false);
|
|
}
|
|
|
|
this._newWorkspaceArea.scale = scale;
|
|
this._newWorkspaceArea.gridX = this._x + (this._width - _width) / 2 + (this._workspaces.length - active) * (_width + this._spacing);
|
|
this._newWorkspaceArea.gridY = this._y + (this._height - this._newWorkspaceArea.actor.height * scale) / 2;
|
|
|
|
this._leftShadow.scale = scale;
|
|
this._leftShadow.gridX = this._x + (this._width - _width) / 2 - (this._leftShadow.width * scale + this._spacing);
|
|
this._leftShadow.gridY = this._y + (this._height - this._leftShadow.height * scale) / 2;
|
|
|
|
this._rightShadow.scale = scale;
|
|
this._rightShadow.gridX = this._x + (this._width - _width) / 2 + (_width + this._spacing);
|
|
this._rightShadow.gridY = this._y + (this._height - this._rightShadow.height * scale) / 2;
|
|
},
|
|
|
|
_scrollToActive: function(showAnimation) {
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
this._updateWorkspaceActors(showAnimation);
|
|
this._scrollScrollBarToIndex(active, showAnimation);
|
|
},
|
|
|
|
_updateWorkspaceActors: function(showAnimation) {
|
|
let active = global.screen.get_active_workspace_index();
|
|
|
|
this._positionWorkspaces();
|
|
|
|
let dx = this._workspaces[0].gridX - this._workspaces[0].actor.x;
|
|
|
|
for (let w = 0; w < this._workspaces.length; w++) {
|
|
let workspace = this._workspaces[w];
|
|
|
|
workspace.actor.show();
|
|
workspace.hideWindowsOverlays();
|
|
|
|
let i = w;
|
|
if (showAnimation) {
|
|
Tweener.addTween(workspace.actor,
|
|
{ x: workspace.gridX,
|
|
y: workspace.gridY,
|
|
scale_x: workspace.scale,
|
|
scale_y: workspace.scale,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
opacity: workspace.opacity,
|
|
transition: 'easeOutQuad',
|
|
onCompleteScope: this,
|
|
onComplete: function() {
|
|
if (i == active) {
|
|
if (!this._inDrag)
|
|
workspace.showWindowsOverlays();
|
|
} else
|
|
workspace.actor.visible = Math.abs(i - active) <= 1;
|
|
}});
|
|
} else {
|
|
workspace.actor.set_scale(workspace.scale, workspace.scale);
|
|
workspace.actor.set_position(workspace.gridX, workspace.gridY);
|
|
workspace.actor.opacity = workspace.opacity;
|
|
if (i == active) {
|
|
if (!this._inDrag)
|
|
workspace.showWindowsOverlays();
|
|
} else
|
|
workspace.actor.visible = Math.abs(i - active) <= 1;
|
|
}
|
|
workspace.positionWindows(0);
|
|
}
|
|
if (active)
|
|
this._leftShadow.show();
|
|
else
|
|
this._leftShadow.hide();
|
|
|
|
if (active == this._workspaces.length - 1)
|
|
this._rightShadow.hide();
|
|
else
|
|
this._rightShadow.show();
|
|
|
|
this._leftShadow.raise_top();
|
|
this._rightShadow.raise_top();
|
|
|
|
if (showAnimation) {
|
|
Tweener.addTween(this._newWorkspaceArea.actor,
|
|
{ x: this._newWorkspaceArea.gridX,
|
|
y: this._newWorkspaceArea.gridY,
|
|
scale_x: this._newWorkspaceArea.scale,
|
|
scale_y: this._newWorkspaceArea.scale,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
this._leftShadow.x = this._leftShadow.gridX;
|
|
Tweener.addTween(this._leftShadow,
|
|
{ y: this._leftShadow.gridY,
|
|
scale_x: this._leftShadow.scale,
|
|
scale_y: this._leftShadow.scale,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
this._rightShadow.x = this._rightShadow.gridX;
|
|
Tweener.addTween(this._rightShadow,
|
|
{ y: this._rightShadow.gridY,
|
|
scale_x: this._rightShadow.scale,
|
|
scale_y: this._rightShadow.scale,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
transition: 'easeOutQuad'
|
|
});
|
|
} else {
|
|
this._newWorkspaceArea.actor.set_scale(this._newWorkspaceArea.scale, this._newWorkspaceArea.scale);
|
|
this._newWorkspaceArea.actor.set_position(this._newWorkspaceArea.gridX, this._newWorkspaceArea.gridY);
|
|
|
|
this._leftShadow.set_scale(this._leftShadow.scale, this._leftShadow.scale);
|
|
this._leftShadow.set_position(this._leftShadow.gridX, this._leftShadow.gridY);
|
|
this._rightShadow.set_scale(this._rightShadow.scale, this._rightShadow.scale);
|
|
this._rightShadow.set_position(this._rightShadow.gridX, this._rightShadow.gridY);
|
|
}
|
|
|
|
for (let l = 0; l < this._lostWorkspaces.length; l++) {
|
|
let workspace = this._lostWorkspaces[l];
|
|
|
|
workspace.gridX += dx;
|
|
workspace.actor.show();
|
|
workspace._hideAllOverlays();
|
|
|
|
if (showAnimation) {
|
|
Tweener.addTween(workspace.actor,
|
|
{ x: workspace.gridX,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: Lang.bind(this, this._cleanWorkspaces)
|
|
});
|
|
} else {
|
|
this._cleanWorkspaces();
|
|
}
|
|
}
|
|
},
|
|
|
|
_cleanWorkspaces: function() {
|
|
if (this._lostWorkspaces.length == 0)
|
|
return;
|
|
|
|
for (let l = 0; l < this._lostWorkspaces.length; l++)
|
|
this._lostWorkspaces[l].destroy();
|
|
this._lostWorkspaces = [];
|
|
|
|
this._positionWorkspaces();
|
|
this._updateWorkspaceActors();
|
|
},
|
|
|
|
_scrollScrollBarToIndex: function(index, showAnimation) {
|
|
if (!this._scroll || this._scrolling)
|
|
return;
|
|
|
|
this._animatingScroll = true;
|
|
|
|
if (showAnimation) {
|
|
Tweener.addTween(this._scroll.adjustment, {
|
|
value: index,
|
|
time: WORKSPACE_SWITCH_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: Lang.bind(this,
|
|
function() {
|
|
this._animatingScroll = false;
|
|
})
|
|
});
|
|
} else {
|
|
this._scroll.adjustment.value = index;
|
|
this._animatingScroll = false;
|
|
}
|
|
},
|
|
|
|
_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;
|
|
|
|
if (this._scroll != null)
|
|
this._scroll.adjustment.upper = newNumWorkspaces;
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
// Create new workspace groups
|
|
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
this._addWorkspaceActor(w);
|
|
this._positionWorkspaces();
|
|
this._updateWorkspaceActors();
|
|
this._scrollScrollBarToIndex(active + 1, false);
|
|
|
|
} else {
|
|
let active = global.screen.get_active_workspace_index();
|
|
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
let removedIndex = active + 1;
|
|
this._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 < this._lostWorkspaces.length; l++)
|
|
this._lostWorkspaces[l]._desktop.actor.reactive = false;
|
|
|
|
// reassign workspaceNum and metaWorkspace, as lost workspaces
|
|
// have not necessarily been removed from the end
|
|
for (let i = removedIndex; i < this._workspaces.length; i++) {
|
|
let metaWorkspace = global.screen.get_workspace_by_index(i);
|
|
this._workspaces[i].workspaceNum = i;
|
|
this._workspaces[i]._metaWorkspace = metaWorkspace;
|
|
}
|
|
|
|
this._scrollScrollBarToIndex(active, false);
|
|
this._scrollToActive(true);
|
|
}
|
|
|
|
this._updatePanelVisibility();
|
|
},
|
|
|
|
_activeWorkspaceChanged: function(wm, from, to, direction) {
|
|
this._updatePanelVisibility();
|
|
|
|
if (this._scrolling)
|
|
return;
|
|
|
|
this._scrollToActive(true);
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
GenericWorkspacesView.prototype._onDestroy.call(this);
|
|
this._dropGroup.destroy();
|
|
if (this._timeoutId) {
|
|
Mainloop.source_remove(this._timeoutId);
|
|
this._timeoutId = 0;
|
|
}
|
|
},
|
|
|
|
acceptDrop: function(source, dropActor, x, y, time) {
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
let [dx, dy] = this._workspaces[i].actor.get_transformed_position();
|
|
let [dw, dh] = this._workspaces[i].actor.get_transformed_size();
|
|
|
|
if (x > dx && x < dx + dw && y > dy && y < dy + dh)
|
|
return this._workspaces[i].acceptDrop(source, dropActor, x, y, time);
|
|
}
|
|
|
|
let [dx, dy] = this._newWorkspaceArea.actor.get_transformed_position();
|
|
let [dw, dh] = this._newWorkspaceArea.actor.get_transformed_size();
|
|
if (x > dx && x < dx + dw && y > dy && y < dy + dh)
|
|
return this._acceptNewWorkspaceDrop(source, dropActor, x, y, time);
|
|
|
|
return false;
|
|
},
|
|
|
|
_onWindowDragBegin: function(w, actor) {
|
|
if (!this._scroll || this._scroll.adjustment.value - Math.round(this._scroll.adjustment.value) != 0)
|
|
return;
|
|
|
|
this._inDrag = true;
|
|
this._updateWorkspaceActors(true);
|
|
|
|
this._dropGroup.raise_top();
|
|
},
|
|
|
|
handleDragOver: function(self, actor, x, y) {
|
|
let onPanel = false;
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
if (x == 0 && activeWorkspaceIndex > 0 && this._dragOverLastX !== 0) {
|
|
this._workspaces[activeWorkspaceIndex - 1]._metaWorkspace.activate(global.get_current_time());
|
|
this._workspaces[activeWorkspaceIndex - 1].setReservedSlot(actor._delegate);
|
|
this._dragOverLastX = 0;
|
|
return;
|
|
}
|
|
if (x == global.screen_width - 1 && this._workspaces[activeWorkspaceIndex + 1] &&
|
|
this._dragOverLastX != global.screen_width - 1) {
|
|
this._workspaces[activeWorkspaceIndex + 1]._metaWorkspace.activate(global.get_current_time());
|
|
this._workspaces[activeWorkspaceIndex + 1].setReservedSlot(actor._delegate);
|
|
this._dragOverLastX = global.screen_width - 1;
|
|
return;
|
|
}
|
|
|
|
this._dragOverLastX = x;
|
|
|
|
let [dx, dy] = this._newWorkspaceArea.actor.get_transformed_position();
|
|
let [dw, dh] = this._newWorkspaceArea.actor.get_transformed_size();
|
|
this._newWorkspaceArea.setStyle(x > dx && x < dx + dw && y > dy && y < dy + dh);
|
|
|
|
[dx, dy] = this._leftShadow.get_transformed_position();
|
|
[dw, dh] = this._leftShadow.get_transformed_size();
|
|
if (this._workspaces[activeWorkspaceIndex - 1]) {
|
|
if (x > dx && x < dx + dw && y > dy && y < dy + dh) {
|
|
onPanel = -1;
|
|
this._workspaces[activeWorkspaceIndex - 1].actor.opacity = 255;
|
|
} else
|
|
this._workspaces[activeWorkspaceIndex - 1].actor.opacity = 200;
|
|
}
|
|
|
|
[dx, dy] = this._rightShadow.get_transformed_position();
|
|
[dw, dh] = this._rightShadow.get_transformed_size();
|
|
if (this._workspaces[activeWorkspaceIndex + 1]) {
|
|
if (x > dx && x < dx + dw && y > dy && y < dy + dh) {
|
|
onPanel = 1;
|
|
this._workspaces[activeWorkspaceIndex + 1].actor.opacity = 255;
|
|
} else
|
|
this._workspaces[activeWorkspaceIndex + 1].actor.opacity = 200;
|
|
}
|
|
if (onPanel) {
|
|
if (!this._timeoutId)
|
|
this._timeoutId = Mainloop.timeout_add_seconds (1, Lang.bind(this, function() {
|
|
let i = global.screen.get_active_workspace_index();
|
|
if (this._workspaces[i + onPanel]) {
|
|
this._workspaces[i + onPanel]._metaWorkspace.activate(global.get_current_time());
|
|
this._workspaces[i + onPanel].setReservedSlot(actor._delegate);
|
|
}
|
|
return true;
|
|
}));
|
|
} else {
|
|
if (this._timeoutId) {
|
|
Mainloop.source_remove(this._timeoutId);
|
|
this._timeoutId = 0;
|
|
}
|
|
}
|
|
},
|
|
|
|
_onWindowDragEnd: function(w, actor) {
|
|
if (this._timeoutId) {
|
|
Mainloop.source_remove(this._timeoutId);
|
|
this._timeoutId = 0;
|
|
}
|
|
this._dropGroup.lower_bottom();
|
|
actor.opacity = 255;
|
|
this._inDrag = false;
|
|
this._updateWorkspaceActors(true);
|
|
|
|
for (let i = 0; i < this._workspaces.length; i++)
|
|
this._workspaces[i].setReservedSlot(null);
|
|
},
|
|
|
|
_addWorkspaceActor: function(workspaceNum) {
|
|
let workspace = new Workspace.Workspace(workspaceNum, this._actor);
|
|
workspace.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
|
|
workspace.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
|
|
this._actor.add_actor(workspace.actor);
|
|
this._workspaces[workspaceNum] = workspace;
|
|
},
|
|
|
|
// handle changes to the scroll bar's adjustment:
|
|
// sync the workspaces' positions to the position of the scroll bar handle
|
|
// 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) {
|
|
let metaWorkspace = this._workspaces[current]._metaWorkspace;
|
|
|
|
if (!this._scrolling) {
|
|
// This here is a little tricky - we get here when StScrollBar
|
|
// animates paging; we switch the active workspace, but
|
|
// leave out any extra animation (just like we would do when
|
|
// the handle was dragged)
|
|
// If StScrollBar emitted scroll-start before and scroll-stop
|
|
// after the animation, this would not be necessary
|
|
this._scrolling = true;
|
|
metaWorkspace.activate(global.get_current_time());
|
|
this._scrolling = false;
|
|
} else {
|
|
metaWorkspace.activate(global.get_current_time());
|
|
}
|
|
}
|
|
|
|
let last = this._workspaces.length - 1;
|
|
let firstWorkspaceX = this._workspaces[0].actor.x;
|
|
let lastWorkspaceX = this._workspaces[last].actor.x;
|
|
let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
|
|
|
|
// The scrollbar is hidden when there is only one workspace, so
|
|
// adj.upper should at least be 2 - but better be safe than sorry
|
|
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();
|
|
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
|
|
this._workspaces[i].actor.x += dx;
|
|
}
|
|
|
|
if (!this._scrolling && active == adj.value) {
|
|
// Again, work around the paging in StScrollBar: simulate
|
|
// the effect of scroll-stop
|
|
this._scrolling = true;
|
|
this._scrollToActive(false);
|
|
this._scrolling = false;
|
|
}
|
|
},
|
|
|
|
// 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;
|
|
if (direction == Clutter.ScrollDirection.DOWN && current < last)
|
|
activate++;
|
|
else if (direction == Clutter.ScrollDirection.UP && current > 0)
|
|
activate--;
|
|
|
|
if (activate != current) {
|
|
let metaWorkspace = this._workspaces[activate]._metaWorkspace;
|
|
metaWorkspace.activate(global.get_current_time());
|
|
}
|
|
},
|
|
|
|
createControllerBar: function() {
|
|
let actor = new St.BoxLayout({ style_class: 'single-view-controls',
|
|
pack_start: true,
|
|
vertical: true });
|
|
|
|
let active = global.screen.get_active_workspace_index();
|
|
let adj = new St.Adjustment({ value: active,
|
|
lower: 0,
|
|
page_increment: 1,
|
|
page_size: 1,
|
|
step_increment: 0,
|
|
upper: this._workspaces.length });
|
|
this._scroll = new St.ScrollBar({ adjustment: adj,
|
|
vertical: false,
|
|
name: 'SwitchScroll' });
|
|
|
|
// we have set adj.step_increment to 0, so all scroll wheel events
|
|
// are processed with this handler - this allows us to animate the
|
|
// workspace switch
|
|
this._scroll.connect('scroll-event',
|
|
Lang.bind(this, this._onScrollEvent));
|
|
|
|
this._scroll.adjustment.connect('notify::value',
|
|
Lang.bind(this, this._onScroll));
|
|
|
|
|
|
this._scroll.connect('scroll-start', Lang.bind(this,
|
|
function() {
|
|
this._scrolling = true;
|
|
}));
|
|
this._scroll.connect('scroll-stop', Lang.bind(this,
|
|
function() {
|
|
this._scrolling = false;
|
|
this._scrollToActive(true);
|
|
}));
|
|
|
|
actor.add(this._createPositionalIndicator(), { expand: true,
|
|
x_fill: true,
|
|
y_fill: true });
|
|
actor.add(this._scroll, { expand: true,
|
|
x_fill: true,
|
|
y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
this._updatePanelVisibility();
|
|
|
|
return actor;
|
|
},
|
|
|
|
_addIndicatorClone: function(i, active) {
|
|
let actor = new St.Button({ style_class: 'workspace-indicator' });
|
|
if (active) {
|
|
actor.style_class = 'workspace-indicator active';
|
|
}
|
|
actor.connect('clicked', Lang.bind(this, function() {
|
|
if (this._workspaces[i] != undefined)
|
|
this._workspaces[i]._metaWorkspace.activate(global.get_current_time());
|
|
}));
|
|
|
|
actor._delegate = {};
|
|
actor._delegate.acceptDrop = Lang.bind(this, function(source, actor, x, y, time) {
|
|
if (this._workspaces[i].acceptDrop(source, actor, x, y, time)) {
|
|
this._workspaces[i]._metaWorkspace.activate(global.get_current_time());
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
});
|
|
|
|
actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
|
|
|
this._indicatorsPanel.add_actor(actor);
|
|
|
|
let [a, spacing] = actor.get_theme_node().get_length('border-spacing', false);
|
|
if (this._indicatorsPanelWidth < spacing * (i + 1) + actor.width * (i + 1))
|
|
actor.hide();
|
|
actor.x = spacing * i + actor.width * i;
|
|
},
|
|
|
|
_fillPositionalIndicator: function() {
|
|
if (this._indicatorsPanel == null || this._indicatorsPanelWidth == 0)
|
|
return;
|
|
let width = this._indicatorsPanelWidth;
|
|
this._indicatorsPanel.remove_all();
|
|
|
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
for (let i = 0; i < this._workspaces.length; i++) {
|
|
this._addIndicatorClone(i, i == activeWorkspaceIndex);
|
|
}
|
|
this._indicatorsPanel.x = (this._indicatorsPanelWidth - this._indicatorsPanel.width) / 2;
|
|
},
|
|
|
|
_createPositionalIndicator: function() {
|
|
let actor = new St.Bin({ style_class: 'panel-button' });
|
|
let group = new Clutter.Group();
|
|
|
|
this._indicatorsPanel = new Shell.GenericContainer();
|
|
this._indicatorsPanel.connect('get-preferred-width', Lang.bind(this, function (actor, fh, alloc) {
|
|
let children = actor.get_children();
|
|
let width = 0;
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (!children[i].visible)
|
|
continue;
|
|
if (children[i].x + children[i].width <= width)
|
|
continue;
|
|
width = children[i].x + children[i].width;
|
|
}
|
|
alloc.min_size = width;
|
|
alloc.natural_size = width;
|
|
}));
|
|
this._indicatorsPanel.connect('get-preferred-height', Lang.bind(this, function (actor, fw, alloc) {
|
|
let children = actor.get_children();
|
|
let height = 0;
|
|
if (children.length)
|
|
height = children[0].height;
|
|
alloc.min_size = height;
|
|
alloc.natural_size = height;
|
|
}));
|
|
this._indicatorsPanel.connect('allocate', Lang.bind(this, function (actor, box, flags) {
|
|
let children = actor.get_children();
|
|
for (let i = 0; i < children.length; i++) {
|
|
if (!children[i].visible)
|
|
continue;
|
|
let childBox = new Clutter.ActorBox();
|
|
childBox.x1 = children[i].x;
|
|
childBox.y1 = 0;
|
|
childBox.x2 = children[i].x + children[i].width;
|
|
childBox.y2 = children[i].height;
|
|
children[i].allocate(childBox, flags);
|
|
}
|
|
}));
|
|
|
|
group.add_actor(this._indicatorsPanel);
|
|
actor.set_child(group);
|
|
actor.set_alignment(St.Align.START, St.Align.START);
|
|
actor.set_fill(true, true);
|
|
this._indicatorsPanel.hide();
|
|
actor.connect('notify::width', Lang.bind(this, function(actor) {
|
|
this._indicatorsPanelWidth = actor.width;
|
|
this._updatePanelVisibility();
|
|
}));
|
|
actor.connect('destroy', Lang.bind(this, function() {
|
|
this._indicatorsPanel = null;
|
|
}));
|
|
return actor;
|
|
},
|
|
|
|
canRemoveWorkspace: function() {
|
|
return global.screen.get_active_workspace_index() != 0;
|
|
},
|
|
|
|
_updatePanelVisibility: function() {
|
|
let showSwitches = (global.screen.n_workspaces > 1);
|
|
if (this._scroll != null) {
|
|
if (showSwitches)
|
|
this._scroll.show();
|
|
else
|
|
this._scroll.hide();
|
|
}
|
|
if (this._indicatorsPanel != null) {
|
|
if (showSwitches)
|
|
this._indicatorsPanel.show();
|
|
else
|
|
this._indicatorsPanel.hide();
|
|
}
|
|
this._fillPositionalIndicator();
|
|
},
|
|
|
|
addWorkspace: function() {
|
|
let ws = global.screen.append_new_workspace(false,
|
|
global.get_current_time());
|
|
ws.activate(global.get_current_time());
|
|
},
|
|
|
|
removeWorkspace: function() {
|
|
let active = global.screen.get_active_workspace_index();
|
|
global.screen.remove_workspace(this._workspaces[active]._metaWorkspace,
|
|
global.get_current_time());
|
|
},
|
|
|
|
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
|
this.addWorkspace();
|
|
return this.acceptNewWorkspaceDrop(source, dropActor, x, y, time);
|
|
}
|
|
};
|
|
|
|
function WorkspacesControls() {
|
|
this._init();
|
|
}
|
|
|
|
WorkspacesControls.prototype = {
|
|
_init: function() {
|
|
this.actor = null;
|
|
this._gconf = Shell.GConf.get_default();
|
|
this._mosaicViewButton = null;
|
|
this._singleViewButton = null;
|
|
this._addButton = null;
|
|
this._removeButton = null;
|
|
|
|
let view = this._gconf.get_string(WORKSPACES_VIEW_KEY).toUpperCase();
|
|
if (view in WorkspacesViewType)
|
|
this._currentViewType = WorkspacesViewType[view];
|
|
else
|
|
this._currentViewType = WorkspacesViewType.SINGLE;
|
|
|
|
this._currentView = null;
|
|
this._nWorkspacesNotifyId = 0;
|
|
this._switchWorkspaceNotifyId = 0;
|
|
},
|
|
|
|
_setView: function(view) {
|
|
this._mosaicViewButton.set_checked(WorkspacesViewType.GRID == view);
|
|
this._singleViewButton.set_checked(WorkspacesViewType.SINGLE == view);
|
|
|
|
if (this._currentViewType == view)
|
|
return;
|
|
this._currentViewType = view;
|
|
this._gconf.set_string(WORKSPACES_VIEW_KEY, view);
|
|
this.emit('view-changed');
|
|
},
|
|
|
|
createCurrentWorkspaceView: function(width, height, x, y, animate) {
|
|
switch (this._currentViewType) {
|
|
case WorkspacesViewType.SINGLE:
|
|
this._currentView = new SingleView(width, height, x, y, animate);
|
|
break;
|
|
case WorkspacesViewType.GRID:
|
|
default:
|
|
this._currentView = new MosaicView(width, height, x, y, animate);
|
|
break;
|
|
}
|
|
this._updateControlsBar();
|
|
return this._currentView;
|
|
},
|
|
|
|
_updateControlsBar: function() {
|
|
if (this.actor)
|
|
this.actor.remove_all();
|
|
else
|
|
this.actor = new St.BoxLayout({ style_class: 'workspaces-bar' });
|
|
|
|
// View switcher buttons
|
|
this._mosaicViewButton = new St.Button({ style_class: 'workspace-controls switch-mosaic',
|
|
toggle_mode: true });
|
|
this._mosaicViewButton.connect('clicked', Lang.bind(this, function() {
|
|
this._setView(WorkspacesViewType.GRID);
|
|
}));
|
|
this.actor.add(this._mosaicViewButton, { y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
this._singleViewButton = new St.Button({ style_class: 'workspace-controls switch-single',
|
|
toggle_mode: true });
|
|
this._singleViewButton.connect('clicked', Lang.bind(this, function() {
|
|
this._setView(WorkspacesViewType.SINGLE);
|
|
}));
|
|
this.actor.add(this._singleViewButton, { y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
if (this._currentViewType == WorkspacesViewType.GRID)
|
|
this._mosaicViewButton.set_checked(true);
|
|
else
|
|
this._singleViewButton.set_checked(true);
|
|
|
|
// View specific controls
|
|
let bar = this._currentView.createControllerBar();
|
|
if (!bar)
|
|
bar = new St.Bin();
|
|
|
|
this.actor.add(bar, { expand: true,
|
|
x_fill: true,
|
|
y_fill: true,
|
|
y_align: St.Align.MIDDLE,
|
|
x_align: St.Align.START });
|
|
|
|
// Add/remove workspace buttons
|
|
this._removeButton = new St.Button({ style_class: 'workspace-controls remove' });
|
|
this._removeButton.connect('clicked', Lang.bind(this, function() {
|
|
this._currentView.removeWorkspace();
|
|
}));
|
|
this.actor.add(this._removeButton, { y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
this._addButton = new St.Button({ style_class: 'workspace-controls add' });
|
|
this._addButton.connect('clicked', Lang.bind(this, function() {
|
|
this._currentView.addWorkspace()
|
|
}));
|
|
this._addButton._delegate = this._addButton;
|
|
this._addButton._delegate.acceptDrop = Lang.bind(this,
|
|
function(source, actor, x, y, time) {
|
|
let view = this._currentView;
|
|
return view._acceptNewWorkspaceDrop(source, actor, x, y, time);
|
|
});
|
|
this.actor.add(this._addButton, { y_fill: false,
|
|
y_align: St.Align.START });
|
|
|
|
this._nWorkspacesNotifyId =
|
|
global.screen.connect('notify::n-workspaces',
|
|
Lang.bind(this, this._workspacesChanged));
|
|
|
|
this._switchWorkspaceNotifyId =
|
|
global.window_manager.connect('switch-workspace',
|
|
Lang.bind(this, this._updateButtonSensitivity));
|
|
|
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
|
|
this._workspacesChanged();
|
|
},
|
|
|
|
_onDestroy: function() {
|
|
this.actor = null;
|
|
if (this._nWorkspacesNotifyId > 0) {
|
|
global.screen.disconnect(this._nWorkspacesNotifyId);
|
|
this._nWorkspacesNotifyId = 0;
|
|
}
|
|
if (this._switchWorkspaceNotifyId > 0) {
|
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
|
this._switchWorkspaceNotifyId = 0;
|
|
}
|
|
},
|
|
|
|
_setButtonSensitivity: function(button, sensitive) {
|
|
if (button == null)
|
|
return;
|
|
button.reactive = sensitive;
|
|
button.opacity = sensitive ? 255 : 85;
|
|
},
|
|
|
|
_updateButtonSensitivity: function() {
|
|
this._setButtonSensitivity(this._addButton,
|
|
this._currentView.canAddWorkspace());
|
|
this._setButtonSensitivity(this._removeButton,
|
|
this._currentView.canRemoveWorkspace());
|
|
},
|
|
|
|
_workspacesChanged: function() {
|
|
if (this.actor == null)
|
|
return;
|
|
|
|
this._updateButtonSensitivity();
|
|
|
|
if (global.screen.n_workspaces == 1) {
|
|
this._mosaicViewButton.hide();
|
|
this._singleViewButton.hide();
|
|
} else {
|
|
this._mosaicViewButton.show();
|
|
this._singleViewButton.show();
|
|
}
|
|
}
|
|
};
|
|
|
|
Signals.addSignalMethods(WorkspacesControls.prototype);
|