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 v1:
  - Remove unneeded include. Remove extra newlines. Other minor
    code fixes.

Signed-off-by: Carlos Garnacho <carl...@gnome.org>
Reviewed-by: Jonas Ådahl <jad...@gmail.com>
---
 clients/dnd.c | 92 ++++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 69 insertions(+), 23 deletions(-)

diff --git a/clients/dnd.c b/clients/dnd.c
index ddf3fcc..4b32be5 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_drag_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 = {
@@ -429,9 +483,9 @@ create_drag_source(struct dnd *dnd,
        struct display *display;
        struct wl_compositor *compositor;
        struct wl_buffer *buffer;
-       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 +503,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);
@@ -481,8 +531,7 @@ create_drag_source(struct dnd *dnd,
                                          window_get_wl_surface(dnd->window),
                                          dnd_drag->drag_surface,
                                          serial);
-               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);
 
                dnd_drag->opaque =
                        create_drag_icon(dnd_drag, item, x, y, 1);
@@ -587,8 +636,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;
@@ -699,7 +746,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");
        }
-- 
2.5.0

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to