Rotary_selector: Add drag and drop function on edit mode 72/144772/2
authorThiep Ha <thiep.ha@samsung.com>
Fri, 18 Aug 2017 01:25:35 +0000 (10:25 +0900)
committerThiep Ha <thiep.ha@samsung.com>
Fri, 18 Aug 2017 01:57:36 +0000 (10:57 +0900)
Change-Id: I62b375ef70f7e77f58b9a6c74604a1f48aab0b96

inc/wearable/efl_extension_common_private.h
src/wearable/efl_extension_rotary_selector.c

index 7562ba0f81b9d45ed56e02841c3fbbaf61f15b38..3dca83f066d6a329becf61114c0dbfce92cefcd0 100644 (file)
@@ -82,6 +82,12 @@ struct _Eext_Rotary_Selector_Selector_State_Color {
    Eext_Rotary_Selector_Selector_Color disabled_color;
 };
 
+typedef enum
+{
+   EEXT_ROTARY_SELECTOR_DIRECTION_CW = 0,
+   EEXT_ROTARY_SELECTOR_DIRECTION_CCW
+} Eext_Rotary_Selector_Direction;
+
 //for accessibility
 typedef enum
 {
@@ -131,16 +137,24 @@ struct _Eext_Rotary_Selector_Data {
    double longpress_timeout;
    Ecore_Timer *editing_timer;
    Ecore_Timer *longpress_timer;
+   Ecore_Timer *stand_timer;
    Ecore_Timer *item_del_timer;
+   Eext_Rotary_Selector_Direction stand_dir;
+   int drag_item_index;
+   int drop_item_index;
+   Eext_Rotary_Selector_Item *drag_item;
    Eext_Rotary_Selector_Item *del_item;
    Evas_Coord downx, downy;
    Evas_Object *next_page;
    Evas_Object *prev_page;
    Evas_Coord page_item_width;
    Evas_Coord page_item_height;
+   Ecore_Animator *drag_item_move_animator;
    Ecore_Animator *del_item_move_animator;
    double item_move_start_time;
    Eina_Bool item_move_started;
+   Eext_Rotary_Selector_Item *tmp_drag_item;
+   Eina_Bool drag_done;
    int del_item_index;
    //for accessibility
    Evas_Object *event_area_access_object;
index 1e41991e1baf2b710a0e6e73170673b546596682..63c34a533d1a3a912ecc4adf5bc45f4c5dd38aa2 100644 (file)
@@ -356,6 +356,462 @@ _del_item_timer_cb(void *data)
    return ECORE_CALLBACK_CANCEL;
 }
 
+static void
+_dnd_enter_cb(void *data, Evas_Object *obj)
+{
+   INF("enter obj: %s", evas_object_type_get(obj));
+}
+
+//FIXME: drop_cb is not called, leave_cb is called: this should be fixed in elm dnd
+static void
+_dnd_leave_cb(void *data, Evas_Object *obj)
+{
+
+   Eext_Rotary_Selector_Data *rsd = data;
+
+   rsd->drag_done = EINA_TRUE;
+   if (rsd->stand_timer)
+     {
+        ecore_timer_del(rsd->stand_timer);
+        rsd->stand_timer = NULL;
+     }
+   if (rsd->drop_item_index != _ROTARY_SELECTOR_PRESSED_ITEM_INDEX_INVALID &&
+       rsd->drop_item_index != rsd->drag_item_index)
+     {
+        //move item to new pos
+        Eext_Rotary_Selector_Item *item;
+
+        item = eina_list_nth(rsd->item_list, rsd->drag_item_index);
+        rsd->item_list = eina_list_remove(rsd->item_list, item);
+        //delete tmp_item
+        evas_object_del(item->base.obj);
+
+        item = eina_list_nth(rsd->item_list, rsd->drop_item_index);
+        rsd->item_list = eina_list_prepend_relative(rsd->item_list, rsd->drag_item, item);
+        evas_object_show(rsd->drag_item->base.obj);
+
+        _item_rearrange(rsd, rsd->selected_index);
+        _items_transformation_update(rsd);
+        _items_invalidate(rsd);
+
+        _item_update_animation_run(rsd);
+     }
+   else
+     {
+        //add back
+        if (rsd->drag_item)
+          {
+             Eext_Rotary_Selector_Item *tmp_item;
+
+             tmp_item = eina_list_nth(rsd->item_list, rsd->drag_item_index);
+             rsd->item_list = eina_list_prepend_relative(rsd->item_list, rsd->drag_item, tmp_item);
+             rsd->item_list = eina_list_remove(rsd->item_list, tmp_item);
+
+             evas_object_show(rsd->drag_item->base.obj);
+
+             _item_rearrange(rsd, rsd->selected_index);
+             _items_transformation_update(rsd);
+             _items_invalidate(rsd);
+             _item_update_animation_run(rsd);
+             //delete tmp_item
+             evas_object_del(tmp_item->base.obj);
+          }
+        else
+          {
+             ERR("drag item is NULL");
+          }
+     }
+}
+
+static Eina_Bool
+_page_move(void *data)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+
+   if (rsd->stand_dir == EEXT_ROTARY_SELECTOR_DIRECTION_CW)
+     {
+        _page_animation_run(rsd, EINA_FALSE);
+        _selector_update(rsd, EINA_TRUE, EINA_TRUE);
+     }
+   else
+     {
+        _page_animation_run(rsd, EINA_TRUE);
+        _selector_update(rsd, EINA_TRUE, EINA_TRUE);
+     }
+   rsd->stand_timer = NULL;
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_drag_item_move_animator_cb(void *data)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+   Eina_List *l;
+   Eext_Rotary_Selector_Item *item;
+   int i = 0;
+   double p = 0.0f, dt;
+   int idx1 = rsd->drag_item_index;
+   int idx2 = rsd->drop_item_index;
+   Eina_Bool reverse = EINA_FALSE;
+   int first_index = rsd->current_page * _ROTARY_SELECTOR_PAGE_ITEM_MAX;
+   int last_index = 0;
+   double v[4] = {_ROTARY_SELECTOR_CUBIC_BEZIER_P1_X, _ROTARY_SELECTOR_CUBIC_BEZIER_P1_Y,
+                  _ROTARY_SELECTOR_CUBIC_BEZIER_P2_X, _ROTARY_SELECTOR_CUBIC_BEZIER_P2_Y};
+
+   if (rsd->drag_done) return ECORE_CALLBACK_CANCEL;
+
+   last_index = (rsd->current_page + 1) * _ROTARY_SELECTOR_PAGE_ITEM_MAX - 1;
+   if (idx1 > idx2)
+     {
+        int tmp = idx1;
+        idx1 = idx2;
+        idx2 = tmp;
+        reverse = EINA_TRUE;
+
+        if (idx2 > last_index)
+          {
+             idx2 = last_index;
+             item = eina_list_nth(rsd->item_list, last_index);
+             evas_object_hide(item->base.obj);
+          }
+     }
+   else
+     {
+        if (idx1 < first_index)
+          {
+             idx1 = first_index;
+             //tmp_drag_item was removed from the list, must subtract 1
+             item = eina_list_nth(rsd->item_list, first_index - 1);
+             evas_object_hide(item->base.obj);
+          }
+     }
+
+   if (rsd->item_move_started)
+     {
+        rsd->item_move_started = EINA_FALSE;
+        rsd->item_move_start_time = ecore_time_unix_get();
+     }
+   dt = ecore_time_unix_get() - rsd->item_move_start_time;
+   p = ecore_animator_pos_map_n(dt / _ROTARY_SELECTOR_DRAG_ANIMATION_DURATION,
+                                ECORE_POS_MAP_CUBIC_BEZIER, 4, v);
+
+   if (p <= 1.0f)
+     {
+        EINA_LIST_FOREACH(rsd->item_list, l, item)
+          {
+             if (i >= idx1 && i < idx2)
+               {
+                  Evas_Object *button;
+                  Eext_Rotary_Selector_Item_Coords *coords = NULL;
+                  const float endAngle[] = { 300.f, 330.f, 0.f, 30.f, 60.f, 90.f,
+                                             120.f, 150.f, 180.f, 210.f, 240.f };
+                  float from = endAngle[item->index % _ROTARY_SELECTOR_PAGE_ITEM_MAX];
+                  float to = from - 30;
+                  int new_a;
+
+                  if (reverse)
+                    to = from + 30;
+
+                  button = item->base.obj;
+                  coords = &item->coords;
+                  _circle_path_interpolator(rsd, p, from, to, rsd->radius, rsd->radius,
+                                            &coords->x, &coords->y, EINA_FALSE);
+                  new_a = p * 255;
+                  evas_object_move(button, coords->x, coords->y);
+                  evas_object_color_set(button, new_a, new_a, new_a, new_a);
+                  evas_object_show(button);
+               }
+
+             i++;
+          }
+     }
+   if (p >= 1.0f)
+     {
+        Eext_Rotary_Selector_Item *pos_item;
+
+        evas_object_show(rsd->tmp_drag_item->base.obj);
+        pos_item = eina_list_nth(rsd->item_list, rsd->drop_item_index);
+        rsd->item_list = eina_list_prepend_relative(rsd->item_list, rsd->tmp_drag_item,
+                                                    pos_item);
+        _item_rearrange(rsd, rsd->selected_index);
+        _items_transformation_update(rsd);
+        _items_invalidate(rsd);
+        rsd->drag_item_index = eina_list_data_idx(rsd->item_list, rsd->tmp_drag_item);
+
+        return ECORE_CALLBACK_CANCEL;
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_drag_item_move(void *data)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+
+   rsd->stand_timer = NULL;
+   rsd->tmp_drag_item = eina_list_nth(rsd->item_list, rsd->drag_item_index);
+   if (!rsd->tmp_drag_item)
+     return ECORE_CALLBACK_CANCEL;
+
+   evas_object_hide(rsd->tmp_drag_item->base.obj);
+   rsd->item_list = eina_list_remove(rsd->item_list, rsd->tmp_drag_item);
+   rsd->item_move_start_time = ecore_loop_time_get();
+   rsd->item_move_started = EINA_TRUE;
+   rsd->drag_item_move_animator = ecore_animator_add(_drag_item_move_animator_cb, rsd);
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_dnd_pos_cb(void *data, Evas_Object *obj, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+
+   const double center_x = _ROTARY_SELECTOR_SCREEN_WIDTH / 2.0f;
+   const double center_y = _ROTARY_SELECTOR_SCREEN_HEIGHT / 2.0f;
+
+   rsd->drop_item_index = _ROTARY_SELECTOR_PRESSED_ITEM_INDEX_INVALID;
+   int point = sqrt(pow(center_x - x, 2) + pow(center_y - y, 2));
+   if (point > _ROTARY_SELECTOR_SELECTOR_TOUCH_AREA_WIDTH / 2)
+     {
+        double angle = _mouse_event_angle_get(x, y);
+        if (angle < 20.f)
+          {
+             if (rsd->current_page > 0)
+               {
+                  if (rsd->stand_timer == NULL)
+                    {
+                       rsd->stand_dir = EEXT_ROTARY_SELECTOR_DIRECTION_CW;
+                       rsd->stand_timer = ecore_timer_add(rsd->longpress_timeout,
+                                                          _page_move, rsd);
+                    }
+               }
+          }
+        else if (angle > 340.f)
+          {
+             int max_page = rsd->item_count / _ROTARY_SELECTOR_PAGE_ITEM_MAX;
+             if ((rsd->current_page + 1) * _ROTARY_SELECTOR_PAGE_ITEM_MAX < rsd->item_count)
+               {
+                  if (rsd->stand_timer == NULL)
+                    {
+                       rsd->stand_dir = EEXT_ROTARY_SELECTOR_DIRECTION_CCW;
+                       rsd->stand_timer = ecore_timer_add(rsd->longpress_timeout,
+                                                          _page_move, rsd);
+                    }
+               }
+          }
+        else
+          {
+             int i = 0, new_index;
+             int first_index = rsd->current_page * _ROTARY_SELECTOR_PAGE_ITEM_MAX;
+             const float touchAreaAngle[] = {45.f, 75.f, 105.f, 135.f, 165.f, 195.f,
+                                                   225.f, 255.f, 285.f, 315.f, 340.f};
+
+             //cancel stand timer
+             if (rsd->stand_timer)
+               {
+                  ecore_timer_del(rsd->stand_timer);
+                  rsd->stand_timer = NULL;
+               }
+             for(i = 0; i < _ROTARY_SELECTOR_PAGE_ITEM_MAX; i++)
+               {
+                  if(angle < touchAreaAngle[i]) break;
+               }
+             new_index = first_index + i;
+             rsd->drop_item_index = new_index;
+             if (new_index != rsd->drag_item_index)
+               {
+                  rsd->stand_timer = ecore_timer_add(rsd->longpress_timeout,
+                                                     _drag_item_move, rsd);
+               }
+          }
+     }
+   else
+     {
+        //cancel stand timer
+        if (rsd->stand_timer)
+          {
+             ecore_timer_del(rsd->stand_timer);
+             rsd->stand_timer = NULL;
+          }
+     }
+}
+
+static Eina_Bool
+_dnd_drop_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev)
+{
+   return EINA_TRUE; //FIXME: fix elm dnd and move dnd_drop_leave_cb to here
+}
+
+static Evas_Object *
+_image_create_icon(void *data, Evas_Object *parent, Evas_Coord *xoff, Evas_Coord *yoff)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+   Eext_Rotary_Selector_Item *item, *tmp_item;
+   int start_idx;
+   const char *file, *group;
+   Evas_Object *clone_img = NULL;
+   Evas_Object *icon, *item_icon, *action_bg;
+
+   if (!rsd)
+     {
+        ERR("failed");
+        return NULL;
+     }
+   //FIXME: xoff, yoff does not work
+   if (xoff) *xoff = rsd->item_width / 2;
+   if (yoff) *yoff = rsd->item_height / 2;
+
+
+   tmp_item = _item_create(rsd);
+   if (!tmp_item)
+     return NULL;
+   evas_object_show(tmp_item->base.obj);
+
+   item = eina_list_nth(rsd->item_list, rsd->pressed_item_index);
+   rsd->drag_item = item;
+   tmp_item->index = item->index;
+   rsd->item_list = eina_list_prepend_relative(rsd->item_list, tmp_item, item);
+   rsd->item_list = eina_list_remove(rsd->item_list, item);
+
+   icon = elm_button_add(parent);
+   elm_object_style_set(icon, "rotary_selector_item");
+   evas_object_resize(icon, rsd->item_width, rsd->item_height);
+   evas_object_show(icon);
+
+   action_bg = elm_layout_add(icon);
+   elm_layout_theme_set(action_bg, "rotary_selector", "item", "bg_image");
+   evas_object_show(action_bg);
+   elm_object_part_content_set(icon, "item,bg_image", action_bg);
+
+   start_idx = rsd->current_page * _ROTARY_SELECTOR_PAGE_ITEM_MAX;
+   tmp_item->delete_enabled = EINA_FALSE;
+   if (item->delete_enabled)
+     {
+        if (item->index >= start_idx &&
+            item->index < start_idx + _ROTARY_SELECTOR_PAGE_ITEM_MAX / 2 )
+          {
+             elm_object_signal_emit(icon, "elm,selector,delete,left", "elm");
+          }
+        else
+          {
+             elm_object_signal_emit(icon, "elm,selector,delete,right", "elm");
+          }
+     }
+   else
+     {
+        elm_object_signal_emit(icon, "elm,selector,delete,hide", "elm");
+     }
+
+   item_icon = elm_object_part_content_get(item->base.obj, "item,icon");
+   elm_image_file_get(item_icon, &file, &group);
+
+   if (file || group)
+     {
+        clone_img = elm_image_add(icon);
+        elm_image_file_set(clone_img, file, group);
+        evas_object_show(clone_img);
+     }
+   else
+     {
+        ERR("Cannot get image file path. Item icon only supports elm image");
+     }
+   elm_object_part_content_set(icon, "item,icon", clone_img);
+
+   evas_object_hide(item->base.obj);
+
+   _item_rearrange(rsd, rsd->selected_index);
+   _items_transformation_update(rsd);
+   _items_invalidate(rsd);
+
+#if 0
+   //FIXME: the data of proxy image returns NULL.
+   Evas_Object *proxy;
+   void *img_data;
+   Evas_Object *new_icon;
+
+   proxy = evas_object_image_filled_add(evas_object_evas_get(item_icon));
+   evas_object_image_source_set(proxy, item_icon);
+   evas_object_resize(proxy, rsd->item_width, rsd->item_height);
+   evas_object_move(proxy, 200, 200);
+   evas_object_show(proxy);
+
+   img_data = evas_object_image_data_get(proxy, EINA_FALSE);
+   if (!img_data)
+     ERR("Cannot get image data of proxy");
+   else
+     ERR("Got image data from porxy");
+
+   new_icon = evas_object_image_filled_add(evas_object_evas_get(parent));
+   evas_object_image_data_set(new_icon, img_data);
+   evas_object_resize(new_icon, rsd->item_width, rsd->item_height);
+   evas_object_move(new_icon, 100, 100);
+   evas_object_show(new_icon);
+
+   return new_icon;
+#endif
+
+
+   ERR("done");
+   return icon;
+}
+
+static void
+_drag_accept_cb(void *data, Evas_Object *obj, Eina_Bool accept)
+{
+   //ERR("obj: %s; accept: %d", evas_object_type_get(obj), accept);
+}
+
+static void
+_drag_done_cb(void *data, Evas_Object *obj)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+   if (rsd->drag_done) return;
+   //there was no dnd pos, leave, done at drop side
+   //add back item
+   if (rsd->drag_item)
+     {
+        Eext_Rotary_Selector_Item *tmp_item;
+
+        tmp_item = eina_list_nth(rsd->item_list, rsd->drag_item_index);
+        rsd->item_list = eina_list_prepend_relative(rsd->item_list, rsd->drag_item, tmp_item);
+        rsd->item_list = eina_list_remove(rsd->item_list, tmp_item);
+
+        evas_object_show(rsd->drag_item->base.obj);
+
+        _item_rearrange(rsd, rsd->selected_index);
+        _items_transformation_update(rsd);
+        _items_invalidate(rsd);
+        _item_update_animation_run(rsd);
+        //delete tmp_item
+        evas_object_del(tmp_item->base.obj);
+     }
+   else
+     {
+        ERR("drag item is NULL");
+     }
+}
+
+static Eina_Bool
+_item_longpress_cb(void *data)
+{
+   Eext_Rotary_Selector_Data *rsd = data;
+
+   rsd->on_delete_icon = EINA_FALSE;
+   rsd->drag_done = EINA_FALSE;
+   elm_drag_start(rsd->rotary_selector, ELM_SEL_FORMAT_TARGETS, "1", ELM_XDND_ACTION_COPY,
+                  _image_create_icon, rsd,
+                  NULL, NULL,
+                  _drag_accept_cb, rsd,
+                  _drag_done_cb, rsd);
+
+   rsd->longpress_timer = NULL;
+
+   return ECORE_CALLBACK_CANCEL;
+}
+
 static void
 _event_area_mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
 {
@@ -434,7 +890,15 @@ _event_area_mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_inf
                             elm_object_signal_emit(it->base.obj,
                                        "elm,action,button,delete,selected", "");
                          }
