[gnome-shell] Bug 594916 - Allow cancelling DND by hitting Esc
- From: Marina Zhurakhinskaya <marinaz src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-shell] Bug 594916 - Allow cancelling DND by hitting Esc
- Date: Tue, 22 Sep 2009 20:16:18 +0000 (UTC)
commit 6cae94edcc23cbbb509c3a02833b132c6e0aa74f
Author: Marina Zhurakhinskaya <marinaz redhat com>
Date: Tue Sep 22 16:15:28 2009 -0400
Bug 594916 - Allow cancelling DND by hitting Esc
Esc used to close the overview and get the DND actor stuck on the desktop when
it was pressed when dragging.
js/ui/appDisplay.js | 2 +-
js/ui/dnd.js | 209 ++++++++++++++++++++++++++++++++++-----------------
2 files changed, 140 insertions(+), 71 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 8298b78..328c387 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -781,7 +781,7 @@ BaseWellItem.prototype = {
if (!hover) {
if (this.actor.pressed && this._dragStartX != null) {
this.actor.fake_release();
- this._draggable.startDrag(this.icon.actor, this._dragStartX, this._dragStartY,
+ this._draggable.startDrag(this._dragStartX, this._dragStartY,
Clutter.get_current_event_time());
} else {
this._dragStartX = null;
diff --git a/js/ui/dnd.js b/js/ui/dnd.js
index 4989d5e..9188cad 100644
--- a/js/ui/dnd.js
+++ b/js/ui/dnd.js
@@ -8,6 +8,25 @@ const Tweener = imports.ui.tweener;
const SNAP_BACK_ANIMATION_TIME = 0.25;
+let eventHandlerActor = null;
+let currentDraggable = null;
+
+function _getEventHandlerActor() {
+ if (!eventHandlerActor) {
+ eventHandlerActor = new Clutter.Rectangle();
+ eventHandlerActor.width = 0;
+ eventHandlerActor.height = 0;
+ global.stage.add_actor(eventHandlerActor);
+ // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
+ // when you've grabbed the pointer.
+ eventHandlerActor.connect('event',
+ function(actor, event) {
+ return currentDraggable._onEvent(actor, event);
+ });
+ }
+ return eventHandlerActor;
+}
+
function _Draggable(actor, manualMode) {
this._init(actor, manualMode);
}
@@ -18,7 +37,11 @@ _Draggable.prototype = {
if (!manualMode)
this.actor.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
- this._haveSourceGrab = false;
+ this._onEventId = null;
+
+ this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
+ this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
+ this._snapBackInProgress = false; // The drag has been cancelled and the item is in the process of snapping back.
},
_onButtonPress : function (actor, event) {
@@ -27,8 +50,8 @@ _Draggable.prototype = {
if (Tweener.getTweenCount(actor))
return false;
- this._haveSourceGrab = true;
- this._grabActor(actor);
+ this._buttonDown = true;
+ this._grabActor();
let [stageX, stageY] = event.get_coords();
this._dragStartX = stageX;
@@ -37,41 +60,69 @@ _Draggable.prototype = {
return false;
},
- _grabActor : function (actor) {
- Clutter.grab_pointer(actor);
+ _grabActor: function() {
+ Clutter.grab_pointer(this.actor);
+ this._onEventId = this.actor.connect('event',
+ Lang.bind(this, this._onEvent));
+ },
- // We intercept motion and button-release events so that when
- // you release after dragging, the app doesn't see that and
- // think you just clicked. We connect to 'event' rather than
- // 'captured-event' because the capturing phase doesn't happen
- // when you've grabbed the pointer.
- this._onEventId = actor.connect('event',
- Lang.bind(this, this._onEvent));
+ _ungrabActor: function() {
+ Clutter.ungrab_pointer();
+ this.actor.disconnect(this._onEventId);
+ this._onEventId = null;
+ },
+
+ _grabEvents: function() {
+ Clutter.grab_pointer(_getEventHandlerActor());
+ Clutter.grab_keyboard(_getEventHandlerActor());
},
- _ungrabActor : function (actor) {
+ _ungrabEvents: function() {
Clutter.ungrab_pointer();
- actor.disconnect(this._onEventId);
+ Clutter.ungrab_keyboard();
},
- _onEvent : function (actor, event) {
- if (this._dragActor) {
- if (actor != this._dragActor )
+ _onEvent: function(actor, event) {
+ // We intercept BUTTON_RELEASE event to know that the button was released in case we
+ // didn't start the drag, to drop the draggable in case the drag was in progress, and
+ // to complete the drag and ensure that whatever happens to be under the pointer does
+ // not get triggered if the drag was cancelled with Esc.
+ if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
+ this._buttonDown = false;
+ if (this._dragInProgress) {
+ return this._dragActorDropped(event);
+ } else if (this._dragActor != null && !this._snapBackInProgress) {
+ // Drag must have been cancelled with Esc.
+ this._dragComplete();
+ return true;
+ } else {
+ // Drag has never started.
+ this._ungrabActor();
return false;
- } else if (actor != this.actor)
- return false;
+ }
+ // We intercept MOTION event to figure out if the drag has started and to draw
+ // this._dragActor under the pointer when dragging is in progress
+ } else if (event.type() == Clutter.EventType.MOTION) {
+ if (this._dragInProgress) {
+ return this._updateDragPosition(event);
+ } else if (this._dragActor == null) {
+ return this._maybeStartDrag(event);
+ }
+ // We intercept KEY_PRESS event so that we can process Esc key press to cancel
+ // dragging and ignore all other key presses.
+ } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragInProgress) {
+ let symbol = event.get_key_symbol();
+ if (symbol == Clutter.Escape) {
+ this._cancelDrag(event.get_time());
+ return true;
+ }
+ }
- if (event.type() == Clutter.EventType.BUTTON_RELEASE)
- return this._onButtonRelease(actor, event);
- else if (event.type() == Clutter.EventType.MOTION)
- return this._onMotion(actor, event);
- else
- return false;
+ return false;
},
/**
* startDrag:
- * @actor: Origin actor for drag and drop
* @stageX: X coordinate of event
* @stageY: Y coordinate of event
* @time: Event timestamp
@@ -80,8 +131,14 @@ _Draggable.prototype = {
* This function is useful to call if you've specified manualMode
* for the draggable.
*/
- startDrag: function (actor, stageX, stageY, time) {
+ startDrag: function (stageX, stageY, time) {
+ currentDraggable = this;
+ this._dragInProgress = true;
+
this.emit('drag-begin', time);
+ if (this._onEventId)
+ this._ungrabActor();
+ this._grabEvents();
this._dragStartX = stageX;
this._dragStartY = stageY;
@@ -112,59 +169,59 @@ _Draggable.prototype = {
this._dragActorSource = this.actor;
}
this._dragOrigParent = undefined;
- if (this._haveSourceGrab) {
- this._haveSourceGrab = false;
- this._ungrabActor(actor);
- }
- this._grabActor(this._dragActor);
this._dragOffsetX = this._dragActor.x - this._dragStartX;
this._dragOffsetY = this._dragActor.y - this._dragStartY;
} else {
- this._dragActor = actor;
+ this._dragActor = this.actor;
this._dragActorSource = undefined;
- this._dragOrigParent = actor.get_parent();
+ this._dragOrigParent = this.actor.get_parent();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
- let [actorStageX, actorStageY] = actor.get_transformed_position();
+ let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the stage
- let [scaledWidth, scaledHeight] = actor.get_transformed_size();
- actor.set_scale(scaledWidth / actor.width,
- scaledHeight / actor.height);
+ let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
+ this.actor.set_scale(scaledWidth / this.actor.width,
+ scaledHeight / this.actor.height);
}
- this._dragActor.reparent(actor.get_stage());
+ this._dragActor.reparent(this.actor.get_stage());
this._dragActor.raise_top();
},
- _onMotion : function (actor, event) {
+ _maybeStartDrag: function(event) {
let [stageX, stageY] = event.get_coords();
- // If we haven't begun a drag, see if the user has moved the
- // mouse enough to trigger a drag
+ // See if the user has moved the mouse enough to trigger a drag
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
- if (!this._dragActor &&
- (Math.abs(stageX - this._dragStartX) > threshold ||
+ if ((Math.abs(stageX - this._dragStartX) > threshold ||
Math.abs(stageY - this._dragStartY) > threshold)) {
- this.startDrag(actor, stageX, stageY, event.get_time());
+ this.startDrag(stageX, stageY, event.get_time());
+ this._updateDragPosition(event);
}
+ return true;
+ },
+
+ _updateDragPosition : function (event) {
+ let [stageX, stageY] = event.get_coords();
+
// If we are dragging, update the position
if (this._dragActor) {
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
// Because we want to find out what other actor is located at the current position of this._dragActor,
// we have to temporarily hide this._dragActor.
- this._dragActor.hide();
- let target = actor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
- stageX + this._dragOffsetX,
- stageY + this._dragOffsetY);
+ this._dragActor.hide();
+ let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
+ stageX + this._dragOffsetX,
+ stageY + this._dragOffsetY);
this._dragActor.show();
while (target) {
if (target._delegate && target._delegate.handleDragOver) {
@@ -172,7 +229,7 @@ _Draggable.prototype = {
// We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
- target._delegate.handleDragOver(this.actor._delegate, actor,
+ target._delegate.handleDragOver(this.actor._delegate, this._dragActor,
(stageX + this._dragOffsetX - targX) / target.scale_x,
(stageY + this._dragOffsetY - targY) / target.scale_y,
event.get_time());
@@ -184,40 +241,42 @@ _Draggable.prototype = {
return true;
},
- _onButtonRelease : function (actor, event) {
- this._ungrabActor(actor);
-
- let dragging = (actor == this._dragActor);
- this._dragActor = undefined;
-
- if (!dragging)
- return false;
-
- // Find a drop target
- actor.hide();
+ _dragActorDropped: function(event) {
+ // Find a drop target. Because we want to find out what other actor is located at
+ // the current position of this._dragActor, we have to temporarily hide this._dragActor.
+ this._dragActor.hide();
let [dropX, dropY] = event.get_coords();
- let target = actor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
- dropX, dropY);
- actor.show();
+ let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
+ dropX, dropY);
+ this._dragActor.show();
while (target) {
if (target._delegate && target._delegate.acceptDrop) {
let [targX, targY] = target.get_transformed_position();
- if (target._delegate.acceptDrop(this.actor._delegate, actor,
+ if (target._delegate.acceptDrop(this.actor._delegate, this._dragActor,
(dropX - targX) / target.scale_x,
(dropY - targY) / target.scale_y,
event.get_time())) {
// If it accepted the drop without taking the actor,
// destroy it.
- if (actor.get_parent() == actor.get_stage())
- actor.destroy();
+ if (this._dragActor.get_parent() == this._dragActor.get_stage())
+ this._dragActor.destroy();
+ this._dragInProgress = false;
this.emit('drag-end', event.get_time(), true);
+ this._dragComplete();
return true;
}
}
target = target.get_parent();
}
+ this._cancelDrag(event.get_time());
+
+ return true;
+ },
+
+ _cancelDrag: function(eventTime) {
+ this._dragInProgress = false;
// Snap back to the actor source if the source is still around, snap back
// to the original location if the actor itself was being dragged or the
// source is no longer around.
@@ -227,17 +286,17 @@ _Draggable.prototype = {
[snapBackX, snapBackY] = this._dragActorSource.get_transformed_position();
}
+ this._snapBackInProgress = true;
// No target, so snap back
- Tweener.addTween(actor,
+ Tweener.addTween(this._dragActor,
{ x: snapBackX,
y: snapBackY,
time: SNAP_BACK_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._onSnapBackComplete,
onCompleteScope: this,
- onCompleteParams: [actor, event.get_time()]
+ onCompleteParams: [this._dragActor, eventTime]
});
- return true;
},
_onSnapBackComplete : function (dragActor, eventTime) {
@@ -249,6 +308,16 @@ _Draggable.prototype = {
dragActor.destroy();
}
this.emit('drag-end', eventTime, false);
+
+ this._snapBackInProgress = false;
+ if (!this._buttonDown)
+ this._dragComplete();
+ },
+
+ _dragComplete: function() {
+ this._dragActor = undefined;
+ currentDraggable = null;
+ this._ungrabEvents();
}
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]