diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2016-01-15 21:14:27 +0100 |
---|---|---|
committer | Jonas Ådahl <jadahl@gmail.com> | 2016-01-19 12:34:36 +0800 |
commit | f377535aff4f2cac0c529c338c0eafd4d045ff4e (patch) | |
tree | 5bd3bb2f4452fb07780501577cc906fd501a873c | |
parent | b288988e831cee3deb7f8bb1a3f440c86230dd9f (diff) |
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>
-rw-r--r-- | clients/dnd.c | 106 |
1 files changed, 84 insertions, 22 deletions
diff --git a/clients/dnd.c b/clients/dnd.c index d32655de..e6c3147a 100644 --- a/clients/dnd.c +++ b/clients/dnd.c @@ -190,7 +190,7 @@ dnd_redraw_handler(struct widget *widget, void *data) struct dnd *dnd = data; struct rectangle allocation; cairo_t *cr; - cairo_surface_t *surface; + cairo_surface_t *surface, *item_surface; unsigned int i; 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++) { if (!dnd->items[i]) 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]->y + allocation.y); cairo_paint(cr); @@ -266,6 +272,30 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y) 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 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_update_surface(dnd_drag); + dnd_drag_update_cursor(dnd_drag); } static void @@ -326,13 +357,27 @@ data_source_send(void *data, struct wl_data_source *source, } 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); - /* Destroy the item that has been dragged out */ - cairo_surface_destroy(dnd_drag->item->surface); - free(dnd_drag->item); + if (delete_item) { + for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) { + 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); @@ -345,11 +390,13 @@ static void data_source_cancelled(void *data, struct wl_data_source *source) { struct dnd_drag *dnd_drag = data; + struct dnd *dnd = dnd_drag->dnd; /* The 'cancelled' event means that the source is no longer in * use by the drag (or current selection). We need to clean * 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 @@ -361,11 +408,17 @@ static void data_source_dnd_finished(void *data, struct wl_data_source *source) { struct dnd_drag *dnd_drag = data; + struct dnd *dnd = dnd_drag->dnd; + bool delete_item; - /* The operation is already finished, we can destroy all - * related data. - */ - dnd_drag_destroy(dnd_drag); + delete_item = + dnd_drag->dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + + /* 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 @@ -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_update_surface(dnd_drag); + dnd_drag_update_cursor(dnd_drag); } static const struct wl_data_source_listener data_source_listener = { @@ -432,6 +486,7 @@ create_drag_source(struct dnd *dnd, unsigned int i; uint32_t serial; cairo_surface_t *icon; + uint32_t actions; widget_get_allocation(dnd->widget, &allocation); 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->mime_type = NULL; - for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) { - if (item == dnd->items[i]){ - dnd->items[i] = 0; - break; - } - } + actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; display = window_get_display(dnd->window); compositor = display_get_compositor(display); @@ -462,6 +513,21 @@ create_drag_source(struct dnd *dnd, dnd_drag->drag_surface = 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) { dnd_drag->data_source = NULL; } else { @@ -478,8 +544,7 @@ create_drag_source(struct dnd *dnd, if (display_get_data_device_manager_version(display) >= WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { - wl_data_source_set_actions(dnd_drag->data_source, - WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + wl_data_source_set_actions(dnd_drag->data_source, actions); } 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 pointer *new_pointer = malloc(sizeof *new_pointer); - dnd->current_drag = NULL; - if (new_pointer) { new_pointer->input = input; 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.y_offset = dnd->current_drag->y_offset; dnd_receive_func(&message, sizeof message, x, y, dnd); - dnd->current_drag = NULL; } else { fprintf(stderr, "ignoring drop from another client\n"); } |