Drag & Drop: add overlapping feature.

This feature is essential if two overlapping widgets can receive drop
information.
Until now, if two widgets (e.g background and button) were added as drop
targets, pointing to common coordinates would have given priority to the
first inserted as drop target.
Now, it will determine which widget is supposed to receive this drop
target by using the same mechanism as used for mouse move, i.e by
walking on the objects tree whose pointer passes through.

A test has been added (Overlapping DnD) to show how this feature can be
used. You can drop in bg, box and button.
This commit is contained in:
Daniel Zaoui 2013-10-13 06:56:04 +03:00
parent b8000c98cd
commit 883ed0d646
3 changed files with 212 additions and 39 deletions

View File

@ -228,6 +228,7 @@ void test_web_mobile(void *data, Evas_Object *obj, void *event_info);
void test_dnd_genlist_default_anim(void *data, Evas_Object *obj, void *event_info);
void test_dnd_genlist_user_anim(void *data, Evas_Object *obj, void *event_info);
void test_dnd_genlist_gengrid(void *data, Evas_Object *obj, void *event_info);
void test_dnd_overlapping(void *data, Evas_Object *obj, void *event_info);
void test_task_switcher(void *data, Evas_Object *obj, void *event_info);
void test_application_server_message(void *data, Evas_Object *obj, void *event_info);
void test_application_server_phone(void *data, Evas_Object *obj, void *event_info);
@ -791,6 +792,7 @@ add_tests:
ADD_TEST(NULL, "Drag & Drop", "Genlist DnD Dflt Anim", test_dnd_genlist_default_anim);
ADD_TEST(NULL, "Drag & Drop", "Genlist DnD User Anim", test_dnd_genlist_user_anim);
ADD_TEST(NULL, "Drag & Drop", "Genlist-Gengrid DnD", test_dnd_genlist_gengrid);
ADD_TEST(NULL, "Drag & Drop", "Overlapping DnD", test_dnd_overlapping);
//------------------------------//
ADD_TEST(NULL, "Miscellaneous", "Copy And Paste", test_cnp);

View File

