workspaceThumbnail: move event handling to ThumbnailsBox

Currently, click and drop events are handled by each WorkspaceThumbnail
instance. With the introduction of the workspace cut and the request
to extend the reactive area of the workspace selector to the edge
of the monitor, it becomes more convenient to do all the event handling
inside ThumbnailsBox, even if this requires some manual layout computation.

https://bugzilla.gnome.org/show_bug.cgi?id=643319
This commit is contained in:
Stefano Facchini 2012-02-10 18:59:05 +01:00
parent 21636f3f29
commit 369c1b0a41

View File

@ -158,8 +158,7 @@ const WorkspaceThumbnail = new Lang.Class({
this._removed = false; this._removed = false;
this.actor = new St.Widget({ reactive: true, this.actor = new St.Widget({ clip_to_allocation: true,
clip_to_allocation: true,
style_class: 'workspace-thumbnail' }); style_class: 'workspace-thumbnail' });
this.actor._delegate = this; this.actor._delegate = this;
@ -167,15 +166,6 @@ const WorkspaceThumbnail = new Lang.Class({
this.actor.add_actor(this._contents); this.actor.add_actor(this._contents);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); 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._activate();
return true;
}));
this._background = Meta.BackgroundActor.new_for_screen(global.screen); this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._contents.add_actor(this._background); this._contents.add_actor(this._background);
@ -407,7 +397,9 @@ const WorkspaceThumbnail = new Lang.Class({
let clone = new WindowClone(win); let clone = new WindowClone(win);
clone.connect('selected', clone.connect('selected',
Lang.bind(this, this._activate)); Lang.bind(this, function(clone, time) {
this.activate(time);
}));
clone.connect('drag-begin', clone.connect('drag-begin',
Lang.bind(this, function(clone) { Lang.bind(this, function(clone) {
Main.overview.beginWindowDrag(); Main.overview.beginWindowDrag();
@ -432,7 +424,7 @@ const WorkspaceThumbnail = new Lang.Class({
return clone; return clone;
}, },
_activate : function (clone, time) { activate : function (time) {
if (this.state > ThumbnailState.NORMAL) if (this.state > ThumbnailState.NORMAL)
return; return;
@ -443,8 +435,8 @@ const WorkspaceThumbnail = new Lang.Class({
this.metaWorkspace.activate(time); this.metaWorkspace.activate(time);
}, },
// Draggable target interface // Draggable target interface used only by ThumbnailsBox
handleDragOver : function(source, actor, x, y, time) { handleDragOverInternal : function(source, time) {
if (source == Main.xdndHandler) { if (source == Main.xdndHandler) {
this.metaWorkspace.activate(time); this.metaWorkspace.activate(time);
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
@ -453,11 +445,6 @@ const WorkspaceThumbnail = new Lang.Class({
if (this.state > ThumbnailState.NORMAL) if (this.state > ThumbnailState.NORMAL)
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
let [w, h] = this.actor.get_transformed_size();
// Bubble up if we're in the "workspace cut".
if (y < WORKSPACE_CUT_SIZE || y > h - WORKSPACE_CUT_SIZE)
return DND.DragMotionResult.CONTINUE;
if (source.realWindow && !this._isMyWindow(source.realWindow)) if (source.realWindow && !this._isMyWindow(source.realWindow))
return DND.DragMotionResult.MOVE_DROP; return DND.DragMotionResult.MOVE_DROP;
if (source.shellWorkspaceLaunch) if (source.shellWorkspaceLaunch)
@ -466,7 +453,7 @@ const WorkspaceThumbnail = new Lang.Class({
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
acceptDrop : function(source, actor, x, y, time) { acceptDropInternal : function(source, time) {
if (this.state > ThumbnailState.NORMAL) if (this.state > ThumbnailState.NORMAL)
return false; return false;
@ -504,7 +491,8 @@ const ThumbnailsBox = new Lang.Class({
Name: 'ThumbnailsBox', Name: 'ThumbnailsBox',
_init: function() { _init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'workspace-thumbnails', this.actor = new Shell.GenericContainer({ reactive: true,
style_class: 'workspace-thumbnails',
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT }); request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); 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('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@ -533,6 +521,7 @@ const ThumbnailsBox = new Lang.Class({
this._indicator = indicator; this._indicator = indicator;
this.actor.add_actor(indicator); this.actor.add_actor(indicator);
this._dropWorkspace = -1;
this._dropPlaceholderPos = -1; this._dropPlaceholderPos = -1;
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' }); this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
this.actor.add_actor(this._dropPlaceholder); this.actor.add_actor(this._dropPlaceholder);
@ -550,6 +539,9 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails = []; this._thumbnails = [];
this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
Main.overview.connect('item-drag-begin', Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin)); Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end', Main.overview.connect('item-drag-end',
@ -564,6 +556,22 @@ const ThumbnailsBox = new Lang.Class({
Lang.bind(this, this._onDragCancelled)); Lang.bind(this, this._onDragCancelled));
}, },
_onButtonRelease: function(actor, event) {
let [stageX, stageY] = event.get_coords();
let [r, x, y] = this.actor.transform_stage_point(stageX, stageY);
for (let i = 0; i < this._thumbnails.length; i++) {
let thumbnail = this._thumbnails[i]
let [w, h] = thumbnail.actor.get_transformed_size();
if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) {
thumbnail.activate(event.time);
break;
}
}
return true;
},
_onDragBegin: function() { _onDragBegin: function() {
this._dragCancelled = false; this._dragCancelled = false;
this._dragMonitor = { this._dragMonitor = {
@ -609,95 +617,108 @@ const ThumbnailsBox = new Lang.Class({
// Draggable target interface // Draggable target interface
handleDragOver : function(source, actor, x, y, time) { handleDragOver : function(source, actor, x, y, time) {
if (!source.realWindow && !source.shellWorkspaceLaunch) if (!source.realWindow && !source.shellWorkspaceLaunch && source != Main.xdndHandler)
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
if (!Meta.prefs_get_dynamic_workspaces()) if (!Meta.prefs_get_dynamic_workspaces())
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
let spacing = this.actor.get_theme_node().get_length('spacing'); let spacing = this.actor.get_theme_node().get_length('spacing');
let thumbHeight = this._porthole.height * this._scale;
let workspace = -1; this._dropWorkspace = -1;
let firstThumbY; let placeholderPos = -1;
let targetBase;
if (this._dropPlaceholderPos == 0) if (this._dropPlaceholderPos == 0)
firstThumbY = this._dropPlaceholder.y; targetBase = this._dropPlaceholder.y;
else else
firstThumbY = this._thumbnails[0].actor.y; targetBase = this._thumbnails[0].actor.y;
for (let i = 0; i < this._thumbnails.length; i ++) { let targetTop = targetBase - spacing - WORKSPACE_CUT_SIZE;
let targetBase = firstThumbY + (thumbHeight + spacing) * i; let length = this._thumbnails.length;
for (let i = 0; i < length; i ++) {
// Allow the reorder target to have a 10px "cut" into // Allow the reorder target to have a 10px "cut" into
// each side of the thumbnail, to make dragging onto the // each side of the thumbnail, to make dragging onto the
// placeholder easier // placeholder easier
let targetTop = targetBase - spacing - WORKSPACE_CUT_SIZE; let [w, h] = this._thumbnails[i].actor.get_transformed_size();
let targetBottom = targetBase + WORKSPACE_CUT_SIZE; let targetBottom = targetBase + WORKSPACE_CUT_SIZE;
let nextTargetBase = targetBase + h + spacing;
let nextTargetTop = nextTargetBase - spacing - ((i == length - 1) ? 0: WORKSPACE_CUT_SIZE);
// Expand the target to include the placeholder, if it exists. // Expand the target to include the placeholder, if it exists.
if (i == this._dropPlaceholderPos) if (i == this._dropPlaceholderPos)
targetBottom += this._dropPlaceholder.get_height(); targetBottom += this._dropPlaceholder.get_height();
if (y > targetTop && y <= targetBottom) { if (y > targetTop && y <= targetBottom && source != Main.xdndHandler) {
workspace = i; placeholderPos = i;
break; break;
} else if (y > targetBottom && y <= nextTargetTop) {
this._dropWorkspace = i;
break
} }
targetBase = nextTargetBase;
targetTop = nextTargetTop;
} }
if (this._dropPlaceholderPos != workspace) { if (this._dropPlaceholderPos != placeholderPos) {
this._dropPlaceholderPos = workspace; this._dropPlaceholderPos = placeholderPos;
this.actor.queue_relayout(); this.actor.queue_relayout();
} }
if (workspace == -1) if (this._dropWorkspace != -1)
return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, time);
else if (this._dropPlaceholderPos != -1)
return DND.DragMotionResult.MOVE_DROP;
else
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
return DND.DragMotionResult.MOVE_DROP;
}, },
acceptDrop: function(source, actor, x, y, time) { acceptDrop: function(source, actor, x, y, time) {
if (this._dropPlaceholderPos == -1) if (this._dropWorkspace != -1) {
return false; return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, time);
} else if (this._dropPlaceholderPos != -1) {
if (!source.realWindow && !source.shellWorkspaceLaunch)
return false;
if (!source.realWindow && !source.shellWorkspaceLaunch) let isWindow = !!source.realWindow;
return false;
let isWindow = !!source.realWindow; // To create a new workspace, we first slide all the windows on workspaces
// below us to the next workspace, leaving a blank workspace for us to recycle.
let newWorkspaceIndex;
[newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
// To create a new workspace, we first slide all the windows on workspaces // Nab all the windows below us.
// below us to the next workspace, leaving a blank workspace for us to recycle. let windows = global.get_window_actors().filter(function(win) {
let newWorkspaceIndex; if (isWindow)
[newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1]; return win.get_workspace() >= newWorkspaceIndex && win != source;
else
return win.get_workspace() >= newWorkspaceIndex;
});
// ... move them down one.
windows.forEach(function(win) {
win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
true, time);
});
// Nab all the windows below us.
let windows = global.get_window_actors().filter(function(win) {
if (isWindow) if (isWindow)
return win.get_workspace() >= newWorkspaceIndex && win != source; // ... and bam, a workspace, good as new.
else source.metaWindow.change_workspace_by_index(newWorkspaceIndex,
return win.get_workspace() >= newWorkspaceIndex; true, time);
}); else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: newWorkspaceIndex,
timestamp: time });
// This new workspace will be automatically removed if the application fails
// to open its first window within some time, as tracked by Shell.WindowTracker.
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
// workspace while we wait for the startup sequence to load.
Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
WORKSPACE_KEEP_ALIVE_TIME);
}
// ... move them down one. return true;
windows.forEach(function(win) { } else {
win.meta_window.change_workspace_by_index(win.get_workspace() + 1, return false;
true, time);
});
if (isWindow)
// ... and bam, a workspace, good as new.
source.metaWindow.change_workspace_by_index(newWorkspaceIndex,
true, time);
else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: newWorkspaceIndex,
timestamp: time });
// This new workspace will be automatically removed if the application fails
// to open its first window within some time, as tracked by Shell.WindowTracker.
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
// workspace while we wait for the startup sequence to load.
Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
WORKSPACE_KEEP_ALIVE_TIME);
} }
return true;
}, },
show: function() { show: function() {