From 633a499cb23a0b925462eec495421940ad77c6c1 Mon Sep 17 00:00:00 2001 From: SangHyeon Jade Lee Date: Tue, 19 Nov 2019 17:21:15 +0900 Subject: [PATCH] efl_ui: apply circle position manager for wearable Change-Id: If187b2276d7f0b746be2d49a823d12aeb9e5099b --- src/lib/elementary/Efl_Wearable.h | 1 + src/lib/elementary/efl_ui_item.c | 45 ++ src/lib/elementary/efl_ui_item_internal.h | 6 + src/lib/elementary/efl_ui_item_private.h | 4 + .../elementary/efl_ui_position_manager_common.h | 18 + .../efl_wearable_position_manager_circle_list.c | 662 +++++++++++++++++++++ .../efl_wearable_position_manager_circle_list.eo | 24 + src/lib/elementary/meson.build | 7 + 8 files changed, 767 insertions(+) create mode 100644 src/lib/elementary/Efl_Wearable.h create mode 100644 src/lib/elementary/efl_ui_item_internal.h create mode 100644 src/lib/elementary/efl_wearable_position_manager_circle_list.c create mode 100644 src/lib/elementary/efl_wearable_position_manager_circle_list.eo diff --git a/src/lib/elementary/Efl_Wearable.h b/src/lib/elementary/Efl_Wearable.h new file mode 100644 index 0000000..29fa6a0 --- /dev/null +++ b/src/lib/elementary/Efl_Wearable.h @@ -0,0 +1 @@ +#include "efl_wearable_position_manager_circle_list.eo.h" diff --git a/src/lib/elementary/efl_ui_item.c b/src/lib/elementary/efl_ui_item.c index f3c35ac..42ce673 100644 --- a/src/lib/elementary/efl_ui_item.c +++ b/src/lib/elementary/efl_ui_item.c @@ -6,6 +6,9 @@ #include "elm_priv.h" #include "efl_ui_item_private.h" +//TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list +#include "efl_ui_item_internal.h" +// #define MY_CLASS EFL_UI_ITEM_CLASS #define MY_CLASS_PFX efl_ui_item @@ -19,6 +22,48 @@ static const Elm_Action key_actions[] = { {NULL, NULL} }; +//TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list +Efl_Object * +_efl_ui_item_proxy_get(Efl_Object *item) +{ + EFL_UI_ITEM_DATA_GET_OR_RETURN(item, pd, NULL); + return pd->proxy; +} + +Efl_Object * +_efl_ui_item_proxy_new(Efl_Object *item) +{ + EFL_UI_ITEM_DATA_GET_OR_RETURN(item, pd); + pd->proxy = evas_object_image_filled_add(evas_object_evas_get(item)); + //Efl_Object *group = efl_canvas_object_render_parent_get(ent); + //efl_canvas_group_member_add(ent, pd->proxy); + //evas_object_clip_unset(ent); + evas_object_image_source_set(pd->proxy, item); + evas_object_image_source_visible_set(pd->proxy, EINA_FALSE); + evas_object_image_source_events_set(pd->proxy, EINA_TRUE); + evas_object_image_source_clip_set(pd->proxy, EINA_FALSE); + evas_object_repeat_events_set(pd->proxy, EINA_TRUE); + efl_canvas_group_member_add(pd->container, pd->proxy); + efl_gfx_entity_visible_set(pd->proxy, EINA_TRUE); + + return pd->proxy; +} + +void +_efl_ui_item_proxy_enabled_set(Efl_Object *item, Eina_Bool enabled) +{ + EFL_UI_ITEM_DATA_GET_OR_RETURN(item, pd); + pd->proxy_enabled = enabled; +} + +Eina_Bool +_efl_ui_item_proxy_enabled_get(Efl_Object *item) +{ + EFL_UI_ITEM_DATA_GET_OR_RETURN(item, pd); + return pd->proxy_enabled; +} +// + static Eina_Bool _key_action_select(Evas_Object *obj, const char *params EINA_UNUSED) { diff --git a/src/lib/elementary/efl_ui_item_internal.h b/src/lib/elementary/efl_ui_item_internal.h new file mode 100644 index 0000000..34e2a3b --- /dev/null +++ b/src/lib/elementary/efl_ui_item_internal.h @@ -0,0 +1,6 @@ +// TIZEN_ONLY() +Efl_Object *_efl_ui_item_proxy_get(Efl_Object *item); +Efl_Object *_efl_ui_item_proxy_new(Efl_Object *item); +void _efl_ui_item_proxy_enabled_set(Efl_Object *item, Eina_Bool enabled); +Eina_Bool _efl_ui_item_proxy_enabled_get(Efl_Object *item); +// diff --git a/src/lib/elementary/efl_ui_item_private.h b/src/lib/elementary/efl_ui_item_private.h index 5c1ef26..04fcaaf 100644 --- a/src/lib/elementary/efl_ui_item_private.h +++ b/src/lib/elementary/efl_ui_item_private.h @@ -8,6 +8,10 @@ typedef struct _Efl_Ui_Item_Data // Eo Objects Eo *container; /* Parent Widget */ Efl_Ui_Item *parent; + //TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list + Efl_Object *proxy; + Eina_Bool proxy_enabled : 1; + // // Boolean Data Eina_Bool selected : 1; /* State for item selected */ diff --git a/src/lib/elementary/efl_ui_position_manager_common.h b/src/lib/elementary/efl_ui_position_manager_common.h index 6991594..f54205f 100644 --- a/src/lib/elementary/efl_ui_position_manager_common.h +++ b/src/lib/elementary/efl_ui_position_manager_common.h @@ -4,6 +4,9 @@ #include #include #include "efl_ui_position_manager_entity.eo.h" +//TIZEN_ONLY +#include "efl_ui_item_internal.h" +// typedef struct { struct { @@ -103,14 +106,29 @@ vis_change_segment(Api_Callbacks cb, int a, int b, Eina_Bool flag) BATCH_ACCESS_OBJECT(cb, i, MAX(a,b), len, data); } ent = data[buffer_id].entity; + //TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list + Efl_Object *proxy = _efl_ui_item_proxy_get(ent); + // if (ent && !flag && (efl_ui_focus_object_focus_get(ent) || efl_ui_focus_object_child_focus_get(ent))) { //we should not make focused object invisible, rather move it to some parking lot efl_gfx_entity_position_set(ent, EINA_POSITION2D(-9999,-9999)); + //TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list + if (proxy) + { + efl_gfx_entity_position_set(proxy, EINA_POSITION2D(-9999, -9999)); + } + // } if (ent && !efl_ui_focus_object_focus_get(ent)) { efl_gfx_entity_visible_set(ent, flag); + //TIZEN_ONLY(20191121): apply proxy fisheye effect for circle list + if (proxy) + { + efl_gfx_entity_visible_set(proxy, flag); + } + // } } } diff --git a/src/lib/elementary/efl_wearable_position_manager_circle_list.c b/src/lib/elementary/efl_wearable_position_manager_circle_list.c new file mode 100644 index 0000000..d351a14 --- /dev/null +++ b/src/lib/elementary/efl_wearable_position_manager_circle_list.c @@ -0,0 +1,662 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif + + +#include +#include +#include "Efl_Wearable.h" +#include "elm_widget.h" +#include "elm_priv.h" +#include "efl_ui_position_manager_common.h" +//TIZEN_ONLY +#include "efl_ui_item_internal.h" +#define DIMED_ITEM_COLOR 50 +#define ALIGNED_ITEM_SCALE_FACTOR 1.5 +// + +#define MY_CLASS EFL_WEARABLE_POSITION_MANAGER_CIRCLE_LIST_CLASS +#define MY_DATA_GET(obj, pd) \ + Efl_Wearable_Position_Manager_Circle_List_Data *pd = efl_data_scope_get(obj, MY_CLASS); + +typedef struct { + unsigned int size; + Eina_Future *rebuild_absolut_size; + Eina_Rect viewport; + Eina_Size2D abs_size; + Eina_Vector2 scroll_position; + Efl_Ui_Layout_Orientation dir; + int *size_cache; + int average_item_size; + int maximum_min_size; + Vis_Segment prev_run; + Efl_Gfx_Entity *last_group; + Api_Callbacks callbacks; +} Efl_Wearable_Position_Manager_Circle_List_Data; + +/* + * The here used cache is a sum map + * Every element in the cache contains the sum of the previous element, and the size of the current item + * This is usefull as a lookup of all previous items is O(1). + * The tradeoff that makes the cache performant here is, that we only need to walk the whole list of items once in the beginning. + * Every other walk of the items is at max the maximum number of items you get into the maximum distance between the average item size and a actaul item size. + */ + +static void +cache_invalidate(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + if (pd->size_cache) + free(pd->size_cache); + pd->size_cache = NULL; +} + +static void +cache_require(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + unsigned int i; + const int len = 100; + Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len]; + Efl_Ui_Position_Manager_Size_Batch_Result size_result; + + if (pd->size_cache) return; + + if (pd->size == 0) + { + pd->size_cache = NULL; + pd->average_item_size = 0; + return; + } + + pd->size_cache = calloc(pd->size + 1, sizeof(int)); + pd->size_cache[0] = 0; + pd->maximum_min_size = 0; + + for (i = 0; i < pd->size; ++i) + { + Eina_Size2D size; + int step; + int min; + int buffer_id = i % len; + + if (buffer_id == 0) + { + BATCH_ACCESS_SIZE(pd->callbacks, i, pd->size, MIN(len, pd->size - i), EINA_TRUE, size_buffer); + } + size = size_buffer[buffer_id].size; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + step = size.h; + min = size.w; + } + else + { + step = size.w; + min = size.h; + } + pd->size_cache[i + 1] = pd->size_cache[i] + step; + pd->maximum_min_size = MAX(pd->maximum_min_size, min); + /* no point iterating further if size calc can't be done yet */ + //if ((!i) && (!pd->maximum_min_size)) break; + } + pd->average_item_size = pd->size_cache[pd->size]/pd->size; + if ((!pd->average_item_size) && (!pd->maximum_min_size)) + cache_invalidate(obj, pd); +} + +static int +cache_access(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd, unsigned int idx) +{ + EINA_SAFETY_ON_FALSE_RETURN_VAL(idx <= pd->size, 0); + return pd->size_cache[idx]; +} + +static void +recalc_absolut_size(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + Eina_Size2D min_size = EINA_SIZE2D(-1, -1); + Eina_Size2D pabs_size = pd->abs_size; + int pmin_size = pd->maximum_min_size; + + cache_require(obj, pd); + /* deferred */ + if (!pd->size_cache) return; + + pd->abs_size = pd->viewport.size; + + if (pd->size) + { + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + pd->abs_size.h = MAX(cache_access(obj, pd, pd->size), pd->abs_size.h); + else + pd->abs_size.w = MAX(cache_access(obj, pd, pd->size), pd->abs_size.w); + } + if ((pabs_size.w != pd->abs_size.w) || (pabs_size.h != pd->abs_size.h)) + efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_SIZE_CHANGED, &pd->abs_size); + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + min_size.w = pd->maximum_min_size; + } + else + { + min_size.h = pd->maximum_min_size; + } + if ((pd->maximum_min_size > 0) && (pd->maximum_min_size != pmin_size)) + efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size); +} + +static inline Vis_Segment +_search_visual_segment(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, int relevant_space_size, int relevant_viewport) +{ + Vis_Segment cur; + //based on the average item size, we jump somewhere into the sum cache. + //After beeing in there, we are walking back, until we have less space then viewport size + cur.start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size); + for (; cache_access(obj, pd, cur.start_id) >= relevant_space_size && cur.start_id > 0; cur.start_id --) { } + + //starting on the start id, we are walking down until the sum of elements is bigger than the lower part of the viewport. + cur.end_id = cur.start_id; + for (; cur.end_id <= pd->size && cache_access(obj, pd, cur.end_id) <= relevant_space_size + relevant_viewport ; cur.end_id ++) { } + cur.end_id = MAX(cur.end_id, cur.start_id + 1); + cur.end_id = MIN(cur.end_id, pd->size); + + #ifdef DEBUG + printf("space_size %d : starting point : %d : cached_space_starting_point %d end point : %d cache_space_end_point %d\n", space_size.h, cur.start_id, pd->size_cache[cur.start_id], cur.end_id, pd->size_cache[cur.end_id]); + #endif + if (relevant_space_size > 0) + EINA_SAFETY_ON_FALSE_GOTO(cache_access(obj, pd, cur.start_id) <= relevant_space_size, err); + if (cur.end_id != pd->size) + EINA_SAFETY_ON_FALSE_GOTO(cache_access(obj, pd, cur.end_id) >= relevant_space_size + relevant_viewport, err); + EINA_SAFETY_ON_FALSE_GOTO(cur.start_id <= cur.end_id, err); + + return cur; + +err: + cur.start_id = cur.end_id = 0; + + return cur; +} + +static inline void +_position_items(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd, Vis_Segment new, int relevant_space_size) +{ + Efl_Gfx_Entity *first_group = NULL, *first_fully_visual_group = NULL; + Eina_Size2D first_group_size; + Eina_Rect geom; + const int len = 100; + Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[len]; + Efl_Ui_Position_Manager_Size_Batch_Result size_result; + Efl_Ui_Position_Manager_Object_Batch_Entity obj_buffer[len]; + Efl_Ui_Position_Manager_Object_Batch_Result object_result; + unsigned int i; + + //placement of the plain items + geom = pd->viewport; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.y -= (relevant_space_size - cache_access(obj, pd, new.start_id)); + else + geom.x -= (relevant_space_size - cache_access(obj, pd, new.start_id)); + + for (i = new.start_id; i < new.end_id; ++i) + { + Eina_Size2D size; + Efl_Gfx_Entity *ent = NULL; + int buffer_id = (i-new.start_id) % len; + + if (buffer_id == 0) + { + BATCH_ACCESS_SIZE(pd->callbacks, i, new.end_id, len, EINA_FALSE, size_buffer); + BATCH_ACCESS_OBJECT(pd->callbacks, i, new.end_id, len, obj_buffer); + + if (i == new.start_id) + { + first_group = object_result.group; + first_group_size = size_result.parent_size; + if (obj_buffer[0].depth_leader) + { + first_group = obj_buffer[0].entity; + first_group_size = size_buffer[0].size; + } + } + } + + size = size_buffer[buffer_id].size; + ent = obj_buffer[buffer_id].entity; + + int diff = cache_access(obj, pd, i + 1) - cache_access(obj, pd, i); + int real_diff = 0; + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + real_diff = size.h; + else + real_diff = size.w; + if (real_diff != diff) + ERR("Reported sizes changed during caching and placement %d %d %d", i, real_diff, diff); + + if (ent == pd->last_group) + { + pd->last_group = NULL; + } + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.h = size.h; + else + geom.w = size.w; + + if (!first_fully_visual_group && obj_buffer[buffer_id].depth_leader && + eina_spans_intersect(geom.x, geom.w, pd->viewport.x, pd->viewport.w) && + eina_spans_intersect(geom.y, geom.h, pd->viewport.y, pd->viewport.h)) + { + first_fully_visual_group = obj_buffer[buffer_id].entity; + } + + if (ent) + { + const char *signal; + efl_gfx_entity_visible_set(ent, EINA_TRUE); + efl_gfx_entity_geometry_set(ent, geom); + if (i % 2 == 0) + signal = "efl,state,even"; + else + signal = "efl,state,odd"; + efl_layout_signal_emit(ent, signal, "efl"); + // TIZEN ONLY() Circle Fish Eye Effect Implementation + { + Efl_Object *proxy = _efl_ui_item_proxy_get(ent); + int v_center_pos, i_center_pos; + Eina_Rect p_geom; + Eina_Size2D resize; + float ratio; + double scale; + double move_v[4] = {0.49, 0.99, 0.45, 1.0}; + double scale_v[4] = {0.49, 0.14, 1, 0.63}; + + if (!proxy) + { + proxy = _efl_ui_item_proxy_new(ent); + } + v_center_pos = pd->viewport.y + pd->viewport.h / 2; + i_center_pos = geom.y + geom.h / 2; // need to be calculated.. + + if (eina_spans_intersect(geom.x, geom.w, pd->viewport.x, pd->viewport.w) && + eina_spans_intersect(geom.y, geom.h, pd->viewport.y, pd->viewport.h)) + { + efl_gfx_entity_visible_set(proxy, EINA_TRUE); + + //need to have some style based vi effect verification. + ratio = (float)abs(v_center_pos - i_center_pos) / (float)((pd->viewport.h / 2) + geom.h / 2); + double scale_p = ecore_animator_pos_map_n(ratio, ECORE_POS_MAP_CUBIC_BEZIER, 4, scale_v); + scale = elm_config_scale_get() - (scale_p * ALIGNED_ITEM_SCALE_FACTOR); + + resize.w = geom.w * (scale / elm_config_scale_get()); + if (!((geom.w - resize.w) % 2)) + p_geom.w = resize.w; + else + p_geom.w = resize.w + 1; + p_geom.x = geom.x + (geom.w - p_geom.w) / 2; + resize.h = geom.h * (scale / elm_config_scale_get()); + + if (!((geom.h - resize.h) % 2)) + p_geom.h = resize.h; + else + p_geom.h = resize.h + 1; + + if (i_center_pos < v_center_pos) + { + double move_p = ecore_animator_pos_map_n(ratio, ECORE_POS_MAP_CUBIC_BEZIER, 4, move_v); + p_geom.y = geom.y + move_p * (geom.h - p_geom.h); + } + else + p_geom.y = geom.y; + + efl_gfx_entity_geometry_set(proxy, p_geom); + } + else + { + efl_gfx_entity_visible_set(proxy, EINA_FALSE); + + } + + // + } + } + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.y += size.h; + else + geom.x += size.w; + } + //Now place group items + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + first_group_size.w = pd->viewport.w; + else + first_group_size.h = pd->viewport.h; + + //if there is a new group item, display the new one, and hide the old one + if (first_group != pd->last_group) + { + efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE); + efl_gfx_stack_raise_to_top(first_group); + pd->last_group = first_group; + } + //we have to set the visibility again here, as changing the visual segments might overwrite our visibility state + efl_gfx_entity_visible_set(first_group, EINA_TRUE); + + //in case there is another group item coming in, the new group item (which is placed as normal item) moves the group item to the top + Eina_Position2D first_group_pos = EINA_POSITION2D(pd->viewport.x, pd->viewport.y); + if (first_fully_visual_group && first_fully_visual_group != first_group) + { + Eina_Position2D first_visual_group; + first_visual_group = efl_gfx_entity_position_get(first_fully_visual_group); + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + first_group_pos.y = MIN(first_group_pos.y, first_visual_group.y - first_group_size.h); + else + first_group_pos.x = MIN(first_group_pos.x, first_visual_group.x - first_group_size.w); + } + + efl_gfx_entity_position_set(first_group, first_group_pos); + efl_gfx_entity_size_set(first_group, first_group_size); +} + + +static void +position_content(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + Eina_Size2D space_size; + Vis_Segment cur; + int relevant_space_size, relevant_viewport; + Efl_Ui_Position_Manager_Range_Update ev; + + if (!pd->size) return; + if (pd->average_item_size <= 0) return; + + cache_require(obj, pd); + + //space size contains the amount of space that is outside the viewport (either to the top or to the left) + space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x; + space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + relevant_space_size = space_size.h; + relevant_viewport = pd->viewport.h; + } + else + { + relevant_space_size = space_size.w; + relevant_viewport = pd->viewport.w; + } + + cur = _search_visual_segment(obj, pd, relevant_space_size, relevant_viewport); + //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false + //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible + vis_segment_swap(pd->callbacks, cur, pd->prev_run); + + _position_items(obj, pd, cur, relevant_space_size); + + if (pd->prev_run.start_id != cur.start_id || pd->prev_run.end_id != cur.end_id) + { + ev.start_id = pd->prev_run.start_id = cur.start_id; + ev.end_id = pd->prev_run.end_id = cur.end_id; + efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, &ev); + } + +} + +static Eina_Value +_rebuild_job_cb(Eo *obj, void *data, const Eina_Value v) +{ + Efl_Wearable_Position_Manager_Circle_List_Data *pd = data; + + cache_require(obj, pd); + recalc_absolut_size(obj, pd); + position_content(obj, pd); + + return v; +} + +static void +_rebuild_job_free(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED) +{ + Efl_Wearable_Position_Manager_Circle_List_Data *pd = data; + + pd->rebuild_absolut_size = NULL; +} + +static void +schedule_recalc_absolut_size(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + if (pd->rebuild_absolut_size) return; + + pd->rebuild_absolut_size = efl_future_then(obj, efl_loop_job(efl_app_main_get()), + .success = _rebuild_job_cb, + .data = pd, + .free = _rebuild_job_free); +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_viewport_set(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, Eina_Rect size) +{ + if (pd->viewport.x == size.x && + pd->viewport.y == size.y && + pd->viewport.w == size.w && + pd->viewport.h == size.h) + return; + + pd->viewport = size; + + recalc_absolut_size(obj, pd); + position_content(obj, pd); +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_scroll_position_set(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, double x, double y) +{ + pd->scroll_position.x = x; + pd->scroll_position.y = y; + position_content(obj, pd); +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_item_added(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, int added_index EINA_UNUSED, Efl_Gfx_Entity *subobj) +{ + if (pd->size == 0) + { + pd->prev_run.start_id = 0; + pd->prev_run.end_id = 0; + } + pd->size ++; + if (subobj) + { + efl_gfx_entity_visible_set(subobj, EINA_FALSE); + } + cache_invalidate(obj, pd); + schedule_recalc_absolut_size(obj, pd); +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_item_removed(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj) +{ + pd->size --; + if (subobj) + { + efl_gfx_entity_visible_set(subobj, EINA_TRUE); + } + cache_invalidate(obj, pd); + schedule_recalc_absolut_size(obj, pd); +} + +EOLIAN static Eina_Rect +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_position_single_item(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, int idx) +{ + Eina_Rect geom; + Eina_Size2D space_size; + int relevant_space_size; + Eina_Size2D size; + Efl_Ui_Position_Manager_Size_Batch_Entity size_buffer[1]; + Efl_Ui_Position_Manager_Size_Batch_Result size_result; + + if (!pd->size) return EINA_RECT(0,0,0,0); + + cache_require(obj, pd); + + //space size contains the amount of space that is outside the viewport (either to the top or to the left) + space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x; + space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0)); + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + relevant_space_size = space_size.h; + } + else + { + relevant_space_size = space_size.w; + } + + geom = pd->viewport; + + BATCH_ACCESS_SIZE_VAL(pd->callbacks, idx, idx + 1, 1, EINA_FALSE, size_buffer, EINA_RECT_EMPTY()); + + size = size_buffer[0].size; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + geom.y -= (relevant_space_size - cache_access(obj, pd, idx)); + geom.h = size.h; + } + else + { + geom.x -= (relevant_space_size - cache_access(obj, pd, idx)); + geom.w = size.w; + } + return geom; +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_item_size_changed(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, int start_id EINA_UNUSED, int end_id EINA_UNUSED) +{ + cache_invalidate(obj, pd); + schedule_recalc_absolut_size(obj, pd); +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd, Efl_Ui_Layout_Orientation dir) +{ + pd->dir = dir; + //in order to reset the state of the visible items, just hide everything and set the old segment accordingly + vis_change_segment(pd->callbacks, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE); + pd->prev_run.start_id = 0; + pd->prev_run.end_id = 0; + + cache_invalidate(obj, pd); + cache_require(obj,pd); + if (!efl_finalized_get(obj)) return; + recalc_absolut_size(obj, pd); + position_content(obj, pd); +} + +EOLIAN static Efl_Ui_Layout_Orientation +_efl_wearable_position_manager_circle_list_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + return pd->dir; +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_object_invalidate(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd) +{ + if (pd->rebuild_absolut_size) + eina_future_cancel(pd->rebuild_absolut_size); + + efl_ui_position_manager_data_access_v1_data_access_set(obj, NULL, NULL, NULL, NULL, NULL, NULL, 0); + + efl_invalidate(efl_super(obj, MY_CLASS)); +} + +EOLIAN static int +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_relative_item(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd, unsigned int current_id, Efl_Ui_Focus_Direction direction) +{ + int new_id = current_id; + switch(direction) + { + case EFL_UI_FOCUS_DIRECTION_RIGHT: + case EFL_UI_FOCUS_DIRECTION_NEXT: + case EFL_UI_FOCUS_DIRECTION_DOWN: + new_id += 1; + break; + case EFL_UI_FOCUS_DIRECTION_LEFT: + case EFL_UI_FOCUS_DIRECTION_PREVIOUS: + case EFL_UI_FOCUS_DIRECTION_UP: + new_id -= 1; + break; + default: + ERR("Uncaught case!"); + new_id = -1; + break; + } + if (new_id < 0 || new_id > (int)pd->size) + return -1; + else + return new_id; +} + +EOLIAN static int +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_version(Eo *obj EINA_UNUSED, Efl_Wearable_Position_Manager_Circle_List_Data *pd EINA_UNUSED, int max EINA_UNUSED) +{ + return 1; +} + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_data_access_v1_data_access_set(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, void *obj_access_data, Efl_Ui_Position_Manager_Object_Batch_Callback obj_access, Eina_Free_Cb obj_access_free_cb, void *size_access_data, Efl_Ui_Position_Manager_Size_Batch_Callback size_access, Eina_Free_Cb size_access_free_cb, int size) +{ + // Cleanup cache first + cache_invalidate(obj, pd); + + // Clean callback if they were set + if (pd->callbacks.object.free_cb) + pd->callbacks.object.free_cb(pd->callbacks.object.data); + if (pd->callbacks.size.free_cb) + pd->callbacks.size.free_cb(pd->callbacks.size.data); + + // Set them + pd->callbacks.object.data = obj_access_data; + pd->callbacks.object.access = obj_access; + pd->callbacks.object.free_cb = obj_access_free_cb; + pd->callbacks.size.data = size_access_data; + pd->callbacks.size.access = size_access; + pd->callbacks.size.free_cb = size_access_free_cb; + pd->size = size; +} + + +EOLIAN static void +_efl_wearable_position_manager_circle_list_efl_ui_position_manager_entity_entities_ready(Eo *obj, Efl_Wearable_Position_Manager_Circle_List_Data *pd, unsigned int start_id, unsigned int end_id) +{ + Eina_Size2D space_size; + int relevant_space_size; + + if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id) + return; + + if (!pd->size) return; + if (pd->average_item_size <= 0) return; + + cache_require(obj, pd); + + //space size contains the amount of space that is outside the viewport (either to the top or to the left) + space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x; + space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + relevant_space_size = space_size.h; + } + else + { + relevant_space_size = space_size.w; + } + _position_items(obj, pd, pd->prev_run, relevant_space_size); +} + + +#include "efl_wearable_position_manager_circle_list.eo.c" diff --git a/src/lib/elementary/efl_wearable_position_manager_circle_list.eo b/src/lib/elementary/efl_wearable_position_manager_circle_list.eo new file mode 100644 index 0000000..0d91bde --- /dev/null +++ b/src/lib/elementary/efl_wearable_position_manager_circle_list.eo @@ -0,0 +1,24 @@ +class @beta Efl.Wearable.Position_Manager.Circle_List extends Efl.Object + implements Efl.Ui.Position_Manager.Entity, + Efl.Ui.Position_Manager.Data_Access_V1 +{ + [[Implementation of @Efl.Ui.Position_Manager.Entity for a circle list + + Every item in the list will get at least his minsize applied, changes to the misize are listened to and change + the layout of all items. This supports the vertical and horizontal orientation. + ]] + implements { + Efl.Object.invalidate; + Efl.Ui.Position_Manager.Entity.version; + Efl.Ui.Position_Manager.Entity.viewport {set;} + Efl.Ui.Position_Manager.Entity.scroll_position {set;} + Efl.Ui.Position_Manager.Entity.item_added; + Efl.Ui.Position_Manager.Entity.item_removed; + Efl.Ui.Position_Manager.Entity.position_single_item; + Efl.Ui.Position_Manager.Entity.item_size_changed; + Efl.Ui.Position_Manager.Entity.relative_item; + Efl.Ui.Position_Manager.Entity.entities_ready; + Efl.Ui.Layout_Orientable.orientation {set; get;} + Efl.Ui.Position_Manager.Data_Access_V1.data_access {set;} + } +} diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 14e72c8..21fbcd9 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -194,6 +194,12 @@ pub_eo_files = [ 'efl_ui_grid_view.eo', ] +#<--TIZEN_ONLY +pub_eo_files += [ + 'efl_wearable_position_manager_circle_list.eo', +] +#TIZEN_ONLY--> + foreach eo_file : pub_eo_files pub_eo_file_target += custom_target('eolian_gen_' + eo_file, input : eo_file, @@ -1012,6 +1018,7 @@ elementary_src += [ 'elm_atspi_proxy.c', 'tizen_vector.c', 'tizen_util.c', + 'efl_wearable_position_manager_circle_list.c', ] elementary_src_tizen = [ -- 2.7.4