dnd: Turn into a full blown example
In order to keep things simple, weston-dnd made a few choices that
turn out to be unrealistic, a few tweaks have been done to make it
less of a playground demo:
- It now caters for copy/move operations, instead of just move,
which still remains the default nonetheless.
- As "move" operations are no longer assumed, the item isn't removed
on start_drag, instead it is made translucent until the drag
operation finishes (and we know whether the item is to be
removed after transfer or left as is)
- For the same reasons, "Drop nowhere to delete item" no longer
happens. Drag-and-drop is a failable operation and must not result
in data loss.
- As multiple actions are now allowed, we set the pointer icon
surface accordingly to the current operation.
This makes weston-dnd a better example of what applications usually
want to do here.
Changes since v2:
- Updated to behave alright-ish with version < 3.
Changes since v1:
- Remove unneeded include. Remove extra newlines. Other minor
code fixes.
Signed-off-by: Carlos Garnacho <carlosg@gnome.org>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
This commit is contained in:
committed by
Jonas Ådahl
parent
b288988e83
commit
f377535aff
+84
-22
@@ -190,7 +190,7 @@ dnd_redraw_handler(struct widget *widget, void *data)
|
|||||||
struct dnd *dnd = data;
|
struct dnd *dnd = data;
|
||||||
struct rectangle allocation;
|
struct rectangle allocation;
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
cairo_surface_t *surface;
|
cairo_surface_t *surface, *item_surface;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
surface = window_get_surface(dnd->window);
|
surface = window_get_surface(dnd->window);
|
||||||
@@ -210,7 +210,13 @@ dnd_redraw_handler(struct widget *widget, void *data)
|
|||||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||||
if (!dnd->items[i])
|
if (!dnd->items[i])
|
||||||
continue;
|
continue;
|
||||||
cairo_set_source_surface(cr, dnd->items[i]->surface,
|
|
||||||
|
if (dnd->current_drag && dnd->items[i] == dnd->current_drag->item)
|
||||||
|
item_surface = dnd->current_drag->translucent;
|
||||||
|
else
|
||||||
|
item_surface = dnd->items[i]->surface;
|
||||||
|
|
||||||
|
cairo_set_source_surface(cr, item_surface,
|
||||||
dnd->items[i]->x + allocation.x,
|
dnd->items[i]->x + allocation.x,
|
||||||
dnd->items[i]->y + allocation.y);
|
dnd->items[i]->y + allocation.y);
|
||||||
cairo_paint(cr);
|
cairo_paint(cr);
|
||||||
@@ -266,6 +272,30 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lookup_dnd_cursor(uint32_t dnd_action)
|
||||||
|
{
|
||||||
|
if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
|
||||||
|
return CURSOR_DND_MOVE;
|
||||||
|
else if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
|
||||||
|
return CURSOR_DND_COPY;
|
||||||
|
|
||||||
|
return CURSOR_DND_FORBIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dnd_drag_update_cursor(struct dnd_drag *dnd_drag)
|
||||||
|
{
|
||||||
|
int cursor;
|
||||||
|
|
||||||
|
if (dnd_drag->mime_type == NULL)
|
||||||
|
cursor = CURSOR_DND_FORBIDDEN;
|
||||||
|
else
|
||||||
|
cursor = lookup_dnd_cursor(dnd_drag->dnd_action);
|
||||||
|
|
||||||
|
input_set_pointer_image(dnd_drag->input, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dnd_drag_update_surface(struct dnd_drag *dnd_drag)
|
dnd_drag_update_surface(struct dnd_drag *dnd_drag)
|
||||||
{
|
{
|
||||||
@@ -293,6 +323,7 @@ data_source_target(void *data,
|
|||||||
|
|
||||||
dnd_drag->mime_type = mime_type;
|
dnd_drag->mime_type = mime_type;
|
||||||
dnd_drag_update_surface(dnd_drag);
|
dnd_drag_update_surface(dnd_drag);
|
||||||
|
dnd_drag_update_cursor(dnd_drag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -326,13 +357,27 @@ data_source_send(void *data, struct wl_data_source *source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dnd_drag_destroy(struct dnd_drag *dnd_drag)
|
dnd_drag_destroy(struct dnd_drag *dnd_drag, bool delete_item)
|
||||||
{
|
{
|
||||||
|
struct dnd *dnd = dnd_drag->dnd;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
wl_data_source_destroy(dnd_drag->data_source);
|
wl_data_source_destroy(dnd_drag->data_source);
|
||||||
|
|
||||||
/* Destroy the item that has been dragged out */
|
if (delete_item) {
|
||||||
cairo_surface_destroy(dnd_drag->item->surface);
|
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||||
free(dnd_drag->item);
|
if (dnd_drag->item == dnd->items[i]) {
|
||||||
|
dnd->items[i] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the item that has been dragged out */
|
||||||
|
cairo_surface_destroy(dnd_drag->item->surface);
|
||||||
|
free(dnd_drag->item);
|
||||||
|
}
|
||||||
|
|
||||||
|
dnd->current_drag = NULL;
|
||||||
|
|
||||||
wl_surface_destroy(dnd_drag->drag_surface);
|
wl_surface_destroy(dnd_drag->drag_surface);
|
||||||
|
|
||||||
@@ -345,11 +390,13 @@ static void
|
|||||||
data_source_cancelled(void *data, struct wl_data_source *source)
|
data_source_cancelled(void *data, struct wl_data_source *source)
|
||||||
{
|
{
|
||||||
struct dnd_drag *dnd_drag = data;
|
struct dnd_drag *dnd_drag = data;
|
||||||
|
struct dnd *dnd = dnd_drag->dnd;
|
||||||
|
|
||||||
/* The 'cancelled' event means that the source is no longer in
|
/* The 'cancelled' event means that the source is no longer in
|
||||||
* use by the drag (or current selection). We need to clean
|
* use by the drag (or current selection). We need to clean
|
||||||
* up the drag object created and the local state. */
|
* up the drag object created and the local state. */
|
||||||
dnd_drag_destroy(dnd_drag);
|
dnd_drag_destroy(dnd_drag, false);
|
||||||
|
window_schedule_redraw(dnd->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -361,11 +408,17 @@ static void
|
|||||||
data_source_dnd_finished(void *data, struct wl_data_source *source)
|
data_source_dnd_finished(void *data, struct wl_data_source *source)
|
||||||
{
|
{
|
||||||
struct dnd_drag *dnd_drag = data;
|
struct dnd_drag *dnd_drag = data;
|
||||||
|
struct dnd *dnd = dnd_drag->dnd;
|
||||||
|
bool delete_item;
|
||||||
|
|
||||||
/* The operation is already finished, we can destroy all
|
delete_item =
|
||||||
* related data.
|
dnd_drag->dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||||
*/
|
|
||||||
dnd_drag_destroy(dnd_drag);
|
/* The operation is already finished, we can destroy all
|
||||||
|
* related data.
|
||||||
|
*/
|
||||||
|
dnd_drag_destroy(dnd_drag, delete_item);
|
||||||
|
window_schedule_redraw(dnd->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -375,6 +428,7 @@ data_source_action(void *data, struct wl_data_source *source, uint32_t dnd_actio
|
|||||||
|
|
||||||
dnd_drag->dnd_action = dnd_action;
|
dnd_drag->dnd_action = dnd_action;
|
||||||
dnd_drag_update_surface(dnd_drag);
|
dnd_drag_update_surface(dnd_drag);
|
||||||
|
dnd_drag_update_cursor(dnd_drag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_data_source_listener data_source_listener = {
|
static const struct wl_data_source_listener data_source_listener = {
|
||||||
@@ -432,6 +486,7 @@ create_drag_source(struct dnd *dnd,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
cairo_surface_t *icon;
|
cairo_surface_t *icon;
|
||||||
|
uint32_t actions;
|
||||||
|
|
||||||
widget_get_allocation(dnd->widget, &allocation);
|
widget_get_allocation(dnd->widget, &allocation);
|
||||||
item = dnd_get_item(dnd, x, y);
|
item = dnd_get_item(dnd, x, y);
|
||||||
@@ -449,12 +504,8 @@ create_drag_source(struct dnd *dnd,
|
|||||||
dnd_drag->dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
dnd_drag->dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||||
dnd_drag->mime_type = NULL;
|
dnd_drag->mime_type = NULL;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
|
||||||
if (item == dnd->items[i]){
|
WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||||
dnd->items[i] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display = window_get_display(dnd->window);
|
display = window_get_display(dnd->window);
|
||||||
compositor = display_get_compositor(display);
|
compositor = display_get_compositor(display);
|
||||||
@@ -462,6 +513,21 @@ create_drag_source(struct dnd *dnd,
|
|||||||
dnd_drag->drag_surface =
|
dnd_drag->drag_surface =
|
||||||
wl_compositor_create_surface(compositor);
|
wl_compositor_create_surface(compositor);
|
||||||
|
|
||||||
|
if (display_get_data_device_manager_version(display) <
|
||||||
|
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
|
||||||
|
/* Data sources version < 3 will not get action
|
||||||
|
* nor dnd_finished events, as we can't honor
|
||||||
|
* the "move" action at the time of finishing
|
||||||
|
* drag-and-drop, do it preemptively here.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||||
|
if (item == dnd->items[i]){
|
||||||
|
dnd->items[i] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dnd->self_only) {
|
if (dnd->self_only) {
|
||||||
dnd_drag->data_source = NULL;
|
dnd_drag->data_source = NULL;
|
||||||
} else {
|
} else {
|
||||||
@@ -478,8 +544,7 @@ create_drag_source(struct dnd *dnd,
|
|||||||
|
|
||||||
if (display_get_data_device_manager_version(display) >=
|
if (display_get_data_device_manager_version(display) >=
|
||||||
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
|
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
|
||||||
wl_data_source_set_actions(dnd_drag->data_source,
|
wl_data_source_set_actions(dnd_drag->data_source, actions);
|
||||||
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_data_device_start_drag(input_get_data_device(input),
|
wl_data_device_start_drag(input_get_data_device(input),
|
||||||
@@ -591,8 +656,6 @@ dnd_enter_handler(struct widget *widget,
|
|||||||
struct dnd *dnd = data;
|
struct dnd *dnd = data;
|
||||||
struct pointer *new_pointer = malloc(sizeof *new_pointer);
|
struct pointer *new_pointer = malloc(sizeof *new_pointer);
|
||||||
|
|
||||||
dnd->current_drag = NULL;
|
|
||||||
|
|
||||||
if (new_pointer) {
|
if (new_pointer) {
|
||||||
new_pointer->input = input;
|
new_pointer->input = input;
|
||||||
new_pointer->dragging = false;
|
new_pointer->dragging = false;
|
||||||
@@ -703,7 +766,6 @@ dnd_drop_handler(struct window *window, struct input *input,
|
|||||||
message.x_offset = dnd->current_drag->x_offset;
|
message.x_offset = dnd->current_drag->x_offset;
|
||||||
message.y_offset = dnd->current_drag->y_offset;
|
message.y_offset = dnd->current_drag->y_offset;
|
||||||
dnd_receive_func(&message, sizeof message, x, y, dnd);
|
dnd_receive_func(&message, sizeof message, x, y, dnd);
|
||||||
dnd->current_drag = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "ignoring drop from another client\n");
|
fprintf(stderr, "ignoring drop from another client\n");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user