@ -89,7 +89,7 @@ _gl_item_getcb(Evas_Object *obj, Evas_Coord x, Evas_Coord y, int *xposret EINA_U
gli = elm_genlist_at_xy_item_get(obj, x, y, yposret);
if (gli)
printf("over <%s>, gli=<%p> yposret %i\n",
elm_object_item_part_text_get(gli, "elm.text"), gli, *yposret);
(char *)elm_object_item_data_get(gli), gli, *yposret);
else
printf("over none, yposret %i\n", *yposret);
return gli;
@ -103,7 +103,7 @@ _grid_item_getcb(Evas_Object *obj, Evas_Coord x, Evas_Coord y, int *xposret, int
item = elm_gengrid_at_xy_item_get(obj, x, y, xposret, yposret);
if (item)
printf("over <%s>, item=<%p> xposret %i yposret %i\n",
elm_object_item_part_text_get(item, "elm.text"), item, *xposret, *yposret);
(char *)elm_object_item_data_get(item), item, *xposret, *yposret);
else
printf("over none, xposret %i yposret %i\n", *xposret, *yposret);
return item;
@ -490,7 +490,7 @@ _gl_get_drag_data(Evas_Object *obj, Elm_Object_Item *it, Eina_List **items)
EINA_LIST_FOREACH(*items, l, it)
{
t = elm_object_item_part_text_get(it, "elm.text");
t = (char *)elm_object_item_data_get(it);
if (t)
len += strlen(t);
}
@ -500,7 +500,7 @@ _gl_get_drag_data(Evas_Object *obj, Elm_Object_Item *it, Eina_List **items)
EINA_LIST_FOREACH(*items, l, it)
{
t = elm_object_item_part_text_get(it, "elm.text");
t = (char *)elm_object_item_data_get(it);
if (t)
{
strcat((char *) drag_data, "#");
@ -537,7 +537,7 @@ _grid_get_drag_data(Evas_Object *obj, Elm_Object_Item *it, Eina_List **items)
EINA_LIST_FOREACH(*items, l, it)
{
t = elm_object_item_part_text_get(it, "elm.text");
t = (char *)elm_object_item_data_get(it);
if (t)
len += strlen(t);
}
@ -547,7 +547,7 @@ _grid_get_drag_data(Evas_Object *obj, Elm_Object_Item *it, Eina_List **items)
EINA_LIST_FOREACH(*items, l, it)
{
t = elm_object_item_part_text_get(it, "elm.text");
t = (char *)elm_object_item_data_get(it);
if (t)
{
strcat((char *) drag_data, "#");
@ -892,3 +892,144 @@ test_dnd_genlist_gengrid(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, v
evas_object_show(win);
}
static Eina_Bool _drop_box_button_new_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev)
{
Evas_Object *win = data;
char *p = strchr(ev->data, '#');
while(p)
{
p++;
char *p2 = strchr(p, '#');
if (p2)
{
*p2 = '\0';
Evas_Object *ic = elm_icon_add(win);
elm_image_file_set(ic, p, NULL);
evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
Evas_Object *bt = elm_button_add(win);
elm_object_text_set(bt, "Dropped button");
elm_object_part_content_set(bt, "icon", ic);
elm_box_pack_end(obj, bt);
evas_object_show(bt);
evas_object_show(ic);
p = p2;
}
else p = NULL;
}
return EINA_TRUE;
}
static Eina_Bool _drop_but_icon_change_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev)
{
Evas_Object *win = data;
Evas_Object *ic = elm_icon_add(win);
char *p = strchr(ev->data, '#');
p++;
char *p2 = strchr(p, '#');
*p2 = '\0';
elm_image_file_set(ic, p, NULL);
evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
evas_object_del(elm_object_part_content_get(obj, "icon"));
elm_object_part_content_set(obj, "icon", ic);
evas_object_show(ic);
return EINA_TRUE;
}
static Eina_Bool _drop_bg_change_cb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Selection_Data *ev)
{
char *p = strchr(ev->data, '#');
p++;
char *p2 = strchr(p, '#');
*p2 = '\0';
elm_bg_file_set(obj, p, NULL);
return EINA_TRUE;
}
void
test_dnd_overlapping(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
char buf[PATH_MAX];
Evas_Object *win, *bxx, *bg;
int i;
win = elm_win_util_standard_add("dnd-overlapping", "DnD-Overlapping");
elm_win_autodel_set(win, EINA_TRUE);
bg = elm_bg_add(win);
evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_drop_target_add(bg, ELM_SEL_FORMAT_TARGETS, NULL, NULL, NULL, NULL, NULL, NULL, _drop_bg_change_cb, NULL);
elm_win_resize_object_add(win, bg);
/* And show the background. */
evas_object_show(bg);
bxx = elm_box_add(win);
elm_box_horizontal_set(bxx, EINA_TRUE);
evas_object_size_hint_weight_set(bxx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, bxx);
evas_object_show(bxx);
{
Evas_Object *grid = elm_gengrid_add(bxx);
evas_object_smart_callback_add(win, "delete,request", _win_del, grid);
elm_gengrid_item_size_set(grid,
elm_config_scale_get() * 100,
elm_config_scale_get() * 100);
elm_gengrid_horizontal_set(grid, EINA_FALSE);
elm_gengrid_reorder_mode_set(grid, EINA_FALSE);
elm_gengrid_multi_select_set(grid, EINA_TRUE); /* We allow multi drag */
evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, EVAS_HINT_FILL);
gic = elm_gengrid_item_class_new();
gic->item_style = "default";
gic->func.text_get = gl_text_get;
gic->func.content_get = gl_content_get;
elm_drag_item_container_add(grid, ANIM_TIME, DRAG_TIMEOUT,
_grid_item_getcb, _grid_data_getcb);
for (i = 0; i < 10; i++)
{
snprintf(buf, sizeof(buf), "%s/images/%s", elm_app_data_dir_get(), img[(i % 9)]);
const char *path = eina_stringshare_add(buf);
elm_gengrid_item_append(grid, gic, path, NULL, NULL);
}
elm_box_pack_end(bxx, grid);
evas_object_show(grid);
}
{
Evas_Object *ic, *bt;
Evas_Object *vert_box = elm_box_add(bxx);
evas_object_size_hint_weight_set(vert_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_box_pack_end(bxx, vert_box);
evas_object_show(vert_box);
elm_drop_target_add(vert_box, ELM_SEL_FORMAT_TARGETS, NULL, NULL, NULL, NULL, NULL, NULL, _drop_box_button_new_cb, win);
ic = elm_icon_add(win);
snprintf(buf, sizeof(buf), "%s/images/logo_small.png", elm_app_data_dir_get());
elm_image_file_set(ic, buf, NULL);
evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
bt = elm_button_add(win);
elm_object_text_set(bt, "Drop into me to change my icon");
elm_drop_target_add(bt, ELM_SEL_FORMAT_TARGETS, NULL, NULL, NULL, NULL, NULL, NULL, _drop_but_icon_change_cb, win);
elm_object_part_content_set(bt, "icon", ic);
elm_box_pack_end(vert_box, bt);
evas_object_show(bt);
evas_object_show(ic);
ic = elm_icon_add(win);
snprintf(buf, sizeof(buf), "%s/images/logo_small.png", elm_app_data_dir_get());
elm_image_file_set(ic, buf, NULL);
evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
bt = elm_button_add(win);
elm_object_text_set(bt, "No action on drop");
elm_object_part_content_set(bt, "icon", ic);
elm_object_disabled_set(bt, EINA_TRUE);
elm_box_pack_end(vert_box, bt);
evas_object_show(bt);
evas_object_show(ic);
}
evas_object_resize(win, 680, 800);
evas_object_show(win);
}

View File

@ -7,9 +7,9 @@
# include <sys/mman.h>
#endif
//#define DEBUGON 1
#define DEBUGON 1
#ifdef DEBUGON
# define cnp_debug(x...) fprintf(stderr, __FILE__": " x)
# define cnp_debug(fmt, args...) fprintf(stderr, __FILE__":%s : " fmt , __FUNCTION__, ##args)
#else
# define cnp_debug(x...) do { } while (0)
#endif
@ -1136,21 +1136,52 @@ _x11_dropable_find(Ecore_X_Window win)
static Dropable *
_x11_dropable_geom_find(Ecore_X_Window win, Evas_Coord px, Evas_Coord py)
{
Eina_List *l;
Dropable *dropable;
Evas_Coord x, y, w, h;
Eina_List *itr, *top_objects_list = NULL;
Evas *evas = NULL;
Evas_Object *top_obj;
Dropable *dropable = NULL;
if (!drops) return NULL;
EINA_LIST_FOREACH(drops, l, dropable)
/* Find the Evas connected to the window */
EINA_LIST_FOREACH(drops, itr, dropable)
{
if (_x11_elm_widget_xwin_get(dropable->obj) == win)
{
evas_object_geometry_get(dropable->obj, &x, &y, &w, &h);
if ((px >= x) && (py >= y) && (px < (x + w)) && (py < (y + h)))
return dropable;
evas = evas_object_evas_get(dropable->obj);
break;
}
}
return NULL;
if (!evas) return NULL;
/* We retrieve the (non-smart) objects pointed by (px, py) */
top_objects_list = evas_tree_objects_at_xy_get(evas, NULL, px, py);
/* We walk on this list from the last because if the list contains more than one
* element, all but the last will repeat events. The last one can repeat events
* or not. Anyway, this last one is the first that has to be taken into account
* for the determination of the drop target.
*/
EINA_LIST_REVERSE_FOREACH(top_objects_list, itr, top_obj)
{
Evas_Object *object = top_obj;
/* We search for the dropable data into the object. If not found, we search into its parent.
* For example, if a button is a drop target, the first object will be an (internal) image.
* The drop target is attached to the button, i.e to image's parent. That's why we need to
* walk on the parents until NULL.
* If we find this dropable data, we found our drop target.
*/
while (object)
{
eo_do(object, eo_base_data_get("__elm_dropable", (void **)&dropable));
if (dropable)
goto end;
else
object = evas_object_smart_parent_get(object);
}
}
end:
eina_list_free(top_objects_list);
if (dropable) cnp_debug("Drop target %p of type %s found\n", dropable->obj, eo_class_name_get(eo_class_get(dropable->obj)));
return dropable;
}
static void
@ -1352,7 +1383,7 @@ _x11_dnd_position(void *data __UNUSED__, int etype __UNUSED__, void *ev)
else
{
ecore_x_dnd_send_status(EINA_FALSE, EINA_FALSE, rect, pos->action);
cnp_debug("dnd position not in obj\n");
cnp_debug("dnd position (%d, %d) not in obj\n", x, y);
_x11_dnd_dropable_handle(dropable_old, 0, 0, EINA_FALSE,
act);
// CCCCCCC: call dnd exit on last obj
@ -1459,7 +1490,7 @@ found:
snprintf(entrytag, len + 1, tagstring, savedtypes.imgfile);
ddata.data = entrytag;
cnp_debug("Insert %s\n", (char *)ddata.data);
dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
if (dropable->dropcb) dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
ecore_x_dnd_send_finished();
if (savedtypes.imgfile) free(savedtypes.imgfile);
savedtypes.imgfile = NULL;
@ -1810,6 +1841,7 @@ _x11_elm_drop_target_add(Evas_Object *obj, Elm_Sel_Format format,
/* Create new drop */
drop = calloc(1, sizeof(Dropable));
if (!drop) return EINA_FALSE;
/* FIXME: Check for eina's deranged error method */
drops = eina_list_append(drops, drop);
@ -1829,6 +1861,7 @@ _x11_elm_drop_target_add(Evas_Object *obj, Elm_Sel_Format format,
drop->types = format;
drop->obj = obj;
eo_do(obj, eo_base_data_set("__elm_dropable", drop, NULL));
evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
/* I love C and varargs */
(Evas_Object_Event_Cb)elm_drop_target_del,
@ -1856,28 +1889,25 @@ _x11_elm_drop_target_add(Evas_Object *obj, Elm_Sel_Format format,
static Eina_Bool
_x11_elm_drop_target_del(Evas_Object *obj)
{
Dropable *drop, *del, *dropable;
Eina_List *item, *l;
Dropable *dropable;
Eina_List *l;
Ecore_X_Window xwin;
Eina_Bool have_drops = EINA_FALSE;
_x11_elm_cnp_init();
del = NULL;
EINA_LIST_FOREACH(drops, item, drop)
eo_do(obj, eo_base_data_get("__elm_dropable", (void **)&dropable));
if (dropable)
{
if (drop->obj == obj)
{
drops = eina_list_remove_list(drops, item);
del = drop;
break;
}
drops = eina_list_remove(drops, dropable);
eo_do(obj, eo_base_data_del("__elm_dropable"));
free(dropable);
dropable = NULL;
}
if (!del) return EINA_FALSE;
else return EINA_FALSE;
evas_object_event_callback_del(obj, EVAS_CALLBACK_FREE,
(Evas_Object_Event_Cb)elm_drop_target_del);
if (del) free(del);
/* TODO BUG: we should handle dnd-aware per window, not just the last that reelased it */
@ -3589,7 +3619,7 @@ _cont_drag_done_cb(void *data, Evas_Object *obj __UNUSED__)
static Eina_Bool
_cont_obj_drag_start(void *data)
{ /* Start a drag-action when timer expires */
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
Item_Container_Drag_Info *st = data;
st->tm = NULL;
Elm_Drag_User_Info *info = &st->user_info;
@ -3646,7 +3676,7 @@ _anim_icons_make(Eina_List *icons)
static Eina_Bool
_drag_anim_play(void *data, double pos)
{ /* Impl of the animation of icons, called on frame time */
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
Item_Container_Drag_Info *st = data;
Eina_List *l;
Anim_Icon *sti;
@ -3685,7 +3715,7 @@ _drag_anim_play(void *data, double pos)
static inline Eina_Bool
_drag_anim_start(void *data)
{ /* Start default animation */
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
Item_Container_Drag_Info *st = data;
st->tm = NULL;
@ -3707,7 +3737,7 @@ _drag_anim_start(void *data)
static Eina_Bool
_cont_obj_anim_start(void *data)
{ /* Start a drag-action when timer expires */
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
Item_Container_Drag_Info *st = data;
int xposret, yposret; /* Unused */
Elm_Object_Item *it = (st->itemgetcb) ?
@ -3753,7 +3783,7 @@ static void
_cont_obj_mouse_down(void *data, Evas *e, Evas_Object *obj __UNUSED__, void *event_info)
{ /* Launch a timer to start dragging */
Evas_Event_Mouse_Down *ev = event_info;
cnp_debug("%s In - event %X\n", __FUNCTION__, ev->event_flags);
cnp_debug("In - event %X\n", ev->event_flags);
if (ev->button != 1)
return; /* We only process left-click at the moment */
@ -3778,10 +3808,10 @@ static Eina_Bool elm_drag_item_container_del_internal(Evas_Object *obj, Eina_Boo
static void
_cont_obj_mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
{ /* Cancel any drag waiting to start on timeout */
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
if (((Evas_Event_Mouse_Move *)event_info)->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
{
cnp_debug("%s event on hold - have to cancel DnD\n", __FUNCTION__);
cnp_debug("event on hold - have to cancel DnD\n");
Item_Container_Drag_Info *st = data;
evas_object_event_callback_del_full
@ -3794,7 +3824,7 @@ _cont_obj_mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__
_anim_st_free(st);
}
cnp_debug("%s Out\n", __FUNCTION__);
cnp_debug("Out\n");
}
static void
@ -3802,7 +3832,7 @@ _cont_obj_mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
{ /* Cancel any drag waiting to start on timeout */
Item_Container_Drag_Info *st = data;
cnp_debug("%s In\n", __FUNCTION__);
cnp_debug("In\n");
if (((Evas_Event_Mouse_Up *)event_info)->button != 1)
return; /* We only process left-click at the moment */