dnd: Add addClickAction() method

The events that the draggable uses to initiate a drag operation
are the same as the ones a click action consumes for clicks.

It is still possible to combine the two, but it's finicky and far
from being straight-forward. To make this easier, add a dedicated
addClickAction() method to draggables that takes care of the
setup before adding the action to the draggable actor.

In the longer term, we'll want to turn DND into an action, and
have something like GTK's gesture group to allow combining actions
that would otherwise step on each other's toes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2742>
This commit is contained in:
Florian Müllner 2023-04-20 12:26:23 +02:00 committed by Marge Bot
parent a9ba9b7a47
commit 3f9ee9cd5e
2 changed files with 54 additions and 37 deletions

View File

@ -119,6 +119,50 @@ var _Draggable = class _Draggable extends Signals.EventEmitter {
this._dragCancellable = true;
}
/**
* addClickAction:
*
* @param {Clutter.ClickAction} action - click action to add to draggable actor
*
* Add @action to the draggable's actor, and set it up so that it does not
* impede drag operations.
*/
addClickAction(action) {
action.connect('clicked', () => (this._actionClicked = true));
action.connect('long-press', (a, actor, state) => {
if (state !== Clutter.LongPressState.CANCEL)
return true;
const event = Clutter.get_current_event();
this._dragTouchSequence = event.get_event_sequence();
if (this._longPressLater)
return true;
// A click cancels a long-press before any click handler is
// run - make sure to not start a drag in that case
const laters = global.compositor.get_laters();
this._longPressLater = laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
delete this._longPressLater;
if (this._actionClicked) {
delete this._actionClicked;
return GLib.SOURCE_REMOVE;
}
action.release();
this.startDrag(
...action.get_coords(),
event.get_time(),
this._dragTouchSequence,
event.get_device());
return GLib.SOURCE_REMOVE;
});
return true;
});
this.actor.add_action(action);
}
_onButtonPress(actor, event) {
if (event.get_button() != 1)
return Clutter.EVENT_PROPAGATE;

View File

@ -98,15 +98,10 @@ var WindowPreview = GObject.registerClass({
this._updateAttachedDialogs();
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', () => this._activate());
clickAction.connect('long-press', this._onLongPress.bind(this));
this.add_action(clickAction);
this.connect('destroy', this._onDestroy.bind(this));
this._draggable = DND.makeDraggable(this, {
restoreOnSuccess: true,
manualMode: true,
dragActorMaxSize: WINDOW_DND_SIZE,
dragActorOpacity: DRAGGING_WINDOW_OPACITY,
});
@ -115,7 +110,16 @@ var WindowPreview = GObject.registerClass({
this._draggable.connect('drag-end', this._onDragEnd.bind(this));
this.inDrag = false;
this._selected = false;
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', () => this._activate());
clickAction.connect('long-press', (action, actor, state) => {
if (state === Clutter.LongPressState.ACTIVATE)
this.showOverlay(true);
return true;
});
this._draggable.addClickAction(clickAction);
this._overlayEnabled = true;
this._overlayShown = false;
this._closeRequested = false;
@ -545,7 +549,6 @@ var WindowPreview = GObject.registerClass({
}
_activate() {
this._selected = true;
this.emit('selected', global.get_current_time());
}
@ -607,36 +610,6 @@ var WindowPreview = GObject.registerClass({
return super.vfunc_key_press_event(keyEvent);
}
_onLongPress(action, actor, state) {
// Take advantage of the Clutter policy to consider
// a long-press canceled when the pointer movement
// exceeds dnd-drag-threshold to manually start the drag
if (state == Clutter.LongPressState.CANCEL) {
let event = Clutter.get_current_event();
this._dragTouchSequence = event.get_event_sequence();
if (this._longPressLater)
return true;
// A click cancels a long-press before any click handler is
// run - make sure to not start a drag in that case
const laters = global.compositor.get_laters();
this._longPressLater = laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
delete this._longPressLater;
if (this._selected) {
this._selected = false;
return;
}
let [x, y] = action.get_coords();
action.release();
this._draggable.startDrag(x, y, global.get_current_time(), this._dragTouchSequence, event.get_device());
});
} else {
this.showOverlay(true);
}
return true;
}
_restack() {
// We may not have a parent if DnD completed successfully, in
// which case our clone will shortly be destroyed and replaced