[gnome-shell/workspace-thumbnails] First draft of "Thumbnails" workspace management
- From: Owen Taylor <otaylor src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/workspace-thumbnails] First draft of "Thumbnails" workspace management
- Date: Tue, 25 Jan 2011 17:10:24 +0000 (UTC)
commit 3a0c87d958a6089bfb09393ba370b775f57b269d
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Mon Jan 24 11:35:53 2011 -0500
First draft of "Thumbnails" workspace management
- Make workspace arrangement vertical
- Add thumbnails to workspace controls on right
- Make adding a new workspace not switch to it immediately
- Remove old workspace indicators
- Etc.
Requires Mutter patch in https://bugzilla.gnome.org/show_bug.cgi?id=640552
data/theme/gnome-shell.css | 13 +-
js/ui/main.js | 8 +
js/ui/windowManager.js | 41 +++-
js/ui/workspace.js | 2 +-
js/ui/workspaceThumbnail.js | 288 ++++++++++++++++++++
js/ui/workspacesView.js | 610 +++++++++++++++++++++----------------------
6 files changed, 643 insertions(+), 319 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index cf6f1eb..4ad9c31 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -275,7 +275,6 @@ StTooltip StLabel {
}
.workspace-controls {
- width: 48px;
font-size: 32px;
font-weight: bold;
color: #ffffff;
@@ -284,12 +283,20 @@ StTooltip StLabel {
border-radius: 9px 0px 0px 9px;
}
+.workspace-thumbnails {
+ spacing: 8px;
+ border: 8px transparent;
+}
+
+.workspace-thumbnail-indicator {
+ outline: 4px solid white;
+}
+
.add-workspace {
- background-color: rgba(128, 128, 128, 0.4);
}
.add-workspace:hover {
- background-color: rgba(128, 128, 128, 0.6);
+ background-color: rgba(128, 128, 128, 0.2);
}
.remove-workspace {
diff --git a/js/ui/main.js b/js/ui/main.js
index 1d456af..c8e87e1 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -172,6 +172,8 @@ function start() {
}
});
+ global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
+
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
@@ -346,6 +348,12 @@ function _globalKeyPressHandler(actor, event) {
case Meta.KeyBindingAction.WORKSPACE_RIGHT:
wm.actionMoveWorkspaceRight();
return true;
+ case Meta.KeyBindingAction.WORKSPACE_UP:
+ wm.actionMoveWorkspaceUp();
+ return true;
+ case Meta.KeyBindingAction.WORKSPACE_DOWN:
+ wm.actionMoveWorkspaceDown();
+ return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
getRunDialog().open();
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 447e479..a6c6ffc 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -525,23 +525,20 @@ WindowManager.prototype = {
},
_showWorkspaceSwitcher : function(shellwm, binding, window, backwards) {
- /* We don't support this kind of layout */
- if (binding == 'switch_to_workspace_up' || binding == 'switch_to_workspace_down')
- return;
-
if (global.screen.n_workspaces == 1)
return;
if (this._workspaceSwitcherPopup == null)
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
- if (binding == 'switch_to_workspace_left') {
+ if (binding == 'switch_to_workspace_left')
this.actionMoveWorkspaceLeft();
- }
-
- if (binding == 'switch_to_workspace_right') {
+ else if (binding == 'switch_to_workspace_right')
this.actionMoveWorkspaceRight();
- }
+ else if (binding == 'switch_to_workspace_up')
+ this.actionMoveWorkspaceUp();
+ else if (binding == 'switch_to_workspace_down')
+ this.actionMoveWorkspaceDown();
},
actionMoveWorkspaceLeft: function() {
@@ -574,5 +571,31 @@ WindowManager.prototype = {
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, indexToActivate);
+ },
+
+ actionMoveWorkspaceUp: function() {
+ let activeWorkspaceIndex = global.screen.get_active_workspace_index();
+ let indexToActivate = activeWorkspaceIndex;
+ if (activeWorkspaceIndex > 0)
+ indexToActivate--;
+
+ if (indexToActivate != activeWorkspaceIndex)
+ global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
+
+ if (!Main.overview.visible)
+ this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.LEFT, indexToActivate);
+ },
+
+ actionMoveWorkspaceDown: function() {
+ let activeWorkspaceIndex = global.screen.get_active_workspace_index();
+ let indexToActivate = activeWorkspaceIndex;
+ if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
+ indexToActivate++;
+
+ if (indexToActivate != activeWorkspaceIndex)
+ global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
+
+ if (!Main.overview.visible)
+ this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, indexToActivate);
}
};
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index e44f558..7d40716 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -1391,7 +1391,7 @@ Workspace.prototype = {
},
acceptDrop : function(source, actor, x, y, time) {
- if (source instanceof WindowClone) {
+ if (source.realWindow) {
let win = source.realWindow;
if (this._isMyWindow(win))
return false;
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
new file mode 100644
index 0000000..3588627
--- /dev/null
+++ b/js/ui/workspaceThumbnail.js
@@ -0,0 +1,288 @@
+/* -*- 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 Signals = imports.signals;
+const St = imports.gi.St;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const Workspace = imports.ui.workspace;
+
+function WindowClone(realWindow) {
+ this._init(realWindow);
+}
+
+WindowClone.prototype = {
+ _init : function(realWindow) {
+ this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
+ clip_to_allocation: true,
+ reactive: true,
+ x: realWindow.x,
+ y: realWindow.y });
+ this.actor._delegate = this;
+ this.realWindow = realWindow;
+ this.metaWindow = realWindow.meta_window;
+ this.metaWindow._delegate = this;
+
+ this.actor.connect('button-release-event',
+ Lang.bind(this, this._onButtonRelease));
+
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+ this._draggable = DND.makeDraggable(this.actor,
+ { restoreOnSuccess: true,
+ dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
+ dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY });
+ this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
+ this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
+ this.inDrag = false;
+
+ this._selected = false;
+ },
+
+ destroy: function () {
+ this.actor.destroy();
+ },
+
+ _onDestroy: function() {
+ this.metaWindow._delegate = null;
+ this.actor._delegate = null;
+
+ if (this.inDrag) {
+ this.emit('drag-end');
+ this.inDrag = false;
+ }
+
+ this.disconnectAll();
+ },
+
+ _onButtonRelease : function (actor, event) {
+ this._selected = true;
+ this.emit('selected', event.get_time());
+ },
+
+ _onDragBegin : function (draggable, time) {
+ this.inDrag = true;
+ this.emit('drag-begin');
+ },
+
+ _onDragEnd : function (draggable, time, snapback) {
+ this.inDrag = false;
+
+ // We may not have a parent if DnD completed successfully, in
+ // which case our clone will shortly be destroyed and replaced
+ // with a new one on the target workspace.
+ if (this.actor.get_parent() != null) {
+ if (this._stackAbove == null)
+ this.actor.lower_bottom();
+ else
+ this.actor.raise(this._stackAbove);
+ }
+
+
+ this.emit('drag-end');
+ }
+};
+Signals.addSignalMethods(WindowClone.prototype);
+
+
+/**
+ * @metaWorkspace: a #Meta.Workspace
+ */
+function WorkspaceThumbnail(metaWorkspace) {
+ this._init(metaWorkspace);
+}
+
+WorkspaceThumbnail.prototype = {
+ _init : function(metaWorkspace) {
+ // When dragging a window, we use this slot for reserve space.
+ this.metaWorkspace = metaWorkspace;
+
+ this.actor = new St.Bin({ reactive: true,
+ style_class: 'workspace-thumbnail' });
+ this.actor._delegate = this;
+
+ this._group = new Clutter.Group();
+ this.actor.add_actor(this._group);
+
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ this.actor.connect('button-press-event', Lang.bind(this,
+ function(actor, event) {
+ return true;
+ }));
+ this.actor.connect('button-release-event', Lang.bind(this,
+ function(actor, event) {
+ this.metaWorkspace.activate(event.get_time());
+ return true;
+ }));
+
+ this._background = new Clutter.Clone({ source: global.background_actor });
+ this._group.add_actor(this._background);
+
+ this._group.set_size(global.screen_width / 10., global.screen_height / 10.);
+ this._group.set_scale(1/10., 1/10.);
+
+ let windows = global.get_window_actors().filter(this._isMyWindow, this);
+
+ // Create clones for windows that should be visible in the Overview
+ this._windows = [];
+ for (let i = 0; i < windows.length; i++) {
+ if (this._isOverviewWindow(windows[i])) {
+ this._addWindowClone(windows[i]);
+ }
+ }
+
+ // Track window changes
+ this._windowAddedId = this.metaWorkspace.connect('window-added',
+ Lang.bind(this, this._windowAdded));
+ this._windowRemovedId = this.metaWorkspace.connect('window-removed',
+ Lang.bind(this, this._windowRemoved));
+ },
+
+ _lookupIndex: function (metaWindow) {
+ for (let i = 0; i < this._windows.length; i++) {
+ if (this._windows[i].metaWindow == metaWindow) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ syncStacking: function(stackIndices) {
+ let visibleClones = this._getVisibleClones();
+ visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
+
+ for (let i = 0; i < visibleClones.length; i++) {
+ let clone = visibleClones[i];
+ let metaWindow = clone.metaWindow;
+ if (i == 0) {
+ clone.setStackAbove(null);
+ } else {
+ let previousClone = visibleClones[i - 1];
+ clone.setStackAbove(previousClone.actor);
+ }
+ }
+ },
+
+ _windowRemoved : function(metaWorkspace, metaWin) {
+ let win = metaWin.get_compositor_private();
+
+ // find the position of the window in our list
+ let index = this._lookupIndex (metaWin);
+
+ if (index == -1)
+ return;
+
+ let clone = this._windows[index];
+ this._windows.splice(index, 1);
+ clone.destroy();
+ },
+
+ _windowAdded : function(metaWorkspace, metaWin) {
+ if (this.leavingOverview)
+ return;
+
+ let win = metaWin.get_compositor_private();
+
+ if (!win) {
+ // Newly-created windows are added to a workspace before
+ // the compositor finds out about them...
+ Mainloop.idle_add(Lang.bind(this,
+ function () {
+ if (this.actor && metaWin.get_compositor_private())
+ this._windowAdded(metaWorkspace, metaWin);
+ return false;
+ }));
+ return;
+ }
+
+ if (!this._isOverviewWindow(win))
+ return;
+
+ let clone = this._addWindowClone(win);
+ },
+
+ destroy : function() {
+ this.actor.destroy();
+ },
+
+ _onDestroy: function(actor) {
+ this.metaWorkspace.disconnect(this._windowAddedId);
+ this.metaWorkspace.disconnect(this._windowRemovedId);
+
+ this._windows = [];
+ this.actor = null;
+ },
+
+ // Tests if @win belongs to this workspaces
+ _isMyWindow : function (win) {
+ return win.get_workspace() == this.metaWorkspace.index() ||
+ (win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
+ },
+
+ // Tests if @win should be shown in the Overview
+ _isOverviewWindow : function (win) {
+ let tracker = Shell.WindowTracker.get_default();
+ return tracker.is_window_interesting(win.get_meta_window());
+ },
+
+ // Create a clone of a (non-desktop) window and add it to the window list
+ _addWindowClone : function(win) {
+ let clone = new WindowClone(win);
+
+ clone.connect('selected',
+ Lang.bind(this, this._onCloneSelected));
+ clone.connect('drag-begin',
+ Lang.bind(this, function(clone) {
+ Main.overview.beginWindowDrag();
+ }));
+ clone.connect('drag-end',
+ Lang.bind(this, function(clone) {
+ Main.overview.endWindowDrag();
+ }));
+ this._group.add_actor(clone.actor);
+
+ this._windows.push(clone);
+
+ return clone;
+ },
+
+ _onCloneSelected : function (clone, time) {
+ this.metaWorkspace.activate(time);
+ },
+
+ // Draggable target interface
+ handleDragOver : function(source, actor, x, y, time) {
+ if (source.realWindow)
+ return DND.DragMotionResult.MOVE_DROP;
+ if (source.shellWorkspaceLaunch)
+ return DND.DragMotionResult.COPY_DROP;
+
+ return DND.DragMotionResult.CONTINUE;
+ },
+
+ acceptDrop : function(source, actor, x, y, time) {
+ if (source.realWindow) {
+ let win = source.realWindow;
+ if (this._isMyWindow(win))
+ return false;
+
+ let metaWindow = win.get_meta_window();
+ metaWindow.change_workspace_by_index(this.metaWorkspace.index(),
+ false, // don't create workspace
+ time);
+ return true;
+ } else if (source.shellWorkspaceLaunch) {
+ this.metaWorkspace.activate(time);
+ source.shellWorkspaceLaunch();
+ return true;
+ }
+
+ return false;
+ }
+};
+
+Signals.addSignalMethods(WorkspaceThumbnail.prototype);
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 40b7320..9323116 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -14,29 +14,29 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
+const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const WORKSPACE_SWITCH_TIME = 0.25;
// Note that mutter has a compile-time limit of 36
const MAX_WORKSPACES = 16;
-const WORKSPACE_DRAGGING_SCALE = 0.85;
-
const CONTROLS_POP_IN_FRACTION = 0.8;
const CONTROLS_POP_IN_TIME = 0.1;
-const INDICATOR_HOVER_SCALE = 1.1;
-
-function WorkspacesView(width, height, x, y, workspaces) {
- this._init(width, height, x, y, workspaces);
+function WorkspacesView(width, height, x, y, zoomScale, workspaces) {
+ this._init(width, height, x, y, zoomScale, workspaces);
}
WorkspacesView.prototype = {
- _init: function(width, height, x, y, workspaces) {
+ _init: function(width, height, x, y, zoomScale, workspaces) {
this.actor = new St.Group({ style_class: 'workspaces-view' });
this.actor.set_clip(x, y, width, height);
+ // The actor itself isn't a drop target, so we don't want to pick on its aea
+ this.actor.set_size(0, 0);
+
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('style-changed', Lang.bind(this,
@@ -52,6 +52,7 @@ WorkspacesView.prototype = {
this._height = height;
this._x = x;
this._y = y;
+ this._zoomScale = zoomScale;
this._spacing = 0;
this._activeWorkspaceX = 0; // x offset of active ws while dragging
this._activeWorkspaceY = 0; // y offset of active ws while dragging
@@ -59,6 +60,7 @@ WorkspacesView.prototype = {
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
this._animatingScroll = false; // programatically updating the adjustment
+ this._zoomOut = false; // zoom to a larger area
this._inDrag = false; // dragging a window
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
@@ -244,7 +246,6 @@ WorkspacesView.prototype = {
} else {
let currentTime = global.get_current_time();
ws = global.screen.append_new_workspace(false, currentTime);
- ws.activate(currentTime);
}
return ws;
@@ -265,8 +266,26 @@ WorkspacesView.prototype = {
global.get_current_time());
},
+ zoomOut: function() {
+ if (this._zoomOut)
+ return;
+
+ this._zoomOut = true;
+ this._computeWorkspacePositions();
+ this._updateWorkspaceActors(true);
+ },
+
+ zoomIn: function() {
+ if (!this._zoomOut)
+ return;
+
+ this._zoomOut = false;
+ this._computeWorkspacePositions();
+ this._updateWorkspaceActors(true);
+ },
+
_handleDragOverNewWorkspace: function(source, dropActor, x, y, time) {
- if (source instanceof Workspace.WindowClone)
+ if (source.realWindow)
return DND.DragMotionResult.MOVE_DROP;
if (source.shellWorkspaceLaunch)
return DND.DragMotionResult.COPY_DROP;
@@ -286,8 +305,8 @@ WorkspacesView.prototype = {
let active = global.screen.get_active_workspace_index();
let scale = this._width / global.screen_width;
- if (this._inDrag)
- scale *= WORKSPACE_DRAGGING_SCALE;
+ if (this._zoomOut)
+ scale *= this._zoomScale;
let _width = this._workspaces[0].actor.width * scale;
let _height = this._workspaces[0].actor.height * scale;
@@ -301,14 +320,19 @@ WorkspacesView.prototype = {
workspace.opacity = (this._inDrag && w != active) ? 200 : 255;
workspace.scale = scale;
- 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);
- }
- workspace.y = this._y + this._activeWorkspaceY;
+ workspace.x = this._x + this._activeWorkspaceX;
+
+ // We adjust the center because the zoomScale is to leave space for
+ // the expanded workspace control so we want to zoom to either the
+ // left part of the area or the right part of the area
+ let offset = 0.5 * (1 - this._zoomScale) * this._width;
+ let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
+ if (this._zoomOut)
+ workspace.x += rtl ? offset : - offset;
+
+ workspace.y = this._y + this._activeWorkspaceY
+ + (w - active) * (_height + this._spacing);
+
}
},
@@ -325,9 +349,9 @@ WorkspacesView.prototype = {
// @showAnimation: iff %true, transition between states
_updateWorkspaceActors: function(showAnimation) {
let active = global.screen.get_active_workspace_index();
- let targetWorkspaceNewX = this._x + this._activeWorkspaceX;
- let targetWorkspaceCurrentX = this._workspaces[active].x;
- let dx = targetWorkspaceNewX - targetWorkspaceCurrentX;
+ let targetWorkspaceNewY = this._y + this._activeWorkspaceY;
+ let targetWorkspaceCurrentY = this._workspaces[active].y;
+ let dy = targetWorkspaceNewY - targetWorkspaceCurrentY;
this._animating = showAnimation;
@@ -336,7 +360,7 @@ WorkspacesView.prototype = {
Tweener.removeTweens(workspace.actor);
- workspace.x += dx;
+ workspace.y += dy;
if (showAnimation) {
let params = { x: workspace.x,
@@ -373,13 +397,13 @@ WorkspacesView.prototype = {
Tweener.removeTweens(workspace.actor);
- workspace.x += dx;
+ workspace.y += dy;
workspace.actor.show();
workspace.hideWindowsOverlays();
if (showAnimation) {
Tweener.addTween(workspace.actor,
- { x: workspace.x,
+ { y: workspace.x,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
@@ -504,7 +528,7 @@ WorkspacesView.prototype = {
_onMappedChanged: function() {
if (this.actor.mapped) {
- let direction = Overview.SwipeScrollDirection.HORIZONTAL;
+ let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(this._scrollAdjustment,
direction);
this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
@@ -522,8 +546,6 @@ WorkspacesView.prototype = {
return;
this._inDrag = true;
- this._computeWorkspacePositions();
- this._updateWorkspaceActors(true);
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
@@ -538,56 +560,51 @@ WorkspacesView.prototype = {
let primary = global.get_primary_monitor();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
- 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];
- }
+ let topWorkspace, bottomWorkspace;
+ topWorkspace = this._workspaces[activeWorkspaceIndex - 1];
+ bottomWorkspace = this._workspaces[activeWorkspaceIndex + 1];
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;
+ let topEdge = primary.y;
+ let switchTop = (dragEvent.y <= topEdge && topWorkspace);
+ if (switchTop && this._dragOverLastY != topEdge) {
+ topWorkspace.metaWorkspace.activate(global.get_current_time());
+ topWorkspace.setReservedSlot(dragEvent.dragActor._delegate);
+ this._dragOverLastY = topEdge;
return DND.DragMotionResult.CONTINUE;
}
- 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;
+ let bottomEdge = primary.y + primary.height - 1;
+ let switchBottom = (dragEvent.y >= bottomEdge && bottomWorkspace);
+ if (switchBottom && this._dragOverLastY != bottomEdge) {
+ bottomWorkspace.metaWorkspace.activate(global.get_current_time());
+ bottomWorkspace.setReservedSlot(dragEvent.dragActor._delegate);
+ this._dragOverLastY = bottomEdge;
return DND.DragMotionResult.CONTINUE;
}
- this._dragOverLastX = dragEvent.x;
+ this._dragOverLastY = dragEvent.y;
let result = DND.DragMotionResult.CONTINUE;
// check hover state of new workspace area / inactive workspaces
- if (leftWorkspace) {
- if (leftWorkspace.actor.contains(dragEvent.targetActor)) {
- hoverWorkspace = leftWorkspace;
- leftWorkspace.opacity = leftWorkspace.actor.opacity = 255;
- result = leftWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
+ if (topWorkspace) {
+ if (topWorkspace.actor.contains(dragEvent.targetActor)) {
+ hoverWorkspace = topWorkspace;
+ topWorkspace.opacity = topWorkspace.actor.opacity = 255;
+ result = topWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
} else {
- leftWorkspace.opacity = leftWorkspace.actor.opacity = 200;
+ topWorkspace.opacity = topWorkspace.actor.opacity = 200;
}
}
- if (rightWorkspace) {
- if (rightWorkspace.actor.contains(dragEvent.targetActor)) {
- hoverWorkspace = rightWorkspace;
- rightWorkspace.opacity = rightWorkspace.actor.opacity = 255;
- result = rightWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
+ if (bottomWorkspace) {
+ if (bottomWorkspace.actor.contains(dragEvent.targetActor)) {
+ hoverWorkspace = bottomWorkspace;
+ bottomWorkspace.opacity = bottomWorkspace.actor.opacity = 255;
+ result = bottomWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor);
} else {
- rightWorkspace.opacity = rightWorkspace.actor.opacity = 200;
+ bottomWorkspace.opacity = bottomWorkspace.actor.opacity = 200;
}
}
@@ -617,8 +634,6 @@ WorkspacesView.prototype = {
}
DND.removeMonitor(this._dragMonitor);
this._inDrag = false;
- this._computeWorkspacePositions();
- this._updateWorkspaceActors(true);
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(null);
@@ -670,22 +685,22 @@ WorkspacesView.prototype = {
}
let last = this._workspaces.length - 1;
- let firstWorkspaceX = this._workspaces[0].actor.x;
- let lastWorkspaceX = this._workspaces[last].actor.x;
- let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
+ let firstWorkspaceY = this._workspaces[0].actor.y;
+ let lastWorkspaceY = this._workspaces[last].actor.y;
+ let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
if (adj.upper == 1)
return;
- let currentX = firstWorkspaceX;
- let newX = this._x - adj.value / (adj.upper - 1) * workspacesWidth;
+ let currentY = firstWorkspaceY;
+ let newY = this._y - adj.value / (adj.upper - 1) * workspacesHeight;
- let dx = newX - currentX;
+ let dy = newY - currentY;
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;
+ this._workspaces[i].actor.y += dy;
}
},
@@ -696,155 +711,6 @@ WorkspacesView.prototype = {
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 the box centered to the available area like StBin would do,
- // except that the full height is used even if the box is not actually
- // shown. This is a workaround, as the size of the workspace area is
- // determined once when entering the overview, so if it would take up
- // the indicator space in that case, it would overlap it later when
- // additional workspaces were added.
- _allocate: function(actor, box, flags) {
- let children = this._box.get_children();
-
- let availWidth = box.x2 - box.x1;
- let availHeight = box.y2 - box.y1;
- let [minWidth, natWidth] = this._box.get_preferred_width(-1);
- let [minHeight, natHeight] = this._box.get_preferred_height(-1);
-
- let childBox = new Clutter.ActorBox();
- childBox.x1 = Math.floor((availWidth - natWidth) / 2);
- childBox.x2 = childBox.x1 + natWidth;
- childBox.y1 = Math.floor((availHeight - natHeight) / 2);
- childBox.y2 = childBox.y2 + natHeight;
-
- 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);
- alloc.min_size = minHeight * INDICATOR_HOVER_SCALE;
- alloc.natural_size = natHeight * INDICATOR_HOVER_SCALE;
- },
-
- updateWorkspaces: function(workspaces) {
- this._workspaces = workspaces;
-
- // Do not display a single indicator
- if (this._workspaces.length == 1)
- this.actor.set_skip_paint(this._box, true);
- else
- this.actor.set_skip_paint(this._box, false);
-
- this._box.remove_all();
- for (let i = 0; i < this._workspaces.length; i++) {
- let actor = new St.Button({ style_class: 'workspace-indicator',
- track_hover: true });
- let workspace = this._workspaces[i];
- let metaWorkspace = this._workspaces[i].metaWorkspace;
-
- actor.connect('clicked', Lang.bind(this, function() {
- metaWorkspace.activate(global.get_current_time());
- }));
- 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);
- }));
-
- 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');
- }
- },
-
- // 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;
-
- 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;
-
- if (activate != current) {
- let metaWorkspace = this._workspaces[activate].metaWorkspace;
- metaWorkspace.activate(global.get_current_time());
- }
- }
-};
-
-
function WorkspaceControlsContainer(controls) {
this._init(controls);
}
@@ -861,50 +727,13 @@ WorkspaceControlsContainer.prototype = {
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));
-
- this._itemDragBeginId = 0;
- this._itemDragEndId = 0;
- this._windowDragBeginId = 0;
- this._windowDragEndId = 0;
},
show: function() {
- if (this._itemDragBeginId == 0)
- this._itemDragBeginId = Main.overview.connect('item-drag-begin',
- Lang.bind(this, this.popOut));
- if (this._itemDragEndId == 0)
- this._itemDragEndId = Main.overview.connect('item-drag-end',
- Lang.bind(this, this.popIn));
- if (this._windowDragBeginId == 0)
- this._windowDragBeginId = Main.overview.connect('window-drag-begin',
- Lang.bind(this, this.popOut));
- if (this._windowDragEndId == 0)
- this._windowDragEndId = Main.overview.connect('window-drag-end',
- Lang.bind(this, this.popIn));
this._controls.x = this._poppedInX();
},
hide: function() {
- if (this._itemDragBeginId > 0) {
- Main.overview.disconnect(this._itemDragBeginId);
- this._itemDragBeginId = 0;
- }
- if (this._itemEndBeginId > 0) {
- Main.overview.disconnect(this._itemDragEndId);
- this._itemDragEndId = 0;
- }
- if (this._windowDragBeginId > 0) {
- Main.overview.disconnect(this._windowDragBeginId);
- this._windowDragBeginId = 0;
- }
- if (this._windowDragEndId > 0) {
- Main.overview.disconnect(this._windowDragEndId);
- this._windowDragEndId = 0;
- }
},
_getPreferredWidth: function(actor, forHeight, alloc) {
@@ -935,13 +764,6 @@ WorkspaceControlsContainer.prototype = {
this._controls.allocate(childBox, flags);
},
- _onHoverChanged: function() {
- if (this._controls.hover)
- this.popOut();
- else
- this.popIn();
- },
-
_poppedInX: function() {
let x = CONTROLS_POP_IN_FRACTION * this._controls.width;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
@@ -956,12 +778,6 @@ WorkspaceControlsContainer.prototype = {
transition: 'easeOutQuad' });
},
- popIn: function() {
- Tweener.addTween(this._controls,
- { x: this._poppedInX(),
- time: CONTROLS_POP_IN_TIME,
- transition: 'easeOutQuad' });
- }
};
function WorkspacesDisplay() {
@@ -970,22 +786,18 @@ function WorkspacesDisplay() {
WorkspacesDisplay.prototype = {
_init: function() {
- this.actor = new St.BoxLayout();
-
- let workspacesBox = new St.BoxLayout({ vertical: true });
- this.actor.add(workspacesBox, { expand: true });
-
- // placeholder for window previews
- this._workspacesBin = new St.Bin();
- workspacesBox.add(this._workspacesBin, { expand: true });
-
- this._workspaceIndicatorPanel = new WorkspaceIndicatorPanel();
- workspacesBox.add(this._workspaceIndicatorPanel.actor);
+ this.actor = new St.Group();
+ this.actor.set_size(0, 0);
let controls = new St.BoxLayout({ vertical: true,
style_class: 'workspace-controls' });
- this._controlsContainer = new WorkspaceControlsContainer(controls);
- this.actor.add(this._controlsContainer.actor);
+ this._controls = controls;
+ this.actor.add_actor(controls);
+
+ controls.reactive = true;
+ controls.track_hover = true;
+ controls.connect('notify::hover',
+ Lang.bind(this, this._onControlsHoverChanged));
// Add/remove workspace buttons
this._removeButton = new St.Button({ label: '–', // n-dash
@@ -995,6 +807,27 @@ WorkspacesDisplay.prototype = {
}));
controls.add(this._removeButton);
+ this._thumbnailsBox = new St.BoxLayout({ vertical: true,
+ style_class: 'workspace-thumbnails' });
+ controls.add(this._thumbnailsBox, { expand: false });
+
+ let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator',
+ fixed_position_set: true });
+
+ // We don't want the indicator to affect drag-and-drop
+ Shell.util_set_hidden_from_pick(indicator, true);
+
+ this._thumbnailIndicator = indicator;
+ this._thumbnailsBox.add(this._thumbnailIndicator);
+ this._thumbnailIndicatorConstraints = [];
+ this._thumbnailIndicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.X }));
+ this._thumbnailIndicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.Y }));
+ this._thumbnailIndicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.WIDTH }));
+ this._thumbnailIndicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.HEIGHT }));
+ this._thumbnailIndicatorConstraints.forEach(function(constraint) {
+ indicator.add_constraint(constraint);
+ });
+
this._addButton = new St.Button({ label: '+',
style_class: 'add-workspace' });
this._addButton.connect('clicked', Lang.bind(this, function() {
@@ -1012,70 +845,181 @@ WorkspacesDisplay.prototype = {
controls.add(this._addButton, { expand: true });
this.workspacesView = null;
+
+ this._inDrag = false;
+ this._zoomOut = false;
+
this._nWorkspacesNotifyId = 0;
+ this._switchWorkspaceNotifyId = 0;
+
+ this._itemDragBeginId = 0;
+ this._itemDragEndId = 0;
+ this._windowDragBeginId = 0;
+ this._windowDragEndId = 0;
},
show: function() {
- this._controlsContainer.show();
+ this._controls.show();
this._workspaces = [];
+ this._workspaceThumbnails = [];
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);
+
+ let thumbnail = new WorkspaceThumbnail.WorkspaceThumbnail(metaWorkspace);
+ this._workspaceThumbnails[i] = thumbnail;
+ this._thumbnailsBox.add(thumbnail.actor);
}
- this._nWorkspacesNotifyId =
- global.screen.connect('notify::n-workspaces',
- Lang.bind(this, this._workspacesChanged));
+ // The thumbnails indicator actually needs to be on top of the thumbnails, but
+ // there is also something more subtle going on as well - actors in a StBoxLayout
+ // are allocated from bottom to to top (start to end), and we need the
+ // thumnail indicator to be allocated after the actors it is constrained to.
+ this._thumbnailIndicator.raise_top();
- let binAllocation = this._workspacesBin.allocation;
- let binWidth = binAllocation.x2 - binAllocation.x1;
- let binHeight = binAllocation.y2 - binAllocation.y1;
+ let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
+
+ let totalAllocation = this.actor.allocation;
+ let totalWidth = totalAllocation.x2 - totalAllocation.x1;
+ // XXXX: 50 is just a hack for message tray compensation
+ let totalHeight = totalAllocation.y2 - totalAllocation.y1 - 50;
+
+ let [controlsMin, controlsNatural] = this._controls.get_preferred_width(-1);
+ let controlsReserved = controlsNatural * (1 - CONTROLS_POP_IN_FRACTION);
+
+ totalWidth -= controlsReserved;
// Workspaces expect to have the same ratio as the screen, so take
- // this into account when fitting the workspace into the bin
+ // this into account when fitting the workspace into the available space
let width, height;
- let binRatio = binWidth / binHeight;
+ let totalRatio = totalWidth / totalHeight;
let wsRatio = global.screen_width / global.screen_height;
- if (wsRatio > binRatio) {
- width = binWidth;
- height = Math.floor(binWidth / wsRatio);
+ if (wsRatio > totalRatio) {
+ width = totalWidth;
+ height = Math.floor(totalWidth / wsRatio);
} else {
- width = Math.floor(binHeight * wsRatio);
- height = binHeight;
+ width = Math.floor(totalHeight * wsRatio);
+ height = totalHeight;
}
- // 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);
+ // Position workspaces in the available space
+ let [x, y] = this.actor.get_transformed_position();
+ x = Math.floor(x + Math.abs(totalWidth - width) / 2);
+ y = Math.floor(y + Math.abs(totalHeight - height) / 2);
+
+ if (rtl)
+ x += controlsReserved;
- let newView = new WorkspacesView(width, height, x, y, this._workspaces);
+ this._controls.x = this._getControlsX();
+ this._controls.height = totalHeight;
+
+ let zoomScale = (totalWidth - controlsNatural) / totalWidth;
+ let newView = new WorkspacesView(width, height, x, y, zoomScale, this._workspaces);
if (this.workspacesView)
this.workspacesView.destroy();
this.workspacesView = newView;
- this._workspaceIndicatorPanel.updateWorkspaces(this._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));
+
+ if (this._itemDragBeginId == 0)
+ this._itemDragBeginId = Main.overview.connect('item-drag-begin',
+ Lang.bind(this, this._dragBegin));
+ if (this._itemDragEndId == 0)
+ this._itemDragEndId = Main.overview.connect('item-drag-end',
+ Lang.bind(this, this._dragEnd));
+ if (this._windowDragBeginId == 0)
+ this._windowDragBeginId = Main.overview.connect('window-drag-begin',
+ Lang.bind(this, this._dragBegin));
+ if (this._windowDragEndId == 0)
+ this._windowDragEndId = Main.overview.connect('window-drag-end',
+ Lang.bind(this, this._dragEnd));
+
+ this._constrainThumbnailIndicator();
},
hide: function() {
- this._controlsContainer.hide();
+ this._controls.hide();
- if (this._nWorkspacesNotifyId > 0)
+ 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;
+ }
+
+ if (this._itemDragBeginId > 0) {
+ Main.overview.disconnect(this._itemDragBeginId);
+ this._itemDragBeginId = 0;
+ }
+ if (this._itemEndBeginId > 0) {
+ Main.overview.disconnect(this._itemDragEndId);
+ this._itemDragEndId = 0;
+ }
+ if (this._windowDragBeginId > 0) {
+ Main.overview.disconnect(this._windowDragBeginId);
+ this._windowDragBeginId = 0;
+ }
+ if (this._windowDragEndId > 0) {
+ Main.overview.disconnect(this._windowDragEndId);
+ this._windowDragEndId = 0;
+ }
+
this.workspacesView.destroy();
this.workspacesView = null;
+ this._unconstrainThumbnailIndicator();
for (let w = 0; w < this._workspaces.length; w++) {
this._workspaces[w].disconnectAll();
this._workspaces[w].destroy();
+ this._workspaceThumbnails[w].destroy();
}
},
+ _constrainThumbnailIndicator: function() {
+ let active = global.screen.get_active_workspace_index();
+ let thumbnail = this._workspaceThumbnails[active];
+
+ this._thumbnailIndicatorConstraints.forEach(function(constraint) {
+ constraint.set_source(thumbnail.actor);
+ constraint.set_enabled(true);
+ });
+ },
+
+ _unconstrainThumbnailIndicator: function() {
+ this._thumbnailIndicatorConstraints.forEach(function(constraint) {
+ constraint.set_enabled(false);
+ });
+ },
+
+ _activeWorkspaceChanged: function(wm, from, to, direction) {
+ let active = global.screen.get_active_workspace_index();
+ let thumbnail = this._workspaceThumbnails[active];
+
+ this._unconstrainThumbnailIndicator();
+ let oldAllocation = this._thumbnailIndicator.allocation;
+ this._thumbnailIndicator.x = oldAllocation.x1;
+ this._thumbnailIndicator.y = oldAllocation.y1;
+ this._thumbnailIndicator.width = oldAllocation.x2 - oldAllocation.x1;
+ this._thumbnailIndicator.height = oldAllocation.y2 - oldAllocation.y1;
+
+ Tweener.addTween(this._thumbnailIndicator,
+ { x: thumbnail.actor.allocation.x1,
+ y: thumbnail.actor.allocation.y1,
+ time: WORKSPACE_SWITCH_TIME,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this,
+ this._constrainThumbnailIndicator)
+ });
+ },
+
_workspacesChanged: function() {
let oldNumWorkspaces = this._workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
@@ -1090,7 +1034,12 @@ WorkspacesDisplay.prototype = {
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
this._workspaces[w] = new Workspace.Workspace(metaWorkspace);
+
+ let thumbnail = new WorkspaceThumbnail.WorkspaceThumbnail(metaWorkspace);
+ this._workspaceThumbnails[w] = thumbnail;
+ this._thumbnailsBox.add(thumbnail.actor);
}
+ this._thumbnailIndicator.raise_top();
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
@@ -1111,12 +1060,61 @@ WorkspacesDisplay.prototype = {
// making its exit.
for (let l = 0; l < lostWorkspaces.length; l++)
lostWorkspaces[l].setReactive(false);
+
+ for (let k = removedIndex; k < removedIndex + removedNum; k++)
+ this._workspaceThumbnails[k].destroy();
+ this._workspaceThumbnails.splice(removedIndex, removedNum);
}
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
newNumWorkspaces,
lostWorkspaces);
- this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces);
+ },
+
+ _getControlsX: function() {
+ let totalAllocation = this.actor.allocation;
+ let totalWidth = totalAllocation.x2 - totalAllocation.x1;
+ let [controlsMin, controlsNatural] = this._controls.get_preferred_width(-1);
+ let controlsReserved = controlsNatural * (1 - CONTROLS_POP_IN_FRACTION);
+
+ let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
+ let width = this._zoomOut ? controlsNatural : controlsReserved;
+ if (rtl)
+ return width;
+ else
+ return totalWidth - width;
+ },
+
+ _updateZoom : function() {
+ let shouldZoom = this._controls.hover || this._inDrag;
+ if (shouldZoom != this._zoomOut) {
+ this._zoomOut = shouldZoom;
+
+ Tweener.addTween(this._controls,
+ { x: this._getControlsX(),
+// time: CONTROLS_POP_IN_TIME,
+ time: WORKSPACE_SWITCH_TIME,
+ transition: 'easeOutQuad' });
+
+ if (shouldZoom)
+ this.workspacesView.zoomOut();
+ else
+ this.workspacesView.zoomIn();
+ }
+ },
+
+ _onControlsHoverChanged: function() {
+ this._updateZoom();
+ },
+
+ _dragBegin: function() {
+ this._inDrag = true;
+ this._updateZoom();
+ },
+
+ _dragEnd: function() {
+ this._inDrag = false;
+ this._updateZoom();
}
};
Signals.addSignalMethods(WorkspacesDisplay.prototype);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]