-                  }
+                       rsd->drag_item_index = new_index;
+                       if (rsd->longpress_timer)
+                         {
+                            ecore_timer_del(rsd->longpress_timer);
+                            rsd->longpress_timer = NULL;
+                         }
+                       rsd->longpress_timer = ecore_timer_add(rsd->longpress_timeout,
+                                                              _item_longpress_cb, rsd);
+                    }
                   else
                     {
                        _item_touched_signal_send(rsd, rsd->pressed_item_index, EINA_TRUE, EINA_FALSE);
@@ -1167,11 +1631,21 @@ _rotary_selector_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
         ecore_timer_del(rsd->longpress_timer);
         rsd->longpress_timer = NULL;
      }
+   if (rsd->stand_timer)
+     {
+        ecore_timer_del(rsd->stand_timer);
+        rsd->stand_timer = NULL;
+     }
    if (rsd->item_del_timer)
      {
         ecore_timer_del(rsd->item_del_timer);
         rsd->item_del_timer = NULL;
      }
+   if (rsd->drag_item_move_animator)
+     {
+        ecore_animator_del(rsd->drag_item_move_animator);
+        rsd->drag_item_move_animator = NULL;
+     }
    if (rsd->del_item_move_animator)
      {
         ecore_animator_del(rsd->del_item_move_animator);
@@ -2292,7 +2766,13 @@ eext_rotary_selector_add(Evas_Object *parent)
                                   _rotary_selector_mouse_up_cb, rsd);
 
    rsd->longpress_timeout = elm_config_longpress_timeout_get();
-   rsd->longpress_timeout = elm_config_longpress_timeout_get();   _event_area_callback_add(rsd);
+   elm_drop_target_add(rotary_selector, ELM_SEL_FORMAT_TEXT,
+                       _dnd_enter_cb, rsd,
+                       _dnd_leave_cb, rsd,
+                       _dnd_pos_cb, rsd,
+                       _dnd_drop_cb, rsd);
+
+   _event_area_callback_add(rsd);
 
    // for accessibility
    evas_object_smart_callback_add(rotary_selector, "atspi,screen,reader,changed", _rotary_selector_screen_reader_changed, rsd);