From 0496b7988ab8df1c20874ebc81c8ff909aee58e1 Mon Sep 17 00:00:00 2001 From: "Godly T.Alias" Date: Wed, 1 Nov 2017 16:11:30 +0900 Subject: [PATCH] Genlist Item Pin Feature Summary: **@feature** T6241 This feature enables genlist to pin an item to viewport which will be available always for user to view/select. **Use Case**: In a big list of music, most times when user finds a song which they like, before playing that they may want to go through the entire list to check whether there is some other good songs, but after seeing the entire list user have to again scroll back to the position of item which they liked to play it then. In this case item pinning can be used, so that the item which they want to keep for future selection can be pinned and then it will remain in viewport, finally when user want to do operation on item, it will be readily available in viewport. Signed-off-by: Godly T.Alias Test Plan: Elementary Test -> Genlist -> Double click on items to enable/disable pinning Reviewers: raster, cedric, prince.dubey, SanghyeonLee Subscribers: rajeshps, jpeg, shilpasingh Tags: #efl Differential Revision: https://phab.enlightenment.org/D5340 --- src/bin/elementary/test_genlist.c | 7 ++ src/lib/elementary/elm_genlist.c | 129 +++++++++++++++++++++--- src/lib/elementary/elm_genlist_item.eo | 17 ++++ src/lib/elementary/elm_widget_genlist.h | 3 +- 4 files changed, 142 insertions(+), 14 deletions(-) diff --git a/src/bin/elementary/test_genlist.c b/src/bin/elementary/test_genlist.c index 2e5c093bd7..789b7b0486 100644 --- a/src/bin/elementary/test_genlist.c +++ b/src/bin/elementary/test_genlist.c @@ -287,6 +287,11 @@ static void _gl_double_clicked(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { printf("double clicked: %p\n", event_info); + Elm_Object_Item *it = event_info; + if (!elm_genlist_item_pin_get(it)) + elm_genlist_item_pin_set(it, EINA_TRUE); + else + elm_genlist_item_pin_set(it, EINA_FALSE); } static void @@ -750,6 +755,7 @@ test_genlist2(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_ evas_object_size_hint_align_set(gl, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(gl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); api->gl = gl; + evas_object_smart_callback_add(gl, "clicked,double", _gl_double_clicked, NULL); evas_object_show(gl); api->itc1 = elm_genlist_item_class_new(); @@ -2439,6 +2445,7 @@ test_genlist_reorder(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, elm_object_text_set(tg, "Reorder Mode:"); elm_check_state_set(tg, elm_config_mirrored_get()); evas_object_smart_callback_add(tg, "changed", _reorder_tg_changed_cb, gl); + evas_object_smart_callback_add(gl, "clicked,double", _gl_double_clicked, NULL); elm_box_pack_end(bx, tg); evas_object_show(tg); diff --git a/src/lib/elementary/elm_genlist.c b/src/lib/elementary/elm_genlist.c index a0706abc2b..19e2a0c02b 100644 --- a/src/lib/elementary/elm_genlist.c +++ b/src/lib/elementary/elm_genlist.c @@ -717,7 +717,7 @@ _item_block_unrealize(Item_Block *itb) dragging = EINA_TRUE; it->want_unrealize = EINA_TRUE; } - else + else if (it != itb->sd->pin_item) _elm_genlist_item_unrealize(it, EINA_FALSE); } } @@ -2202,6 +2202,7 @@ _group_items_recalc(void *data) Eina_List *l; Elm_Gen_Item *git; Elm_Genlist_Data *sd = data; + Evas_Coord vy; evas_event_freeze(evas_object_evas_get(sd->obj)); EINA_LIST_FOREACH(sd->group_items, l, git) @@ -2209,6 +2210,22 @@ _group_items_recalc(void *data) if (git->item->want_realize) { if (!git->realized) _item_realize(git, git->item->order_num_in, EINA_FALSE); + if (sd->pin_item && git == sd->pin_item->item->group_item && + sd->pin_item->item->scrl_y <= (git->item->scrl_y + git->item->h)) + { + elm_interface_scrollable_content_viewport_geometry_get + (sd->obj, NULL, &vy, NULL, NULL); + if ((git->item->scrl_y + git->item->h) > vy) + { + sd->pin_item->item->scrl_y = git->item->scrl_y + git->item->h; + evas_object_move(VIEW(sd->pin_item), + sd->pin_item->item->scrl_x, sd->pin_item->item->scrl_y); + } + } + else if (sd->pin_item && sd->pin_item_top && git != sd->pin_item->item->group_item && + (git->item->scrl_y < (sd->pin_item->item->scrl_y + sd->pin_item->item->h))) + git->item->scrl_y = sd->pin_item->item->scrl_y + sd->pin_item->item->h; + evas_object_resize(VIEW(git), sd->minw, git->item->h); evas_object_move(VIEW(git), git->item->scrl_x, git->item->scrl_y); evas_object_stack_above(VIEW(git), sd->stack[1]); @@ -2320,6 +2337,33 @@ _reorder_item_space_get(Elm_Gen_Item *it) return 0; } +static void +_pin_item_recalc(Elm_Gen_Item *it) +{ + Evas_Coord vx, vy, vw, vh; + ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd); + + sd->pin_item_top = EINA_FALSE; + elm_interface_scrollable_content_viewport_geometry_get + (sd->obj, &vx, &vy, &vw, &vh); + if (it->item->scrl_x < vx) + it->item->scrl_x = vx; + else if (it->item->scrl_x + it->item->w > vx + vw) + it->item->scrl_x = vx + vw - it->item->w; + + if (it->item->scrl_y < vy) + { + sd->pin_item_top = EINA_TRUE; + it->item->scrl_y = vy; + } + else if (it->item->scrl_y + it->item->h > vy + vh) + it->item->scrl_y = vy + vh - it->item->h; + + evas_object_resize(VIEW(it), it->item->w, it->item->h); + evas_object_move(VIEW(it), it->item->scrl_x, it->item->scrl_y); + evas_object_show(VIEW(it)); +} + static void _item_block_position(Item_Block *itb, const int blk_idx) { @@ -2429,7 +2473,7 @@ _item_block_position(Item_Block *itb, const int blk_idx) } else { - if (!sd->tree_effect_animator) + if (!sd->tree_effect_animator && (it != sd->pin_item)) _elm_genlist_item_unrealize(it, EINA_FALSE); } } @@ -2440,7 +2484,11 @@ _item_block_position(Item_Block *itb, const int blk_idx) } y += it->item->h; vis_count++; + + if (it == sd->pin_item) + _pin_item_recalc(it); } + evas_event_thaw(evas_object_evas_get((itb->sd)->obj)); evas_event_thaw_eval(evas_object_evas_get((itb->sd)->obj)); } @@ -2527,6 +2575,15 @@ _elm_genlist_pan_efl_canvas_group_group_calculate(Eo *obj, Elm_Genlist_Pan_Data else { if (itb->realized) _item_block_unrealize(itb); + if (sd->pin_item && itb == sd->pin_item->item->block) + { + if (!sd->pin_item->realized) + _item_realize(sd->pin_item, sd->pin_item->item->order_num_in, EINA_FALSE); + sd->pin_item->item->w = itb->w; + sd->pin_item->item->scrl_x = itb->x - sd->pan_x + ox; + sd->pin_item->item->scrl_y = itb->y - sd->pan_y + oy; + _pin_item_recalc(sd->pin_item); + } } in += itb->vis_count; } @@ -2566,6 +2623,8 @@ _elm_genlist_pan_efl_canvas_group_group_calculate(Eo *obj, Elm_Genlist_Pan_Data if (git->realized) evas_object_raise(VIEW(git)); } + if (sd->pin_item) + evas_object_raise(VIEW(sd->pin_item)); //update item before the render to prevent delayed update by job. if (sd->update_job) { @@ -2746,18 +2805,21 @@ _elm_genlist_item_focused(Elm_Object_Item *eo_it) (elm_wdg_item_disabled_get(eo_it))) return; - switch (_elm_config->focus_autoscroll_mode) + if (it != sd->pin_item) { - case ELM_FOCUS_AUTOSCROLL_MODE_SHOW: - elm_genlist_item_show(eo_it, - ELM_GENLIST_ITEM_SCROLLTO_IN); - break; - case ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN: - elm_genlist_item_bring_in(eo_it, - ELM_GENLIST_ITEM_SCROLLTO_IN); - break; - default: - break; + switch (_elm_config->focus_autoscroll_mode) + { + case ELM_FOCUS_AUTOSCROLL_MODE_SHOW: + elm_genlist_item_show(eo_it, + ELM_GENLIST_ITEM_SCROLLTO_IN); + break; + case ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN: + elm_genlist_item_bring_in(eo_it, + ELM_GENLIST_ITEM_SCROLLTO_IN); + break; + default: + break; + } } sd->focused_item = eo_it; @@ -3438,6 +3500,8 @@ _item_highlight(Elm_Gen_Item *it) else evas_object_stack_below(VIEW(it), sd->stack[1]); if ((it->item->group_item) && (it->item->group_item->realized)) evas_object_stack_above(it->item->VIEW(group_item), sd->stack[1]); + if (sd->pin_item && sd->pin_item->realized) + evas_object_stack_above(VIEW(sd->pin_item), sd->stack[1]); } it->highlighted = EINA_TRUE; } @@ -3696,6 +3760,7 @@ _item_del(Elm_Gen_Item *it) } elm_genlist_item_subitems_clear(EO_OBJ(it)); if (sd->show_item == it) sd->show_item = NULL; + if (sd->pin_item == it) sd->pin_item = NULL; if (it->realized) _elm_genlist_item_unrealize(it, EINA_FALSE); if (it->item->decorate_all_item_realized) _decorate_all_item_unrealize(it); if (it->item->block) _item_block_del(it); @@ -5587,6 +5652,8 @@ _elm_genlist_efl_canvas_group_group_add(Eo *obj, Elm_Genlist_Data *priv) priv->item_cache_max = priv->max_items_per_block * 2; priv->longpress_timeout = _elm_config->longpress_timeout; priv->highlight = EINA_TRUE; + priv->pin_item = NULL; + priv->pin_item_top = EINA_FALSE; priv->pan_obj = efl_add(MY_PAN_CLASS, evas_object_evas_get(obj)); pan_data = efl_data_scope_get(priv->pan_obj, MY_PAN_CLASS); @@ -7999,6 +8066,7 @@ _elm_genlist_reorder_mode_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bo Elm_Object_Item *eo_it; if (sd->reorder_mode == !!reorder_mode) return; + if (sd->pin_item) elm_genlist_item_pin_set(EO_OBJ(sd->pin_item), EINA_FALSE); sd->reorder_mode = !!reorder_mode; realized = elm_genlist_realized_items_get(obj); EINA_LIST_FREE(realized, eo_it) @@ -8039,6 +8107,41 @@ _elm_genlist_item_type_get(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it) return it->item->type; } +EOLIAN static void +_elm_genlist_item_pin_set(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it, Eina_Bool pin) +{ + ELM_GENLIST_ITEM_CHECK_OR_RETURN(it); + ELM_GENLIST_DATA_GET(WIDGET(it), sd); + if (sd->reorder_mode) return; + if (it->item->type & ELM_GENLIST_ITEM_GROUP) return; + + if (pin ^ (sd->pin_item == it)) + { + if (sd->pin_item) + { + if (sd->pin_item->item->block) + sd->pin_item->item->block->realized = EINA_TRUE; + evas_object_smart_changed(sd->pan_obj); + } + if (pin) + sd->pin_item = it; + else + sd->pin_item = NULL; + } +} + +EOLIAN static Eina_Bool +_elm_genlist_item_pin_get(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it) +{ + ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE); + ELM_GENLIST_DATA_GET(WIDGET(it), sd); + + if (sd->pin_item == it) + return EINA_TRUE; + else + return EINA_FALSE; +} + EAPI Elm_Genlist_Item_Class * elm_genlist_item_class_new(void) { diff --git a/src/lib/elementary/elm_genlist_item.eo b/src/lib/elementary/elm_genlist_item.eo index a8076618a6..28a5beaead 100644 --- a/src/lib/elementary/elm_genlist_item.eo +++ b/src/lib/elementary/elm_genlist_item.eo @@ -270,6 +270,23 @@ class Elm.Genlist.Item(Elm.Widget.Item) type: Elm.Genlist.Item.Type(Elm.Genlist.Item.Type.max); [[Item type.]] } } + @property pin { + get { + [[Get whether a given genlist item is pinned or not.]] + } + set { + [[Set whether a given genlist item is pinned or not + + This sets a genlist item as pinned so that it will be always available in + the viewport available for user interaction. Group items cannot be pinned. + Also when a new item is pinned, the current pinned item will get unpinned. + Item pinning cannot be done in reorder mode too. + ]] + } + values { + pin: bool; [[The item pin state state ($true pin item, $false unpin item).]] + } + } /* init { FIXME params { Evas_Smart_Cb func; diff --git a/src/lib/elementary/elm_widget_genlist.h b/src/lib/elementary/elm_widget_genlist.h index 5a1f4e8008..5e81cbe8ac 100644 --- a/src/lib/elementary/elm_widget_genlist.h +++ b/src/lib/elementary/elm_widget_genlist.h @@ -89,7 +89,7 @@ struct _Elm_Genlist_Data Ecore_Idler *must_recalc_idler; Eina_List *queue; Elm_Gen_Item *show_item, *anchor_item, *mode_item, - *reorder_rel, *expanded_item; + *reorder_rel, *expanded_item, *pin_item; Eina_Inlist *item_cache; /* an inlist of * edje object it * cache. */ @@ -201,6 +201,7 @@ struct _Elm_Genlist_Data Eina_Bool item_looping_on : 1; Eina_Bool tree_effect_animator : 1; + Eina_Bool pin_item_top : 1; }; typedef struct _Item_Block Item_Block;