dnd: Get dragging device from the triggering events

Instead of fetching the CLUTTER_POINTER_DEVICE device. It will
be wrong if drags get initiated from tablet pointers. This allows
for DnD operations to be started, moved, and more importantly
finished through tablet devices.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/540
This commit is contained in:
Carlos Garnacho 2018-09-07 15:13:18 +02:00 committed by Florian Müllner
parent 6845c6f958
commit c2961f2152

View File

@ -119,7 +119,7 @@ var _Draggable = class _Draggable {
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
this._buttonDown = true; this._buttonDown = true;
this._grabActor(); this._grabActor(event.get_device());
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
this._dragStartX = stageX; this._dragStartX = stageX;
@ -136,10 +136,8 @@ var _Draggable = class _Draggable {
if (Tweener.getTweenCount(actor)) if (Tweener.getTweenCount(actor))
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
this._touchSequence = event.get_event_sequence();
this._buttonDown = true; this._buttonDown = true;
this._grabActor(); this._grabActor(event.get_device(), event.get_event_sequence());
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
this._dragStartX = stageX; this._dragStartX = stageX;
@ -148,16 +146,14 @@ var _Draggable = class _Draggable {
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
} }
_grabDevice(actor) { _grabDevice(actor, pointer, touchSequence) {
let manager = Clutter.DeviceManager.get_default(); if (touchSequence)
let pointer = manager.get_core_device(Clutter.InputDeviceType.POINTER_DEVICE); pointer.sequence_grab(touchSequence, actor);
if (pointer && this._touchSequence)
pointer.sequence_grab(this._touchSequence, actor);
else if (pointer) else if (pointer)
pointer.grab (actor); pointer.grab (actor);
this._grabbedDevice = pointer; this._grabbedDevice = pointer;
this._touchSequence = touchSequence;
} }
_ungrabDevice() { _ungrabDevice() {
@ -170,8 +166,8 @@ var _Draggable = class _Draggable {
this._grabbedDevice = null; this._grabbedDevice = null;
} }
_grabActor() { _grabActor(device, touchSequence) {
this._grabDevice(this.actor); this._grabDevice(this.actor, device, touchSequence);
this._onEventId = this.actor.connect('event', this._onEventId = this.actor.connect('event',
this._onEvent.bind(this)); this._onEvent.bind(this));
} }
@ -185,11 +181,11 @@ var _Draggable = class _Draggable {
this._onEventId = null; this._onEventId = null;
} }
_grabEvents() { _grabEvents(device, touchSequence) {
if (!this._eventsGrabbed) { if (!this._eventsGrabbed) {
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor()); this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
if (this._eventsGrabbed) if (this._eventsGrabbed)
this._grabDevice(_getEventHandlerActor()); this._grabDevice(_getEventHandlerActor(), device, touchSequence);
} }
} }
@ -201,14 +197,29 @@ var _Draggable = class _Draggable {
} }
} }
_eventIsRelease(event) {
if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
let buttonMask = (Clutter.ModifierType.BUTTON1_MASK |
Clutter.ModifierType.BUTTON2_MASK |
Clutter.ModifierType.BUTTON3_MASK);
/* We only obey the last button release from the device,
* other buttons may get pressed/released during the DnD op.
*/
return (event.get_state() & buttonMask) == 0;
} else if (event.type() == Clutter.EventType.TOUCH_END) {
/* For touch, we only obey the pointer emulating sequence */
return global.display.is_pointer_emulating_sequence(event.get_event_sequence());
}
return false;
},
_onEvent(actor, event) { _onEvent(actor, event) {
// We intercept BUTTON_RELEASE event to know that the button was released in case we // 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 // 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 // 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. // not get triggered if the drag was cancelled with Esc.
if (event.type() == Clutter.EventType.BUTTON_RELEASE || if (this._eventIsRelease(event)) {
(event.type() == Clutter.EventType.TOUCH_END &&
global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
this._buttonDown = false; this._buttonDown = false;
if (this._dragState == DragState.DRAGGING) { if (this._dragState == DragState.DRAGGING) {
return this._dragActorDropped(event); return this._dragActorDropped(event);
@ -268,7 +279,19 @@ var _Draggable = class _Draggable {
* This function is useful to call if you've specified manualMode * This function is useful to call if you've specified manualMode
* for the draggable. * for the draggable.
*/ */
startDrag(stageX, stageY, time, sequence) { startDrag(stageX, stageY, time, sequence, device) {
if (device == undefined) {
let event = Clutter.get_current_event();
if (event)
device = event.get_device();
if (device == undefined) {
let manager = Clutter.DeviceManager.get_default();
device = manager.get_core_device(Clutter.InputDeviceType.POINTER_DEVICE);
}
}
currentDraggable = this; currentDraggable = this;
this._dragState = DragState.DRAGGING; this._dragState = DragState.DRAGGING;
@ -283,8 +306,7 @@ var _Draggable = class _Draggable {
if (this._onEventId) if (this._onEventId)
this._ungrabActor(); this._ungrabActor();
this._touchSequence = sequence; this._grabEvents(device, sequence);
this._grabEvents();
global.display.set_cursor(Meta.Cursor.DND_IN_DRAG); global.display.set_cursor(Meta.Cursor.DND_IN_DRAG);
this._dragX = this._dragStartX = stageX; this._dragX = this._dragStartX = stageX;
@ -401,7 +423,7 @@ var _Draggable = class _Draggable {
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold; let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
if ((Math.abs(stageX - this._dragStartX) > threshold || if ((Math.abs(stageX - this._dragStartX) > threshold ||
Math.abs(stageY - this._dragStartY) > threshold)) { Math.abs(stageY - this._dragStartY) > threshold)) {
this.startDrag(stageX, stageY, event.get_time(), this._touchSequence); this.startDrag(stageX, stageY, event.get_time(), this._touchSequence, event.get_device());
this._updateDragPosition(event); this._updateDragPosition(event);
} }