[gnome-shell] [Workspaces] Clean up scrolling in linear view
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] [Workspaces] Clean up scrolling in linear view
- Date: Wed, 17 Feb 2010 22:41:33 +0000 (UTC)
commit 8ef75524ea6fb3499892699be9fea22be78f9691
Author: Florian Müllner <fmuellner src gnome org>
Date: Wed Feb 10 06:56:36 2010 +0100
[Workspaces] Clean up scrolling in linear view
Reorganize the code to break up positioning into:
1) updating workspace object's scale and position
2) applying the updated parameters to the workspace actor
3) scrolling the view to a particular workspace
4) handling dragging of the scroll bar
With these cleanups, it becomes much easier to fix
the following issues:
- use animations consistantly instead of doing hard breaks
for some actions and smooth transitions for others
- snap to the closest workspace when scrolling stops
(https://bugzilla.gnome.org/show_bug.cgi?id=607823)
- fix the regression of the zoomFromOverlay animation when
the selected app is on another workspace
(https://bugzilla.gnome.org/show_bug.cgi?id=609081)
https://bugzilla.gnome.org/show_bug.cgi?id=609673
js/ui/workspacesView.js | 339 ++++++++++++++++++++++++++++-------------------
1 files changed, 205 insertions(+), 134 deletions(-)
---
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index c78d1d4..45fabeb 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -329,8 +329,6 @@ MosaicView.prototype = {
let oldGridHeight = Math.ceil(oldNumWorkspaces / oldGridWidth);
let lostWorkspaces = [];
- // The old last workspace is no longer removable.
-
if (newNumWorkspaces > oldNumWorkspaces) {
// Create new workspace groups
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
@@ -344,9 +342,6 @@ MosaicView.prototype = {
lostWorkspaces = this._workspaces.splice(newNumWorkspaces);
}
- // The new last workspace may be removable
- let newLastWorkspace = this._workspaces[this._workspaces.length - 1];
-
// Figure out the new layout
this._positionWorkspaces();
let newScale = this._workspaces[0].scale;
@@ -457,7 +452,6 @@ SingleView.prototype = {
__proto__: GenericWorkspacesView.prototype,
_init: function(width, height, x, y, animate) {
- this._scroll = null;
GenericWorkspacesView.prototype._init.call(this, width, height, x, y, animate);
this._actor.set_clip(x, y, width, height);
@@ -465,25 +459,14 @@ SingleView.prototype = {
this._removeButton = null;
this._indicatorsPanel = null;
this._indicatorsPanelWidth = null;
-
- let activeWorkspaceIndex = global.screen.get_active_workspace_index();
- for (let w = 0; w < this._workspaces.length; w++) {
- if (w != activeWorkspaceIndex) {
- this._workspaces[w].actor.hide();
- continue;
- }
- this._workspaces[w].actor.show();
- this._workspaces[w]._windowOverlaysGroup.show();
- }
+ this._scroll = null;
+ this._scrolling = false;
+ this._animatingScroll = false;
},
_positionWorkspaces: function() {
- let position = global.screen.get_active_workspace_index();
let scale = this._width / global.screen_width;
-
- if (this._scroll != null)
- position = this._scroll.adjustment.value;
- let isInt = (Math.round(position) === position);
+ let active = global.screen.get_active_workspace_index();
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
@@ -492,156 +475,255 @@ SingleView.prototype = {
workspace.gridCol = 0;
workspace.scale = scale;
- workspace.actor.set_scale(scale, scale);
let _width = workspace.actor.width * scale;
- workspace.gridX = this._x
- + (w - position) * (_width + SINGLE_VIEW_SPACING);
+ workspace.gridX = this._x + (w - active) * (_width + SINGLE_VIEW_SPACING);
workspace.gridY = this._y;
+
+ workspace.setSelected(false);
+ }
+ },
+
+ _updateWorkspaceActors: function() {
+ for (let w = 0; w < this._workspaces.length; w++) {
+ let workspace = this._workspaces[w];
+
+ workspace.actor.set_scale(workspace.scale, workspace.scale);
workspace.actor.set_position(workspace.gridX, workspace.gridY);
- // show the overlay unconditionally first, so items get
- // positioned correctly, then hide if necessary
- workspace._windowOverlaysGroup.show();
- if (isInt) {
- if (this.actor.get_stage() != null)
- workspace.positionWindows(0);
- if (w == position) {
- workspace.actor.show();
- } else {
- workspace._windowOverlaysGroup.hide();
- workspace.actor.hide();
- }
+ workspace.positionWindows(0);
+ }
+ },
+
+ _scrollToActive: function(showAnimation) {
+ let active = global.screen.get_active_workspace_index();
+
+ this._scrollWorkspacesToIndex(active, showAnimation);
+ this._scrollScrollBarToIndex(active, showAnimation);
+ },
+
+ _scrollWorkspacesToIndex: function(index, showAnimation) {
+ let active = global.screen.get_active_workspace_index();
+ let targetWorkspaceNewX = this._x;
+ let targetWorkspaceCurrentX = this._workspaces[index].gridX;
+ let dx = targetWorkspaceNewX - targetWorkspaceCurrentX;
+
+ for (let w = 0; w < this._workspaces.length; w++) {
+ let workspace = this._workspaces[w];
+
+ workspace.gridX += dx;
+ workspace.actor.show();
+ workspace._hideAllOverlays();
+
+ let visible = (w == active);
+ if (showAnimation) {
+ Tweener.addTween(workspace.actor,
+ { x: workspace.gridX,
+ time: WORKSPACE_SWITCH_TIME,
+ transition: 'easeOutQuad',
+ onComplete: function() {
+ if (visible)
+ workspace._fadeInAllOverlays();
+ else
+ workspace.actor.hide();
+ }});
} else {
- workspace._windowOverlaysGroup.hide();
- if (Math.abs(w - position) <= 1)
- workspace.actor.show();
+ workspace.actor.x = workspace.gridX;
+ if (visible)
+ workspace._fadeInAllOverlays();
else
workspace.actor.hide();
}
}
},
+ _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) {
- let adj = this._scroll.get_adjustment();
- adj.upper = newNumWorkspaces;
- this._scroll.adjustment = adj;
- }
- let lostWorkspaces = [];
+ if (this._scroll != null)
+ this._scroll.adjustment.upper = newNumWorkspaces;
if (newNumWorkspaces > oldNumWorkspaces) {
// Create new workspace groups
- for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
+ for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
this._addWorkspaceActor(w);
- this._workspaces[w].actor.hide();
- }
+ this._positionWorkspaces();
+ this._updateWorkspaceActors();
+ this._scrollScrollBarToIndex(active + 1, false);
} else {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].destroy();
+ this._workspaces = [];
this._actor.remove_all();
- //Without this will be a lot of warnings
+ // Without this there will be lots of warnings
this._actor.hide();
-
- this._workspaces = [];
- let activeWorkspaceIndex = global.screen.get_active_workspace_index();
- for (let w = 0; w < global.screen.n_workspaces; w++) {
+ for (let w = 0; w < global.screen.n_workspaces; w++)
this._addWorkspaceActor(w);
- if (w == activeWorkspaceIndex) {
- this._workspaces[w].actor.show();
- } else {
- this._workspaces[w].actor.hide();
- }
- }
this._actor.show();
+ this._positionWorkspaces();
+ this._updateWorkspaceActors();
}
- this._positionWorkspaces();
-
- // 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].actor.show();
- this._workspaces[activeIndex]._windowOverlaysGroup.show();
this._updatePanelVisibility();
},
_activeWorkspaceChanged: function(wm, from, to, direction) {
this._updatePanelVisibility();
- let showAnimation = true;
- if (this._scroll != null) {
- let adj = this._scroll.get_adjustment();
- if (Math.round(adj.value - to) != adj.value - to)
- showAnimation = false;
- if (adj.value - to == 0)
- showAnimation = false;
- adj.value = to;
- this._scroll.adjustment = adj;
- }
- if (showAnimation) {
- let fx;
- if (from > to) {
- fx = this._workspaces[0].actor.width;
- } else {
- fx = -this._workspaces[0].actor.width;
- }
- this._workspaces[from]._windowOverlaysGroup.hide();
- this._workspaces[to].actor.set_position(this._x - fx, this._workspaces[to].gridY);
- this._workspaces[to].actor.show();
- Tweener.addTween(this._workspaces[to].actor,
- { x: this._x,
- transition: 'easeOutQuad',
- time: WORKSPACE_SWITCH_TIME
- });
-
- Tweener.addTween(this._workspaces[from].actor,
- { x: this._x + fx,
- transition: 'easeOutQuad',
- time: WORKSPACE_SWITCH_TIME,
- onComplete: this._positionWorkspaces,
- onCompleteScope: this
- });
- } else
- this._positionWorkspaces();
+ if (this._scrolling)
+ return;
+
+ this._scrollToActive(true);
},
_addWorkspaceActor: function(workspaceNum) {
let workspace = new Workspace.Workspace(workspaceNum, this._actor);
- this._actor.add_actor(workspace.actor);
- workspace._windowOverlaysGroup.hide();
+ 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();
+ if (Math.abs(i - adj.value) <= 1)
+ this._workspaces[i].actor.show();
+ else
+ this._workspaces[i].actor.hide();
+ 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 panel = new St.BoxLayout({ 'pack-start': true, vertical: true });
let actor = new St.BoxLayout({ 'pack-start': true });
- let adj = new St.Adjustment({ value: global.screen.get_active_workspace_index(),
+ 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': 1,
+ page_increment: 1,
+ page_size: 1,
+ step_increment: 0,
upper: this._workspaces.length });
- this._scroll = new St.ScrollBar({ adjustment: null, vertical: false, name: 'SwitchScroll' });
-
- this._scroll.connect('notify::adjustment', Lang.bind(this, function() {
- this._scroll.adjustment.connect('notify::value', Lang.bind(this, function () {
- if (Math.abs(Math.round(this._scroll.adjustment.value) - this._scroll.adjustment.value) < 0.1) {
- this._scroll.adjustment.set_value (Math.round(this._scroll.adjustment.value));
- this._workspaces[Math.round(this._scroll.adjustment.value)]._metaWorkspace.activate(global.get_current_time());
- } else
- this._positionWorkspaces();
+ 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);
}));
- }));
- this._scroll.adjustment = adj;
let addButton = new St.Button({ style_class: "workspace-controls add" });
this._addButton = addButton;
@@ -680,7 +762,7 @@ SingleView.prototype = {
if (active) {
actor.style_class = 'workspace-indicator active';
}
- actor.connect('button-release-event', Lang.bind(this, function() {
+ actor.connect('clicked', Lang.bind(this, function() {
if (this._workspaces[i] != undefined)
this._workspaces[i]._metaWorkspace.activate(global.get_current_time());
}));
@@ -695,16 +777,7 @@ SingleView.prototype = {
return false;
});
- actor.connect('scroll-event', Lang.bind(this, function(actor, event) {
- let direction = event.get_scroll_direction();
- let activeWorkspaceIndex = global.screen.get_active_workspace_index();
- let numWorkspaces = global.screen.n_workspaces;
- if (direction == Clutter.ScrollDirection.DOWN && activeWorkspaceIndex < numWorkspaces - 1) {
- this._workspaces[activeWorkspaceIndex+1]._metaWorkspace.activate(global.get_current_time());
- } else if (direction == Clutter.ScrollDirection.UP && activeWorkspaceIndex > 0) {
- this._workspaces[activeWorkspaceIndex-1]._metaWorkspace.activate(global.get_current_time());
- }
- }));
+ actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._indicatorsPanel.add_actor(actor);
@@ -806,11 +879,9 @@ SingleView.prototype = {
},
_addNewWorkspace: function() {
- // Button with opacity 0 is clickable.
- if (global.screen.n_workspaces >= MAX_WORKSPACES)
- return;
- global.screen.append_new_workspace(false, global.get_current_time());
- this._workspaces[this._workspaces.length - 1]._metaWorkspace.activate(Clutter.get_current_event_time());
+ let ws = global.screen.append_new_workspace(false,
+ global.get_current_time());
+ ws.activate(global.get_current_time());
},
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]