From 577b82dad69c7f07856cbb254fcc60aab6f90999 Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Sat, 29 Jun 2019 16:19:08 +0200 Subject: [PATCH] Introduce Efl.Ui.Item_Container this is a new widget which aims to replace Efl.Ui.Grid / Efl.Ui.List. The widget is split up in a widget and a interface for item placement. Efl_Ui_Item_Position_Manager: The interface contains API which is used by the Item_Container to place the items, there is also a set of common tests which tests for the casual tripping wires, and ensures that the events are emitted in the correct moments (the later part still can be improved) Efl_Ui_Item_Container: The widget itself, it contains the API for the enduser to add Items to the widget, it handles the different modes for selection type and emits the events for selection changes. The pack API is conform with the spec unit test. An additional set of tests is defined which should be able to be run on every widget with a specific position_manager beeing set. Reviewed-by: Mike Blumenkrantz Reviewed-by: Cedric BAIL Differential Revision: https://phab.enlightenment.org/D9285 --- data/elementary/themes/default.edc | 1 + .../themes/edc/efl/item_container.edc | 3 + src/benchmarks/elementary/item_container.c | 100 ++ src/benchmarks/elementary/meson.build | 7 + src/bin/elementary/meson.build | 1 + src/bin/elementary/test.c | 5 +- src/bin/elementary/test_ui_item_container.c | 272 ++++++ src/lib/elementary/Efl_Ui.h | 9 + .../elementary/efl_ui_grid_position_manager.c | 321 +++++++ .../efl_ui_grid_position_manager.eo | 17 + src/lib/elementary/efl_ui_item_container.c | 857 ++++++++++++++++++ src/lib/elementary/efl_ui_item_container.eo | 94 ++ .../elementary/efl_ui_item_position_manager.c | 14 + .../efl_ui_item_position_manager.eo | 96 ++ .../elementary/efl_ui_list_position_manager.c | 389 ++++++++ .../efl_ui_list_position_manager.eo | 18 + src/lib/elementary/meson.build | 8 + src/tests/elementary/efl_ui_suite.c | 3 + src/tests/elementary/efl_ui_suite.h | 3 + .../elementary/efl_ui_test_grid_container.c | 36 + .../elementary/efl_ui_test_item_container.c | 54 ++ .../efl_ui_test_item_container_common.c | 233 +++++ .../efl_ui_test_item_container_common.h | 11 + .../elementary/efl_ui_test_list_container.c | 36 + .../efl_ui_test_position_manager_common.c | 192 ++++ src/tests/elementary/meson.build | 5 + .../elementary/spec/efl_test_pack_linear.c | 5 +- src/tests/elementary/spec/efl_ui_spec_suite.c | 12 + src/tests/elementary/spec/efl_ui_spec_suite.h | 1 + src/tests/elementary/spec/meson.build | 19 +- .../spec/test_efl_ui_item_container_list.eo | 6 + 31 files changed, 2824 insertions(+), 4 deletions(-) create mode 100644 data/elementary/themes/edc/efl/item_container.edc create mode 100644 src/benchmarks/elementary/item_container.c create mode 100644 src/bin/elementary/test_ui_item_container.c create mode 100644 src/lib/elementary/efl_ui_grid_position_manager.c create mode 100644 src/lib/elementary/efl_ui_grid_position_manager.eo create mode 100644 src/lib/elementary/efl_ui_item_container.c create mode 100644 src/lib/elementary/efl_ui_item_container.eo create mode 100644 src/lib/elementary/efl_ui_item_position_manager.c create mode 100644 src/lib/elementary/efl_ui_item_position_manager.eo create mode 100644 src/lib/elementary/efl_ui_list_position_manager.c create mode 100644 src/lib/elementary/efl_ui_list_position_manager.eo create mode 100644 src/tests/elementary/efl_ui_test_grid_container.c create mode 100644 src/tests/elementary/efl_ui_test_item_container.c create mode 100644 src/tests/elementary/efl_ui_test_item_container_common.c create mode 100644 src/tests/elementary/efl_ui_test_item_container_common.h create mode 100644 src/tests/elementary/efl_ui_test_list_container.c create mode 100644 src/tests/elementary/efl_ui_test_position_manager_common.c create mode 100644 src/tests/elementary/spec/test_efl_ui_item_container_list.eo diff --git a/data/elementary/themes/default.edc b/data/elementary/themes/default.edc index 201ccc9d2b..cd7d0f909a 100644 --- a/data/elementary/themes/default.edc +++ b/data/elementary/themes/default.edc @@ -207,4 +207,5 @@ collections { #include "edc/efl/tab_pager.edc" #include "edc/efl/tab_bar.edc" #include "edc/efl/tab_page.edc" +#include "edc/efl/item_container.edc" } diff --git a/data/elementary/themes/edc/efl/item_container.edc b/data/elementary/themes/edc/efl/item_container.edc new file mode 100644 index 0000000000..9980349612 --- /dev/null +++ b/data/elementary/themes/edc/efl/item_container.edc @@ -0,0 +1,3 @@ +group { "efl/item_container"; + inherit: "efl/scroller"; +} diff --git a/src/benchmarks/elementary/item_container.c b/src/benchmarks/elementary/item_container.c new file mode 100644 index 0000000000..b93004c8df --- /dev/null +++ b/src/benchmarks/elementary/item_container.c @@ -0,0 +1,100 @@ +#include + +static Eo *first, *last, *middle; +static int timer = 15; +static int frames = 0; +static double start_time; + +static void +_timer_tick(void *data, const Efl_Event *ev) +{ + if (timer % 2 == 0) + { + efl_ui_item_container_item_scroll(data, last, EINA_TRUE); + } + else + { + efl_ui_item_container_item_scroll(data, first, EINA_TRUE); + } + + timer--; + + if (timer == 0) + { + double runtime = ecore_time_get() - start_time; + efl_loop_quit(efl_app_main_get(), EINA_VALUE_EMPTY); + efl_del(ev->object); + printf("We did %d frames in %f s seconds\n", frames, runtime); + printf("FPS: %f\n", ((double)frames / runtime)); + + } +} + +static void +_caputure(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED) +{ + frames ++; +} + +static void +_started_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(), + efl_loop_timer_interval_set(efl_added, 1.0), + efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _timer_tick, data) + ); + start_time = ecore_time_get(); + efl_event_callback_add(evas_object_evas_get(data), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _caputure, data); + efl_del(ev->object); +} + +static void +_first_frame_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + efl_ui_item_container_item_scroll(data, middle, EINA_FALSE); + //give time to stabelize + efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(), + efl_loop_timer_interval_set(efl_added, 15.0), + efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _started_cb, data) + ); + efl_event_callback_del(ev->object, EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, data); +} + +EAPI_MAIN void +efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED) +{ + Eo *win, *item_container, *list; + + win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(), + efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC), + efl_text_set(efl_added, "Efl.Ui.Item_Container benchmark"), + efl_ui_win_autodel_set(efl_added, EINA_TRUE) + ); + + list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS); + item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, + efl_ui_item_container_position_manager_set(efl_added, list)); + efl_content_set(win, item_container); + + printf("Building 5000 objects\n"); + for (int i = 0; i < 5000; ++i) + { + Eo *il = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); + double r = 10+((double)190/(double)10)*(i%10); + + if (i == 0) + first = il; + else if (i == 2500) + middle = il; + else if (i == 4999) + last = il; + efl_gfx_color_set(il, r, 10, 10, 255); + efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40)); + efl_pack_end(item_container, il); + } + printf("Done!\n"); + efl_gfx_entity_size_set(win, EINA_SIZE2D(200, 200)); + + efl_event_callback_add(evas_object_evas_get(win), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, item_container); +} +EFL_MAIN() diff --git a/src/benchmarks/elementary/meson.build b/src/benchmarks/elementary/meson.build index 206ea06e4b..af71b972fa 100644 --- a/src/benchmarks/elementary/meson.build +++ b/src/benchmarks/elementary/meson.build @@ -6,3 +6,10 @@ focus_widget_tree_bench = executable('focus_widget_tree_bench', benchmark('focus_widget_tree', focus_widget_tree_bench, args: ['5'], ) + +item_container = executable('item_container', + 'item_container.c', + dependencies: [elementary, ecore_input_evas, eio], +) + +benchmark('item_container', item_container) diff --git a/src/bin/elementary/meson.build b/src/bin/elementary/meson.build index 96b6e906a0..10372d9154 100644 --- a/src/bin/elementary/meson.build +++ b/src/bin/elementary/meson.build @@ -156,6 +156,7 @@ elementary_test_src = [ 'test_gesture_framework.c', 'test_ui_tab_pager.c', 'test_ui_relative_layout.c', + 'test_ui_item_container.c', 'test.h' ] diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index b751d65321..2062f18acb 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -394,7 +394,8 @@ void test_ui_spotlight_scroll(void *data, Evas_Object *obj, void *event_info); void test_ui_relative_layout(void *data, Evas_Object *obj, void *event_info); void test_efl_ui_radio(void *data, Evas_Object *obj, void *event_info); - +void test_efl_ui_item_container_list(void *data, Evas_Object *obj, void *event_info ); +void test_efl_ui_item_container_grid(void *data, Evas_Object *obj, void *event_info); static void _list_udpate(void); static Evas_Object *win, *tbx, *entry; // TODO: refactoring @@ -868,6 +869,8 @@ add_tests: ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table (Linear API)", test_ui_table_linear); ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table_Static", test_ui_table_static); ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Relative_Layout", test_ui_relative_layout); + ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container List", test_efl_ui_item_container_list); + ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container Grid", test_efl_ui_item_container_grid); //------------------------------// ADD_TEST_EO(NULL, "Events", "Event Refeed", test_events); diff --git a/src/bin/elementary/test_ui_item_container.c b/src/bin/elementary/test_ui_item_container.c new file mode 100644 index 0000000000..77f410a1b3 --- /dev/null +++ b/src/bin/elementary/test_ui_item_container.c @@ -0,0 +1,272 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include + +static void +_selection_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + if (efl_ui_check_selected_get(ev->object)) + efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_VERTICAL); + else + efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL); +} + +static void +_scroll_to_animated_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Efl_Ui_Widget *element_1154 = efl_pack_content_get(data, 1154); + + EINA_SAFETY_ON_NULL_RETURN(element_1154); + + efl_ui_item_container_item_scroll(data, element_1154, EINA_TRUE); +} + +static void +_scroll_to_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Efl_Ui_Widget *element_10 = efl_pack_content_get(data, 10); + + EINA_SAFETY_ON_NULL_RETURN(element_10); + + efl_ui_item_container_item_scroll(data, element_10, EINA_FALSE); +} + +static void +_change_min_size_cb(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED) +{ + static Eina_Bool b = EINA_FALSE; + Efl_Ui_Widget *element_0 = efl_pack_content_get(data, 0); + + EINA_SAFETY_ON_NULL_RETURN(element_0); + + if (!b) + { + efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 200)); + } + else + { + efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 40)); + } + b = !b; +} + +typedef struct { + Efl_Ui_Check *v, *h; + Efl_Ui_Item_Container *c; +} Match_Content_Ctx; + +static void +_selection_changed_match_content_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Match_Content_Ctx *c = data; + Eina_Bool v,h; + + v = efl_ui_check_selected_get(c->v); + h = efl_ui_check_selected_get(c->h); + + efl_ui_scrollable_match_content_set(c->c, v, h); +} + +static void +_widget_del_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + free(data); +} + +static void +_add_item(Efl_Ui_Item_Container *c) +{ + Efl_Class *itc = efl_key_data_get(c, "__item_class"); + char buf[PATH_MAX]; + int r = 0, g = 0, b = 0; + int i = efl_content_count(c); + Eo *rect; + Eo *il; + + + il = efl_add(itc, c); + + snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13); + efl_text_set(il, buf); + + rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(c)); + switch (i % 5) + { + case 0: + r = 255; + break; + case 1: + g = 255; + break; + case 2: + b = 255; + break; + case 3: + r = g = b = 255; + break; + case 4: + r = g = b = 0; + break; + } + efl_gfx_color_set(rect, r, g, b, 255); + efl_content_set(il, rect); + if (itc == EFL_UI_GRID_DEFAULT_ITEM_CLASS) + efl_gfx_hint_size_min_set(il, EINA_SIZE2D(100, 180)); + else + efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40)); + efl_pack_end(c, il); +} + +static void +_remove_all_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + efl_pack_clear(data); +} + +static void +_add_one_item(void *data, const Efl_Event *ev EINA_UNUSED) +{ + _add_item(data); +} + +static void +_add_thousend_items(void *data, const Efl_Event *ev EINA_UNUSED) +{ + for (int i = 0; i < 1000; ++i) + { + _add_item(data); + } +} + +static void +_select_value_cb(void *data, const Efl_Event *ev) +{ + Efl_Ui_Item_Container *c = data; + + efl_ui_select_mode_set(c, efl_ui_radio_group_selected_value_get(ev->object)); +} + +void create_item_container_ui(Efl_Ui_Item_Position_Manager *manager, const Efl_Class *item, const char *name) +{ + Efl_Ui_Win *win, *o, *tbl, *item_container, *bx; + Match_Content_Ctx *ctx = calloc(1, sizeof(*ctx)); + + win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(), + efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC), + efl_text_set(efl_added, name), + efl_ui_win_autodel_set(efl_added, EINA_TRUE)); + tbl = efl_add(EFL_UI_TABLE_CLASS, win); + efl_content_set(win, tbl); + + item_container = o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, + efl_ui_item_container_position_manager_set(efl_added, manager)); + efl_key_data_set(o, "__item_class", item); + efl_event_callback_add(o, EFL_EVENT_DEL, _widget_del_cb, ctx); + for (int i = 0; i < 2000; ++i) + { + _add_item(o); + } + efl_pack_table(tbl, o, 1, 0, 1, 11); + ctx->c = o; + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Scroll to 1154 ANIMATED"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_animated_cb, item_container); + efl_pack_table(tbl, o, 0, 1, 1, 1); + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Scroll to 10"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_cb, item_container); + efl_pack_table(tbl, o, 0, 2, 1, 1); + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Change min size of 0"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _change_min_size_cb, item_container); + efl_pack_table(tbl, o, 0, 3, 1, 1); + + o = efl_add(EFL_UI_CHECK_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Vertical"); + efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_cb, item_container); + efl_ui_check_selected_set(o, EINA_TRUE); + efl_pack_table(tbl, o, 0, 4, 1, 1); + + o = efl_add(EFL_UI_CHECK_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Match Vertical"); + efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx); + efl_pack_table(tbl, o, 0, 5, 1, 1); + ctx->v = o; + + o = efl_add(EFL_UI_CHECK_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Match Horizontal"); + efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx); + efl_pack_table(tbl, o, 0, 6, 1, 1); + efl_gfx_entity_size_set(win, EINA_SIZE2D(260, 200)); + ctx->h = o; + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Remove all items"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _remove_all_cb, item_container); + efl_pack_table(tbl, o, 0, 7, 1, 1); + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Add 1 item"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_one_item, item_container); + efl_pack_table(tbl, o, 0, 8, 1, 1); + + o = efl_add(EFL_UI_BUTTON_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_text_set(o, "Add 1000 item"); + efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_thousend_items, item_container); + efl_pack_table(tbl, o, 0, 9, 1, 1); + + bx = efl_add(EFL_UI_RADIO_BOX_CLASS, tbl, + efl_gfx_hint_weight_set(efl_added, 0.0, 0.0), + efl_gfx_hint_align_set(efl_added, 0, 0.5)); + efl_event_callback_add(bx, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _select_value_cb, item_container); + efl_pack_table(tbl, bx, 0, 10, 1, 1); + o = efl_add(EFL_UI_RADIO_CLASS, bx, + efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE)); + efl_text_set(o, "Singleselect"); + efl_pack_end(bx, o); + o = efl_add(EFL_UI_RADIO_CLASS, bx, + efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE_ALWAYS)); + efl_text_set(o, "Singleselect Always"); + efl_pack_end(bx, o); + o = efl_add(EFL_UI_RADIO_CLASS, bx, + efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_MULTI)); + efl_text_set(o, "Multiselect"); + efl_pack_end(bx, o); +} + +void test_efl_ui_item_container_grid(void *data EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + create_item_container_ui(efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS), EFL_UI_GRID_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container Grid"); +} + +void test_efl_ui_item_container_list(void *data EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + create_item_container_ui(efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS), EFL_UI_LIST_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container List"); +} diff --git a/src/lib/elementary/Efl_Ui.h b/src/lib/elementary/Efl_Ui.h index 6ef6a65049..99e389f190 100644 --- a/src/lib/elementary/Efl_Ui.h +++ b/src/lib/elementary/Efl_Ui.h @@ -250,6 +250,15 @@ typedef Eo Efl_Ui_Spotlight_Indicator; # include # include # include +# include +# include +# include +# include +# include +# include +# include +# include +# include /** * Initialize Elementary diff --git a/src/lib/elementary/efl_ui_grid_position_manager.c b/src/lib/elementary/efl_ui_grid_position_manager.c new file mode 100644 index 0000000000..f4dfd78c31 --- /dev/null +++ b/src/lib/elementary/efl_ui_grid_position_manager.c @@ -0,0 +1,321 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif + +#include +#include +#include "elm_widget.h" +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_GRID_POSITION_MANAGER_CLASS +#define MY_DATA_GET(obj, pd) \ + Efl_Ui_Grid_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS); + +typedef struct { + Eina_Accessor *content_acc, *size_acc; + unsigned int size; + Eina_Rect viewport; + Eina_Vector2 scroll_position; + Efl_Ui_Layout_Orientation dir; + struct { + unsigned int start_id, end_id; + } prev_run; + Eina_Size2D max_min_size; + Eina_Size2D last_viewport_size; + Eina_Size2D prev_min_size; + struct { + int columns; + int rows; + } current_display_table; +} Efl_Ui_Grid_Position_Manager_Data; + +static inline void +vis_change_segment(Efl_Ui_Grid_Position_Manager_Data *pd, int a, int b, Eina_Bool flag) +{ + for (int i = MIN(a, b); i < MAX(a, b); ++i) + { + Efl_Gfx_Entity *ent; + + EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent)); + if (ent && !efl_ui_focus_object_focus_get(ent)) + { + efl_gfx_entity_visible_set(ent, flag); + } + } +} + +static void +_reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd) +{ + Eina_Size2D space_size; + int relevant_space_size, relevant_viewport; + unsigned int start_id, end_id, step; + + if (!pd->size) return; + if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return; + if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return; + + //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->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x; + space_size.h = (MAX(pd->last_viewport_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; + step = pd->max_min_size.h; + } + else + { + relevant_space_size = space_size.w; + relevant_viewport = pd->viewport.w; + step = pd->max_min_size.w; + } + start_id = MIN((MAX(relevant_space_size,0) / step)*pd->current_display_table.columns, pd->size); + end_id = MIN((((MAX(relevant_space_size,0) + relevant_viewport + step) / step)*pd->current_display_table.columns)+1, pd->size); + + EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id); + EINA_SAFETY_ON_FALSE_RETURN(start_id <= pd->size); + + //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 + if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id) + { + //it is important to first make the segment visible here, and then hide the rest + //otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic. + vis_change_segment(pd, start_id, end_id, EINA_TRUE); + vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE); + } + else + { + vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id)); + vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id)); + } + + for (unsigned int i = start_id; i < end_id; ++i) + { + Eina_Rect geom; + Efl_Gfx_Entity *ent; + geom.size = pd->max_min_size; + geom.pos = pd->viewport.pos; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + geom.x += pd->max_min_size.w*(i%pd->current_display_table.columns); + geom.y += pd->max_min_size.h*(i/pd->current_display_table.columns); + geom.y -= (relevant_space_size); + } + else + { + geom.x += pd->max_min_size.w*(i/pd->current_display_table.columns); + geom.y += pd->max_min_size.h*(i%pd->current_display_table.columns); + geom.x -= (relevant_space_size); + } + + EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent)); + //printf(">%d (%d, %d, %d, %d) %p\n", i, geom.x, geom.y, geom.w, geom.h, ent); + efl_gfx_entity_geometry_set(ent, geom); + } + pd->prev_run.start_id = start_id; + pd->prev_run.end_id = end_id; +} + +static inline void +_flush_abs_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd) +{ + int minor, major; + Eina_Size2D vp_size; + + if (!pd->size) return; + if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + major = pd->viewport.w/pd->max_min_size.w; + pd->current_display_table.columns = major; + } + else + { + major = pd->viewport.h/pd->max_min_size.h; + pd->current_display_table.columns = major; + } + + if (major <= 0) return; + minor = ceil((double)pd->size/(double)major); + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + pd->current_display_table.rows = minor; + else + pd->current_display_table.rows = minor; + + /* + * calculate how much size we need with major in the given orientation. + * The + */ + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + vp_size.w = pd->viewport.w; + vp_size.h = minor*pd->max_min_size.h; + } + else + { + vp_size.h = pd->viewport.h; + vp_size.w = minor*pd->max_min_size.w; + } + if (vp_size.h != pd->last_viewport_size.h || vp_size.w != pd->last_viewport_size.w) + { + pd->last_viewport_size = vp_size; + efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, &vp_size); + } +} + +static inline void +_update_min_size(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int added_index) +{ + Eina_Size2D elemsize; + + EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, added_index, (void*)&elemsize)); + pd->max_min_size.w = MAX(pd->max_min_size.w, elemsize.w); + pd->max_min_size.h = MAX(pd->max_min_size.h, elemsize.h); +} + +static inline void +_flush_min_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd) +{ + Eina_Size2D min_size = pd->max_min_size; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + min_size.h = -1; + else + min_size.w = -1; + + if (pd->prev_min_size.w != min_size.w || pd->prev_min_size.h != min_size.h) + { + pd->prev_min_size = min_size; + efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size); + } +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_data_access_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Eina_Accessor *obj_access, Eina_Accessor *size_access, int size) +{ + pd->size_acc = size_access; + pd->content_acc = obj_access; + pd->size = size; +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_viewport_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Eina_Rect viewport) +{ + pd->viewport = viewport; + _flush_abs_size(obj, pd); + _reposition_content(obj, pd); +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_scroll_position_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, double x, double y) +{ + pd->scroll_position.x = x; + pd->scroll_position.y = y; + _reposition_content(obj, pd); +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_added(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd, int added_index, Efl_Gfx_Entity *subobj EINA_UNUSED) +{ + pd->size ++; + + efl_gfx_entity_visible_set(subobj, EINA_FALSE); + _update_min_size(obj, pd, added_index); + _flush_min_size(obj, pd); + _flush_abs_size(obj, pd); + _reposition_content(obj, pd); //FIXME we might can skip that +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj EINA_UNUSED) +{ + EINA_SAFETY_ON_FALSE_RETURN(pd->size > 0); + pd->size --; + pd->prev_run.start_id = MIN(pd->prev_run.start_id, pd->size); + pd->prev_run.end_id = MIN(pd->prev_run.end_id, pd->size); + //we ignore here that we might loose the item giving the current max min size + _flush_abs_size(obj, pd); + _reposition_content(obj, pd); //FIXME we might can skip that + efl_gfx_entity_visible_set(subobj, EINA_TRUE); +} + + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_size_changed(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd, int start_id, int end_id) +{ + for (int i = start_id; i <= end_id; ++i) + { + _update_min_size(obj, pd, i); + } + + _flush_min_size(obj, pd); + _flush_abs_size(obj, pd); + _reposition_content(obj, pd); //FIXME we could check if this is needed or not +} + +EOLIAN static void +_efl_ui_grid_position_manager_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Efl_Ui_Layout_Orientation dir) +{ + pd->dir = dir; + _flush_min_size(obj, pd); + _flush_abs_size(obj, pd); + _reposition_content(obj, pd); //FIXME we could check if this is needed or not +} + + +EOLIAN static Efl_Ui_Layout_Orientation +_efl_ui_grid_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd) +{ + return pd->dir; +} + +EOLIAN static Eina_Rect +_efl_ui_grid_position_manager_efl_ui_item_position_manager_position_single_item(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int idx) +{ + Eina_Rect geom; + Eina_Size2D space_size; + unsigned int relevant_space_size; + + if (!pd->size) return EINA_RECT(0, 0, 0, 0); + if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return EINA_RECT(0, 0, 0, 0); + if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return EINA_RECT(0, 0, 0, 0); + + //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->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x; + space_size.h = (MAX(pd->last_viewport_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.size = pd->max_min_size; + geom.pos = pd->viewport.pos; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + { + geom.x += pd->max_min_size.w*(idx%pd->current_display_table.columns); + geom.y += pd->max_min_size.h*(idx/pd->current_display_table.columns); + geom.y -= (relevant_space_size); + } + else + { + geom.x += pd->max_min_size.w*(idx/pd->current_display_table.columns); + geom.y += pd->max_min_size.h*(idx%pd->current_display_table.columns); + geom.x -= (relevant_space_size); + } + + return geom; +} + +#include "efl_ui_grid_position_manager.eo.c" diff --git a/src/lib/elementary/efl_ui_grid_position_manager.eo b/src/lib/elementary/efl_ui_grid_position_manager.eo new file mode 100644 index 0000000000..f825f4db9d --- /dev/null +++ b/src/lib/elementary/efl_ui_grid_position_manager.eo @@ -0,0 +1,17 @@ +class @beta Efl.Ui.Grid_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager +{ + [[Implementation of @Efl.Ui.Item_Position_Manager for two-dimensional grids. + + Every item in the grid has the same size, which is the biggest minimum size of all items. + ]] + implements { + Efl.Ui.Item_Position_Manager.data_access {set;} + Efl.Ui.Item_Position_Manager.viewport {set;} + Efl.Ui.Item_Position_Manager.scroll_position {set;} + Efl.Ui.Item_Position_Manager.item_added; + Efl.Ui.Item_Position_Manager.item_removed; + Efl.Ui.Item_Position_Manager.position_single_item; + Efl.Ui.Item_Position_Manager.item_size_changed; + Efl.Ui.Layout_Orientable.orientation {set; get;} + } +} diff --git a/src/lib/elementary/efl_ui_item_container.c b/src/lib/elementary/efl_ui_item_container.c new file mode 100644 index 0000000000..af737f5316 --- /dev/null +++ b/src/lib/elementary/efl_ui_item_container.c @@ -0,0 +1,857 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif + +#define ELM_LAYOUT_PROTECTED +#define EFL_UI_SCROLL_MANAGER_PROTECTED +#define EFL_UI_SCROLLBAR_PROTECTED +#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED + +#include +#include +#include "elm_widget.h" +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_ITEM_CONTAINER_CLASS + +#define MY_DATA_GET(obj, pd) \ + Efl_Ui_Item_Container_Data *pd = efl_data_scope_get(obj, MY_CLASS); + +typedef struct { + Efl_Ui_Scroll_Manager *smanager; + Efl_Ui_Pan *pan; + Eina_List *selected; + Eina_List *items; + Efl_Ui_Select_Mode mode; + Efl_Ui_Layout_Orientation dir; + Eina_Size2D content_min_size; + Efl_Ui_Item_Position_Manager *pos_man; + struct { + Eina_Accessor pass_on; + unsigned int last_index; + const Eina_List *current; + } obj_accessor; + struct { + Eina_Bool w; + Eina_Bool h; + } match_content; + Eina_Accessor size_accessor; + Efl_Gfx_Entity *sizer; +} Efl_Ui_Item_Container_Data; + +static Eina_Bool register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item); +static Eina_Bool unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item); + +static void +flush_min_size(Eo *obj, Efl_Ui_Item_Container_Data *pd) +{ + Eina_Size2D tmp = pd->content_min_size; + + if (!pd->match_content.w) + tmp.w = -1; + + if (!pd->match_content.h) + tmp.h = -1; + + efl_gfx_hint_size_min_set(obj, tmp); +} + +static int +clamp_index(Efl_Ui_Item_Container_Data *pd, int index) +{ + if (index < ((int)eina_list_count(pd->items)) * -1) + return -1; + else if (index > (int)eina_list_count(pd->items) - 1) + return 1; + return 0; +} + +static int +index_adjust(Efl_Ui_Item_Container_Data *pd, int index) +{ + int c = eina_list_count(pd->items); + if (index < c * -1) + return 0; + else if (index > c - 1) + return c - 1; + else if (index < 0) + return index + c; + return index; +} + +static Eina_Bool +_obj_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data) +{ + ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor); + Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset; + const Eina_List *over; + unsigned int middle; + unsigned int i; + + if (idx >= eina_list_count(pd->items)) + return EINA_FALSE; + + if (pd->obj_accessor.last_index == idx) + over = pd->obj_accessor.current; + else if (idx > pd->obj_accessor.last_index) + { + /* After current position. */ + middle = ((eina_list_count(pd->items) - pd->obj_accessor.last_index))/2; + + if (idx > middle) + /* Go backward from the end. */ + for (i = eina_list_count(pd->items) - 1, + over = eina_list_last(pd->items); + i > idx && over; + --i, over = eina_list_prev(over)) + ; + else + /* Go forward from current. */ + for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current; + i < idx && over; + ++i, over = eina_list_next(over)) + ; + } + else + { + /* Before current position. */ + middle = pd->obj_accessor.last_index/2; + + if (idx > middle) + /* Go backward from current. */ + for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current; + i > idx && over; + --i, over = eina_list_prev(over)) + ; + else + /* Go forward from start. */ + for (i = 0, over = pd->items; + i < idx && over; + ++i, over = eina_list_next(over)) + ; + } + + if (!over) + return EINA_FALSE; + + pd->obj_accessor.last_index = idx; + pd->obj_accessor.current = over; + + *data = eina_list_data_get(over); + return EINA_TRUE; +} + +static Eina_Accessor* +_obj_clone(Eina_Accessor *accessor) +{ + ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor); + Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset; + + return eina_list_accessor_new(pd->items); +} + +static Eina_List * +_null_container(Eina_Accessor *accessor EINA_UNUSED) +{ + ERR("Not allowed to get a container!"); + return NULL; +} + +static void +_free(Eina_Accessor *accessor EINA_UNUSED) +{ + ERR("Freeing this accessor is not supported"); +} + +static void +_obj_accessor_init(Eina_Accessor *accessor) +{ + //this is the accessor for accessing the items + //we have to workarround here the problem that + //no accessor can be created for a not yet created list. + accessor->version = EINA_ACCESSOR_VERSION; + accessor->get_at = FUNC_ACCESSOR_GET_AT(_obj_accessor_get_at); + accessor->clone = FUNC_ACCESSOR_CLONE(_obj_clone); + accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container); + accessor->free = FUNC_ACCESSOR_FREE(_free); + EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR); +} + +static Eina_Bool +_size_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data) +{ + Eina_Size2D *size = (Eina_Size2D*)data; + ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, size_accessor); + Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset; + + if (idx > eina_list_count(pd->items)) + return EINA_FALSE; + + Eo *subobj = eina_list_nth(pd->items, idx); + + *size = efl_gfx_hint_size_combined_min_get(subobj); + + return EINA_TRUE; +} + +static Eina_Accessor* +_size_clone(Eina_Accessor *accessor EINA_UNUSED) +{ + return NULL; +} + +static void +_size_accessor_init(Eina_Accessor *accessor) +{ + accessor->version = EINA_ACCESSOR_VERSION; + accessor->get_at = FUNC_ACCESSOR_GET_AT(_size_accessor_get_at); + accessor->clone = FUNC_ACCESSOR_CLONE(_size_clone); + accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container); + accessor->free = FUNC_ACCESSOR_FREE(_free); + EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR); +} + +static void +_pan_viewport_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + MY_DATA_GET(data, pd); + Eina_Rect rect = efl_ui_scrollable_viewport_geometry_get(data); + + efl_ui_item_position_manager_viewport_set(pd->pos_man, rect); +} + +static void +_pan_position_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED) +{ + MY_DATA_GET(data, pd); + Eina_Position2D pos = efl_ui_pan_position_get(pd->pan); + Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan); + Eina_Vector2 rpos = {0.0, 0.0}; + + if (max.x > 0.0) + rpos.x = (double)pos.x/(double)max.x; + if (max.y > 0.0) + rpos.y = (double)pos.y/(double)max.y; + + efl_ui_item_position_manager_scroll_position_set(pd->pos_man, rpos.x, rpos.y); +} + +EFL_CALLBACKS_ARRAY_DEFINE(pan_events_cb, + {EFL_UI_PAN_EVENT_PAN_POSITION_CHANGED, _pan_position_changed_cb}, + {EFL_UI_PAN_EVENT_PAN_VIEWPORT_CHANGED, _pan_viewport_changed_cb}, +) + +static void +_item_scroll_internal(Eo *obj EINA_UNUSED, + Efl_Ui_Item_Container_Data *pd, + Efl_Ui_Item *item, + double align, + Eina_Bool anim) +{ + Eina_Rect ipos, view; + Eina_Position2D vpos; + + if (!pd->smanager) return; + + ipos = efl_ui_item_position_manager_position_single_item(pd->pos_man, eina_list_data_idx(pd->items, item)); + view = efl_ui_scrollable_viewport_geometry_get(pd->smanager); + vpos = efl_ui_scrollable_content_pos_get(pd->smanager); + + ipos.x = ipos.x + vpos.x - view.x; + ipos.y = ipos.y + vpos.y - view.y; + + //FIXME scrollable needs some sort of align, the docs do not even garantee to completly move in the element + efl_ui_scrollable_scroll(pd->smanager, ipos, anim); +} + +EOLIAN static void +_efl_ui_item_container_item_scroll(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, Eina_Bool animation) +{ + _item_scroll_internal(obj, pd, item, -1.0, animation); +} + +EOLIAN static void +_efl_ui_item_container_item_scroll_align(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, double align, Eina_Bool animation) +{ + _item_scroll_internal(obj, pd, item, align, animation); +} + +EOLIAN static Efl_Ui_Item* +_efl_ui_item_container_last_selected_item_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return eina_list_last_data_get(pd->selected); +} + +EOLIAN static Eina_Iterator* +_efl_ui_item_container_selected_items_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return eina_list_iterator_new(pd->selected); +} + +EOLIAN static Efl_Object* +_efl_ui_item_container_efl_object_constructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED) +{ + Eo *o; + + pd->dir = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; + + _obj_accessor_init(&pd->obj_accessor.pass_on); + _size_accessor_init(&pd->size_accessor); + + if (!elm_widget_theme_klass_get(obj)) + elm_widget_theme_klass_set(obj, "item_container"); + + o = efl_constructor(efl_super(obj, MY_CLASS)); + + pd->sizer = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(obj)); + efl_gfx_color_set(pd->sizer, 0, 0, 0, 0); + + pd->pan = efl_add(EFL_UI_PAN_CLASS, obj); + efl_content_set(pd->pan, pd->sizer); + efl_event_callback_array_add(pd->pan, pan_events_cb(), obj); + + pd->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj); + efl_composite_attach(obj, pd->smanager); + efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj)); + efl_ui_scroll_manager_pan_set(pd->smanager, pd->pan); + + efl_ui_scroll_connector_bind(obj, pd->smanager); + + return o; +} + +EOLIAN static Efl_Object* +_efl_ui_item_container_efl_object_finalize(Eo *obj, Efl_Ui_Item_Container_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->pos_man, NULL); + + return efl_finalize(efl_super(obj, MY_CLASS)); +} + +EOLIAN static Eina_Error +_efl_ui_item_container_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Item_Container_Data *pd) +{ + Eina_Error res; + + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC); + res = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); + if (res == EFL_UI_THEME_APPLY_ERROR_GENERIC) return res; + efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj)); + efl_content_set(efl_part(wd->resize_obj, "efl.content"), pd->pan); + + return res; +} + +EOLIAN static void +_efl_ui_item_container_efl_object_destructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED) +{ + efl_destructor(efl_super(obj, MY_CLASS)); +} + +static void +deselect_all(Efl_Ui_Item_Container_Data *pd) +{ + while(pd->selected) + { + Eo *item = eina_list_data_get(pd->selected); + efl_ui_item_selected_set(item, EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN(eina_list_data_get(pd->selected) == item); + } +} + +EOLIAN static void +_efl_ui_item_container_efl_object_invalidate(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED) +{ + efl_ui_item_container_position_manager_set(obj, NULL); + + deselect_all(pd); + + while(pd->items) + efl_del(pd->items->data); + + efl_invalidate(efl_super(obj, MY_CLASS)); +} + +EOLIAN static Eina_Iterator* +_efl_ui_item_container_efl_container_content_iterate(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return eina_list_iterator_new(pd->items); +} + +EOLIAN static int +_efl_ui_item_container_efl_container_content_count(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return eina_list_count(pd->items); +} + +EOLIAN static void +_efl_ui_item_container_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Layout_Orientation dir) +{ + if (pd->dir == dir) return; + + pd->dir = dir; + if (pd->pos_man) + efl_ui_layout_orientation_set(pd->pos_man, dir); +} + +EOLIAN static Efl_Ui_Layout_Orientation +_efl_ui_item_container_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return pd->dir; +} + +EOLIAN static void +_efl_ui_item_container_efl_ui_scrollable_interactive_match_content_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Eina_Bool w, Eina_Bool h) +{ + if (pd->match_content.w == w && pd->match_content.h == h) + return; + + pd->match_content.w = w; + pd->match_content.h = h; + + efl_ui_scrollable_match_content_set(pd->smanager, w, h); + flush_min_size(obj, pd); +} + +EOLIAN static void +_efl_ui_item_container_efl_ui_multi_selectable_select_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Select_Mode mode) +{ + pd->mode = mode; + if ((mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || mode == EFL_UI_SELECT_MODE_SINGLE) && + eina_list_count(pd->selected) > 0) + { + Efl_Ui_Item *last = eina_list_last_data_get(pd->selected); + + pd->selected = eina_list_remove_list(pd->selected, eina_list_last(pd->selected)); + deselect_all(pd); + pd->selected = eina_list_append(pd->selected, last); + } + else if (mode == EFL_UI_SELECT_MODE_NONE && pd->selected) + { + deselect_all(pd); + } +} + +EOLIAN static Efl_Ui_Select_Mode +_efl_ui_item_container_efl_ui_multi_selectable_select_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return pd->mode; +} + +static void +_selected_cb(void *data, const Efl_Event *ev) +{ + Eo *obj = data; + MY_DATA_GET(obj, pd); + + if (pd->mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || pd->mode == EFL_UI_SELECT_MODE_SINGLE) + { + //we might get the situation that the item is already in the list and selected again, so just free the list, it will be rebuild below + if (eina_list_data_get(pd->selected) == ev->object) + { + pd->selected = eina_list_free(pd->selected); + } + else + { + deselect_all(pd); + } + + } + else if (pd->mode == EFL_UI_SELECT_MODE_NONE) + { + ERR("Selection while mode is NONE, uncaught state!"); + return; + } + pd->selected = eina_list_append(pd->selected, ev->object); + efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_SELECTED, ev->object); +} + +static void +_unselected_cb(void *data, const Efl_Event *ev) +{ + Eo *obj = data; + MY_DATA_GET(obj, pd); + + pd->selected = eina_list_remove(pd->selected, ev->object); + efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_UNSELECTED, ev->object); +} + +static void +_invalidate_cb(void *data, const Efl_Event *ev) +{ + Eo *obj = data; + MY_DATA_GET(obj, pd); + + unregister_item(obj, pd, ev->object); +} + +static void +_hints_changed_cb(void *data, const Efl_Event *ev) +{ + Eo *obj = data; + MY_DATA_GET(obj, pd); + int idx = eina_list_data_idx(pd->items, ev->object); + + efl_ui_item_position_manager_item_size_changed(pd->pos_man, idx, idx); +} + +static void +_redirect_cb(void *data, const Efl_Event *ev) +{ + Eo *obj = data; + +#define REDIRECT_EVT(item_evt, item) \ + if (item_evt == ev->desc) efl_event_callback_call(obj, item, ev->object); + REDIRECT_EVT(EFL_UI_EVENT_PRESSED, EFL_UI_EVENT_ITEM_PRESSED); + REDIRECT_EVT(EFL_UI_EVENT_UNPRESSED, EFL_UI_EVENT_ITEM_UNPRESSED); + REDIRECT_EVT(EFL_UI_EVENT_LONGPRESSED, EFL_UI_EVENT_ITEM_LONGPRESSED); + REDIRECT_EVT(EFL_UI_EVENT_CLICKED_ANY, EFL_UI_EVENT_ITEM_CLICKED_ANY); + REDIRECT_EVT(EFL_UI_EVENT_CLICKED, EFL_UI_EVENT_ITEM_CLICKED); +#undef REDIRECT_EVT +} + +EFL_CALLBACKS_ARRAY_DEFINE(active_item, + {EFL_GFX_ENTITY_EVENT_HINTS_CHANGED, _hints_changed_cb}, + {EFL_UI_EVENT_ITEM_SELECTED, _selected_cb}, + {EFL_UI_EVENT_ITEM_UNSELECTED, _unselected_cb}, + {EFL_UI_EVENT_PRESSED, _redirect_cb}, + {EFL_UI_EVENT_UNPRESSED, _redirect_cb}, + {EFL_UI_EVENT_LONGPRESSED, _redirect_cb}, + {EFL_UI_EVENT_CLICKED, _redirect_cb}, + {EFL_UI_EVENT_CLICKED_ANY, _redirect_cb}, + {EFL_EVENT_INVALIDATE, _invalidate_cb}, +) + +static Eina_Bool +register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item) +{ + EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(item, EFL_UI_ITEM_CLASS), EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_list_data_find(pd->items, item), EINA_FALSE); + + if (!efl_ui_widget_sub_object_add(obj, item)) + return EINA_FALSE; + + efl_ui_item_container_set(item, obj); + efl_canvas_group_member_add(pd->pan, item); + efl_event_callback_array_add(item, active_item(), obj); + efl_ui_mirrored_set(item, efl_ui_mirrored_get(obj)); + + return EINA_TRUE; +} + +static Eina_Bool +unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item) +{ + Eina_List *elem = eina_list_data_find_list(pd->items, item); + if (!elem) + { + ERR("Item %p is not part of this widget", item); + return EINA_FALSE; + } + + if (!efl_ui_widget_sub_object_del(obj, item)) + return EINA_FALSE; + + unsigned int id = eina_list_data_idx(pd->items, item); + if (pd->obj_accessor.last_index == id) + { + if (eina_list_next(elem)) + { + pd->obj_accessor.current = eina_list_next(elem); + } + else if (eina_list_prev(elem)) + { + pd->obj_accessor.last_index = id-1; + pd->obj_accessor.current = eina_list_prev(elem); + } + else + { + //everything >= length is invalid, and we need that. + pd->obj_accessor.last_index = eina_list_count(pd->items); + pd->obj_accessor.current = NULL; + } + + } + + pd->items = eina_list_remove(pd->items, item); + pd->selected = eina_list_remove(pd->selected, item); + efl_event_callback_array_del(item, active_item(), obj); + efl_ui_item_position_manager_item_removed(pd->pos_man, id, item); + + return EINA_TRUE; +} + +static void +update_pos_man(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj) +{ + int id = eina_list_data_idx(pd->items, subobj); + if (id == 0) + { + pd->obj_accessor.last_index = id; + pd->obj_accessor.current = pd->items; + } + efl_ui_item_position_manager_item_added(pd->pos_man, id, subobj); +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_pack_clear(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + while(pd->items) + { + efl_del(pd->items->data); + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_unpack_all(Eo *obj, Efl_Ui_Item_Container_Data *pd) +{ + while(pd->items) + { + if (!unregister_item(obj, pd, pd->items->data)) + return EINA_FALSE; + } + return EINA_TRUE; +} + +EOLIAN static Efl_Gfx_Entity* +_efl_ui_item_container_efl_pack_linear_pack_unpack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, int index) +{ + Efl_Ui_Item *it = eina_list_nth(pd->items, index_adjust(pd, index)); + + EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); + + if (!unregister_item(obj, pd, it)) + return NULL; + + return it; +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_unpack(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj) +{ + return unregister_item(obj, pd, subobj); +} + + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_pack(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Gfx_Entity *subobj) +{ + return efl_pack_end(obj, subobj); +} + + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_linear_pack_end(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj) +{ + if (!register_item(obj, pd, subobj)) + return EINA_FALSE; + pd->items = eina_list_append(pd->items, subobj); + update_pos_man(obj, pd, subobj); + return EINA_TRUE; +} + + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_linear_pack_begin(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj) +{ + if (!register_item(obj, pd, subobj)) + return EINA_FALSE; + pd->items = eina_list_prepend(pd->items, subobj); + update_pos_man(obj, pd, subobj); + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_linear_pack_before(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing) +{ + Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing); + EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE); + + if (!register_item(obj, pd, subobj)) + return EINA_FALSE; + pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list); + update_pos_man(obj, pd, subobj); + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_linear_pack_after(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing) +{ + Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing); + EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE); + + if (!register_item(obj, pd, subobj)) + return EINA_FALSE; + pd->items = eina_list_append_relative_list(pd->items, subobj, subobj_list); + update_pos_man(obj, pd, subobj); + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_pack_linear_pack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, int index) +{ + Eina_List *subobj_list; + int clamp; + + clamp = clamp_index(pd, index); + index = index_adjust(pd, index); + subobj_list = eina_list_nth_list(pd->items, index); + EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE); + if (!register_item(obj, pd, subobj)) + return EINA_FALSE; + if (clamp == 0) + pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list); + else if (clamp == 1) + pd->items = eina_list_append(pd->items, subobj); + else + pd->items = eina_list_prepend(pd->items, subobj); + update_pos_man(obj, pd, subobj); + return EINA_TRUE; +} + +EOLIAN static int +_efl_ui_item_container_efl_pack_linear_pack_index_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, const Efl_Gfx_Entity *subobj) +{ + return eina_list_data_idx(pd->items, (void*)subobj); +} + +EOLIAN static Efl_Gfx_Entity* +_efl_ui_item_container_efl_pack_linear_pack_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, int index) +{ + return eina_list_nth(pd->items, index_adjust(pd, index)); +} + +static void +_pos_content_size_changed_cb(void *data, const Efl_Event *ev) +{ + Eina_Size2D *size = ev->info; + MY_DATA_GET(data, pd); + + efl_gfx_entity_size_set(pd->sizer, *size); +} + +static void +_pos_content_min_size_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev) +{ + Eina_Size2D *size = ev->info; + MY_DATA_GET(data, pd); + + pd->content_min_size = *size; + + flush_min_size(data, pd); +} + +EFL_CALLBACKS_ARRAY_DEFINE(pos_manager_cbs, + {EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _pos_content_size_changed_cb}, + {EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _pos_content_min_size_changed_cb}, +) + +EOLIAN static void +_efl_ui_item_container_position_manager_set(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item_Position_Manager *layouter) +{ + if (layouter) + EINA_SAFETY_ON_FALSE_RETURN(efl_isa(layouter, EFL_UI_ITEM_POSITION_MANAGER_INTERFACE)); + + if (pd->pos_man) + { + efl_event_callback_array_del(pd->pos_man, pos_manager_cbs(), obj); + efl_ui_item_position_manager_data_access_set(pd->pos_man, NULL, NULL, 0); + efl_del(pd->pos_man); + } + pd->pos_man = layouter; + if (pd->pos_man) + { + efl_parent_set(pd->pos_man, obj); + efl_event_callback_array_add(pd->pos_man, pos_manager_cbs(), obj); + efl_ui_item_position_manager_data_access_set(pd->pos_man, &pd->obj_accessor.pass_on, &pd->size_accessor, eina_list_count(pd->items)); + efl_ui_item_position_manager_viewport_set(pd->pos_man, efl_ui_scrollable_viewport_geometry_get(obj)); + efl_ui_layout_orientation_set(pd->pos_man, pd->dir); + } +} + +EOLIAN static Efl_Ui_Item_Position_Manager* +_efl_ui_item_container_position_manager_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd) +{ + return pd->pos_man; +} + +EOLIAN static Efl_Ui_Focus_Manager* +_efl_ui_item_container_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root) +{ + return efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj, + efl_ui_focus_manager_root_set(efl_added, root)); +} + +EOLIAN static Eina_Bool +_efl_ui_item_container_efl_ui_widget_focus_state_apply(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect EINA_UNUSED) +{ + return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj); +} + +EOLIAN static Efl_Ui_Focus_Object* +_efl_ui_item_container_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Focus_Direction direction) +{ + Eo *new_obj, *focus; + Eina_Size2D step; + + new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction); + focus = efl_ui_focus_manager_focus_get(obj); + step = efl_gfx_hint_size_min_get(focus); + if (!new_obj) + { + Eina_Rect pos = efl_gfx_entity_geometry_get(focus); + Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(pd->smanager); + Eina_Position2D vpos = efl_ui_scrollable_content_pos_get(pd->smanager); + + pos.x = pos.x + vpos.x - view.x; + pos.y = pos.y + vpos.y - view.y; + Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan); + + if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT) + { + if (pos.x < max.x) + { + pos.x = MIN(max.x, pos.x + step.w); + efl_ui_scrollable_scroll(obj, pos, EINA_TRUE); + new_obj = focus; + } + } + else if (direction == EFL_UI_FOCUS_DIRECTION_LEFT) + { + if (pos.x > 0) + { + pos.x = MAX(0, pos.x - step.w); + efl_ui_scrollable_scroll(obj, pos, EINA_TRUE); + new_obj = focus; + } + } + else if (direction == EFL_UI_FOCUS_DIRECTION_UP) + { + if (pos.y > 0) + { + pos.y = MAX(0, pos.y - step.h); + efl_ui_scrollable_scroll(obj, pos, EINA_TRUE); + new_obj = focus; + } + } + else if (direction == EFL_UI_FOCUS_DIRECTION_DOWN) + { + if (pos.y < max.y) + { + pos.y = MAX(0, pos.y + step.h); + efl_ui_scrollable_scroll(obj, pos, EINA_TRUE); + new_obj = focus; + } + } + } + else + { + _item_scroll_internal(obj, pd, new_obj, .0, EINA_TRUE); + } + + return new_obj; +} + +#include "efl_ui_item_container.eo.c" diff --git a/src/lib/elementary/efl_ui_item_container.eo b/src/lib/elementary/efl_ui_item_container.eo new file mode 100644 index 0000000000..b8c6d48df2 --- /dev/null +++ b/src/lib/elementary/efl_ui_item_container.eo @@ -0,0 +1,94 @@ +class @beta Efl.Ui.Item_Container extends Efl.Ui.Layout_Base implements + Efl.Ui.Scrollable_Interactive, + Efl.Ui.Scrollbar, + Efl.Pack_Linear, Efl.Pack_Layout, + Efl.Ui.Layout_Orientable, + Efl.Ui.Selectable, + Efl.Ui.Multi_Selectable, + Efl.Ui.Focus.Manager_Sub, + Efl.Ui.Widget_Focus_Manager +{ + [[This widget displays a list of items in an arrangement controlled by an external $position_manager object. By using different $position_manager objects this widget can show lists of items or two-dimensional grids of items, for example. + + Items inside this widget can be selected according to the $select_mode policy, and retrieved with $selected_items_get. + ]] + event_prefix:efl_ui; + methods { + item_scroll { + [[Bring the passed item into the viewport.]] + params { + @in item: Efl.Ui.Item; [[The target to move in.]] + @in animation: bool; [[If you want to have an animation or not.]] + } + } + item_scroll_align { + [[Bring the passed item into the viewport, place the item accordingly to align in the viewport. + + $align selects the final position of the object inside the viewport. 0.0 will move the object to the first visible position inside the viewport, 1.0 will move it to the last visible position, and values in between will move it accordingly to positions in between, along the scrolling axis. + ]] + params { + @in item: Efl.Ui.Item; [[The target to move in.]] + @in align: double; [[0.0 to have this item at the upper or left side of the viewport, 1.0 to have this item at the lower or right side of the viewport]] + @in animation: bool; [[If you want to have an animation or not.]] + } + } + @property last_selected_item { + [[The item that was selected most recently. + ]] + get {} + values { + item: Efl.Ui.Item; [[The latest selected item.]] + } + } + selected_items_get { + [[Get the selected items iterator. The iterator sequence will be decided by selection.]] + return: iterator @owned @no_unused; [[User has to free the iterator after usage.]] + } + @property position_manager { + [[Position manager object that handles placement of items.]] + values { + position_manager : Efl.Ui.Item_Position_Manager @owned; [[The objects ownership is passed to the item container.]] + } + } + } + implements { + Efl.Object.constructor; + Efl.Object.finalize; + Efl.Object.destructor; + Efl.Object.invalidate; + Efl.Container.content_iterate; + Efl.Container.content_count; + Efl.Ui.Layout_Orientable.orientation { get; set; } + Efl.Ui.Widget.theme_apply; + Efl.Pack.pack_clear; + Efl.Pack.unpack_all; + Efl.Pack.unpack; + Efl.Pack.pack; + Efl.Pack_Linear.pack_end; + Efl.Pack_Linear.pack_begin; + Efl.Pack_Linear.pack_before; + Efl.Pack_Linear.pack_after; + Efl.Pack_Linear.pack_at; + Efl.Pack_Linear.pack_unpack_at; + Efl.Pack_Linear.pack_index_get; + Efl.Pack_Linear.pack_content_get; + Efl.Ui.Scrollable_Interactive.match_content { set; } + Efl.Ui.Multi_Selectable.select_mode {get; set;} + Efl.Ui.Widget_Focus_Manager.focus_manager_create; + Efl.Ui.Widget.focus_state_apply; + Efl.Ui.Focus.Manager.move; + } + events { + item,pressed : Efl.Ui.Item; [[A $press event occurred over an item.]] + item,unpressed : Efl.Ui.Item; [[A $unpress event occurred over an item.]] + item,longpressed : Efl.Ui.Item; [[A $longpressed event occurred over an item.]] + item,clicked : Efl.Ui.Item; [[A $clicked event occurred over an item.]] + item,clicked,any : Efl.Ui.Item; [[A $clicked,any event occurred over an item.]] + } + composite { + Efl.Ui.Scrollable_Interactive; + Efl.Ui.Scrollbar; + Efl.Ui.Focus.Manager; + } +} + diff --git a/src/lib/elementary/efl_ui_item_position_manager.c b/src/lib/elementary/efl_ui_item_position_manager.c new file mode 100644 index 0000000000..abd74f0762 --- /dev/null +++ b/src/lib/elementary/efl_ui_item_position_manager.c @@ -0,0 +1,14 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif + +#define ELM_LAYOUT_PROTECTED +#define EFL_UI_SCROLL_MANAGER_PROTECTED +#define EFL_UI_SCROLLBAR_PROTECTED + +#include +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_ITEM_POSITION_MANAGER_CLASS + +#include "efl_ui_item_position_manager.eo.c" diff --git a/src/lib/elementary/efl_ui_item_position_manager.eo b/src/lib/elementary/efl_ui_item_position_manager.eo new file mode 100644 index 0000000000..c733f376aa --- /dev/null +++ b/src/lib/elementary/efl_ui_item_position_manager.eo @@ -0,0 +1,96 @@ + +interface @beta Efl.Ui.Item_Position_Manager extends Efl.Ui.Layout_Orientable +{ + [[ + This abstracts the basic placement of items in a not defined form under a viewport. + + The interface gets a defined set of elements that is meant to be displayed. The implementation provides a way to calculate the size that is required to display all items. Every time this absolut size of items is changed, content_size,changed is called. + ]] + methods { + @property data_access { + [[This gives access to items to be managed. The manager reads this information and modifies the retrieved items' positions and sizes. + + $obj_access gives access to the graphical entitites to manage. Some of them might be NULL, meaning they are not yet ready to be displayed. Their size in the $size_access array will be correct, though, so other entities can still be positioned correctly. + Typically, only entities inside the viewport will be retrieved. + + $size_access gives access to the 2D sizes for the items to manage. All sizes will always be valid, and might change over time (indicated through the @.item_size_changed method). + The whole range might need to be traversed in order to calculate the position of all items in some arrangements. + ]] + set { + + } + values { + obj_access : accessor; [[The accessor for canvas obejcts, even if the id is valid, the returned object may be NULL]] + size_access : accessor; [[Accessor for the size, returned values are always valid, but might be changed / updated]] + size : int; [[valid size for accessors, 0 <= i < size]] + } + } + @property viewport { + [[This is the position and size of the viewport, where elements are displayed in. + Entities outside this viewport will not be shown.]] + set { + + } + values { + viewport : Eina.Rect; + } + } + @property scroll_position { + [[Move the items relative to the viewport. + + The items that are managed with this position manager might be bigger than the actual viewport. + The positioning of the layer where all items are on is described by these values. + 0.0,0.0 means that layer is moved that the top left items are shown, + 1.0,1.0 means, that the lower right items are shown. + ]] + set { + + } + values { + x : double; [[X position of the scroller, valid form 0 to 1.0]] + y : double; [[Y position of the scroller, valid form 0 to 1.0]] + } + } + position_single_item { + [[Return the position and size of item idx + + This method returns the size and position of the item at $idx. + Even if the item is outside the viewport, the returned rectangle must be valid. The result can be used for scrolling calculations. + ]] + params { + idx : int; [[The id for the item]] + } + return : Eina.Rect; [[Position and Size in canvas coordinations]] + } + item_added { + [[The new item $subobj has been added at the $added_index field. + + The accessor provided through @.data_access will contain updated Entities.]] + params { + added_index : int; + subobj : Efl.Gfx.Entity; + } + } + item_removed { + [[The item $subobj previously at position $removed_index has been removed. + The accessor provided through @.data_access will contain updated Entities. + ]] + params { + removed_index : int; + subobj : Efl.Gfx.Entity; + } + } + item_size_changed { + [[The size of the items from $start_id to $end_id have been changed. + The positioning and sizing of all items will be updated]] + params { + start_id : int; [[The first item that has a new size]] + end_id : int; [[The last item that has a new size]] + } + } + } + events { + content_size,changed : Eina.Size2D; [[Emitted when the aggregate size of all items has changed. This can be used to resize an enclosing Pan object.]] + content_min_size,changed : Eina.Size2D; [[Emitted when the minimum size of all items has changed. The minimum size is the size, that this position_manager needs at *least* to display a single item.]] + } +} diff --git a/src/lib/elementary/efl_ui_list_position_manager.c b/src/lib/elementary/efl_ui_list_position_manager.c new file mode 100644 index 0000000000..19308fb0fc --- /dev/null +++ b/src/lib/elementary/efl_ui_list_position_manager.c @@ -0,0 +1,389 @@ +#ifdef HAVE_CONFIG_H +#include "elementary_config.h" +#endif + + +#include +#include +#include "elm_widget.h" +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_LIST_POSITION_MANAGER_CLASS +#define MY_DATA_GET(obj, pd) \ + Efl_Ui_List_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS); + +typedef struct { + Eina_Accessor *content_acc, *size_acc; + 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; + struct { + unsigned int start_id, end_id; + } prev_run; +} Efl_Ui_List_Position_Manager_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_require(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd) +{ + unsigned int i; + + 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; + + eina_accessor_data_get(pd->size_acc, i, (void**) &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); + } + pd->average_item_size = pd->size_cache[pd->size]/pd->size; +} + +static void +cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd) +{ + if (pd->size_cache) + free(pd->size_cache); + pd->size_cache = NULL; +} + +static inline int +cache_access(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_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_Ui_List_Position_Manager_Data *pd) +{ + Eina_Size2D min_size = EINA_SIZE2D(-1, -1); + cache_require(obj, pd); + + 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); + } + + efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_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; + } + + efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size); +} + +static inline void +vis_change_segment(Efl_Ui_List_Position_Manager_Data *pd, int a, int b, Eina_Bool flag) +{ + for (int i = MIN(a, b); i < MAX(a, b); ++i) + { + Efl_Gfx_Entity *ent = NULL; + + eina_accessor_data_get(pd->content_acc, i, (void**) &ent); + if (ent && !efl_ui_focus_object_focus_get(ent)) + { + efl_gfx_entity_visible_set(ent, flag); + } + } +} + +static void +position_content(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd) +{ + Eina_Rect geom; + Eina_Size2D space_size; + unsigned int start_id = 0, end_id = 0, i; + int relevant_space_size, relevant_viewport; + + if (!pd->size) return; + if (pd->average_item_size <= 0) return; + + //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; + } + + //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 + start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size); + for (; cache_access(obj, pd, start_id) >= relevant_space_size && start_id > 0; 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. + end_id = start_id; + for (; end_id <= pd->size && cache_access(obj, pd, end_id) <= relevant_space_size + relevant_viewport ; end_id ++) { } + end_id = MAX(end_id, start_id + 1); + end_id = MIN(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, start_id, pd->size_cache[start_id], end_id, pd->size_cache[end_id]); + #endif + if (relevant_space_size > 0) + EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, start_id) <= relevant_space_size); + if (end_id != pd->size) + EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, end_id) >= relevant_space_size + relevant_viewport); + EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id); + + //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 + if (end_id <= pd->prev_run.start_id || start_id >= pd->prev_run.end_id) + { + //it is important to first make the segment visible here, and then hide the rest + //otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic. + vis_change_segment(pd, start_id, end_id, EINA_TRUE); + vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE); + } + else + { + vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id)); + vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id)); + } + + geom = pd->viewport; + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.y -= (relevant_space_size - cache_access(obj, pd, start_id)); + else + geom.x -= (relevant_space_size - cache_access(obj, pd, start_id)); + + for (i = start_id; i < end_id; ++i) + { + Eina_Size2D size; + Efl_Gfx_Entity *ent = NULL; + + EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, i, (void**) &size)); + EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent)); + + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.h = size.h; + else + geom.w = size.w; + if (ent) + efl_gfx_entity_geometry_set(ent, geom); + if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) + geom.y += size.h; + else + geom.x += size.w; + } + pd->prev_run.start_id = start_id; + pd->prev_run.end_id = end_id; +} + +static Eina_Value +_rebuild_job_cb(void *data, Eina_Value v EINA_UNUSED, const Eina_Future *f EINA_UNUSED) +{ + MY_DATA_GET(data, pd); + + if (!efl_alive_get(data)) return EINA_VALUE_EMPTY; + + cache_require(data, pd); + recalc_absolut_size(data, pd); + position_content(data, pd); + pd->rebuild_absolut_size = NULL; + + return EINA_VALUE_EMPTY; +} + +static void +schedule_recalc_absolut_size(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd) +{ + if (pd->rebuild_absolut_size) return; + + pd->rebuild_absolut_size = efl_loop_job(efl_app_main_get()); + eina_future_then(pd->rebuild_absolut_size, _rebuild_job_cb, obj); +} + +EOLIAN static void +_efl_ui_list_position_manager_efl_ui_item_position_manager_data_access_set(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, Eina_Accessor *content_access, Eina_Accessor *size_access, int size) +{ + cache_invalidate(obj, pd); + pd->content_acc = content_access; + pd->size_acc = size_access; + pd->size = size; +} + +EOLIAN static void +_efl_ui_list_position_manager_efl_ui_item_position_manager_viewport_set(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, Eina_Rect size) +{ + pd->viewport = size; + + recalc_absolut_size(obj, pd); + position_content(obj, pd); +} + +EOLIAN static void +_efl_ui_list_position_manager_efl_ui_item_position_manager_scroll_position_set(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, double x, double y) +{ + pd->scroll_position.x = x; + pd->scroll_position.y = y; + position_content(obj, pd); +} + +EOLIAN static void +_efl_ui_list_position_manager_efl_ui_item_position_manager_item_added(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_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_ui_list_position_manager_efl_ui_item_position_manager_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_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_ui_list_position_manager_efl_ui_item_position_manager_position_single_item(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, int idx) +{ + Eina_Rect geom; + Eina_Size2D space_size; + int relevant_space_size; + Eina_Size2D size; + + if (!pd->size) return EINA_RECT(0,0,0,0); + + //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; + + eina_accessor_data_get(pd->size_acc, idx, (void**)&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_ui_list_position_manager_efl_ui_item_position_manager_item_size_changed(Eo *obj, Efl_Ui_List_Position_Manager_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_ui_list_position_manager_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_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, 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); + recalc_absolut_size(obj, pd); + position_content(obj, pd); +} + +EOLIAN static Efl_Ui_Layout_Orientation +_efl_ui_list_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd) +{ + return pd->dir; +} + +EOLIAN static void +_efl_ui_list_position_manager_efl_object_destructor(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd) +{ + if (pd->rebuild_absolut_size) + eina_future_cancel(pd->rebuild_absolut_size); + + efl_destructor(efl_super(obj, MY_CLASS)); +} + + +#include "efl_ui_list_position_manager.eo.c" diff --git a/src/lib/elementary/efl_ui_list_position_manager.eo b/src/lib/elementary/efl_ui_list_position_manager.eo new file mode 100644 index 0000000000..5c7135b038 --- /dev/null +++ b/src/lib/elementary/efl_ui_list_position_manager.eo @@ -0,0 +1,18 @@ +class @beta Efl.Ui.List_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager +{ + [[Implementation of Efl.Ui.Item_Position_manager for a list + + Every item in the list will get at least his minsize applied, changes to the misize are listend to and change the layouting of all items. This supports the vertical and horizontal orientation. + ]] + implements { + Efl.Object.destructor; + Efl.Ui.Item_Position_Manager.data_access {set;} + Efl.Ui.Item_Position_Manager.viewport {set;} + Efl.Ui.Item_Position_Manager.scroll_position {set;} + Efl.Ui.Item_Position_Manager.item_added; + Efl.Ui.Item_Position_Manager.item_removed; + Efl.Ui.Item_Position_Manager.position_single_item; + Efl.Ui.Item_Position_Manager.item_size_changed; + Efl.Ui.Layout_Orientable.orientation {set; get;} + } +} diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 92e780d728..3aef4df9ce 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -181,6 +181,10 @@ pub_eo_files = [ 'efl_ui_clickable.eo', 'efl_ui_clickable_util.eo', 'efl_ui_format.eo', + 'efl_ui_item_container.eo', + 'efl_ui_item_position_manager.eo', + 'efl_ui_list_position_manager.eo', + 'efl_ui_grid_position_manager.eo', ] foreach eo_file : pub_eo_files @@ -938,6 +942,10 @@ elementary_src = [ 'efl_ui_clickable_util.c', 'efl_ui_format.c', 'efl_ui_scroll_util.c', + 'efl_ui_item_container.c', + 'efl_ui_item_position_manager.c', + 'efl_ui_list_position_manager.c', + 'efl_ui_grid_position_manager.c', ] elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl] diff --git a/src/tests/elementary/efl_ui_suite.c b/src/tests/elementary/efl_ui_suite.c index 74a9f909fc..bc8a1c6902 100644 --- a/src/tests/elementary/efl_ui_suite.c +++ b/src/tests/elementary/efl_ui_suite.c @@ -32,6 +32,9 @@ static const Efl_Test_Case etc[] = { { "efl_ui_win", efl_ui_test_win }, { "efl_ui_spin", efl_ui_test_spin }, { "efl_ui_spin_button", efl_ui_test_spin_button }, + { "efl_ui_item_container", efl_ui_test_item_container }, + { "efl_ui_grid_container", efl_ui_test_grid_container }, + { "efl_ui_list_container", efl_ui_test_list_container }, { NULL, NULL } }; diff --git a/src/tests/elementary/efl_ui_suite.h b/src/tests/elementary/efl_ui_suite.h index 7fd8665f96..16170efde1 100644 --- a/src/tests/elementary/efl_ui_suite.h +++ b/src/tests/elementary/efl_ui_suite.h @@ -43,6 +43,9 @@ void efl_ui_test_slider(TCase *tc); void efl_ui_test_win(TCase *tc); void efl_ui_test_spin(TCase *tc); void efl_ui_test_spin_button(TCase *tc); +void efl_ui_test_item_container(TCase *tc); +void efl_ui_test_list_container(TCase *tc); +void efl_ui_test_grid_container(TCase *tc); void loop_timer_interval_set(Eo *obj, double in); diff --git a/src/tests/elementary/efl_ui_test_grid_container.c b/src/tests/elementary/efl_ui_test_grid_container.c new file mode 100644 index 0000000000..26549e9893 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_grid_container.c @@ -0,0 +1,36 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include "efl_ui_suite.h" +#include "efl_ui_test_item_container_common.h" + +static Eo *win; + +static void +item_container_setup() +{ + Eo * list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS); + position_manager = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS); + win = win_add(); + item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, + efl_ui_item_container_position_manager_set(efl_added, list)); + +} + +static void +item_container_teardown() +{ + item_container = NULL; + position_manager = NULL; + win = NULL; +} + +void efl_ui_test_grid_container(TCase *tc) +{ + tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown); + tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown); + efl_ui_test_item_container_common_add(tc); + efl_ui_test_position_manager_common_add(tc); +} diff --git a/src/tests/elementary/efl_ui_test_item_container.c b/src/tests/elementary/efl_ui_test_item_container.c new file mode 100644 index 0000000000..fbca2f7f85 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_item_container.c @@ -0,0 +1,54 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include "efl_ui_suite.h" +#include "efl_ui_test_item_container_common.h" + +static Eo* win; + +static void +item_container_setup() +{ + win = win_add(); +} + +static void +item_container_teardown() +{ + win = NULL; +} + +EFL_START_TEST(finalizer_check) +{ + Eo *grid = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS); + Eo *list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS); + Eo *random_obj = efl_add(EFL_UI_BUTTON_CLASS, win); + Eo *o; + + EXPECT_ERROR_START; + ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win), NULL); + EXPECT_ERROR_END; + + EXPECT_ERROR_START; + ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, random_obj)), NULL); + EXPECT_ERROR_END; + + o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, grid)); + ck_assert_ptr_ne(o, NULL); + ck_assert_ptr_eq(efl_parent_get(grid), o); + efl_del(o); + o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, list)); + ck_assert_ptr_ne(o, NULL); + ck_assert_ptr_eq(efl_parent_get(list), o); + efl_del(o); +} +EFL_END_TEST + +void efl_ui_test_item_container(TCase *tc) +{ + tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown); + tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown); + tcase_add_test(tc, finalizer_check); +} diff --git a/src/tests/elementary/efl_ui_test_item_container_common.c b/src/tests/elementary/efl_ui_test_item_container_common.c new file mode 100644 index 0000000000..e97f3e7971 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_item_container_common.c @@ -0,0 +1,233 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include "efl_ui_suite.h" +#include "efl_ui_test_item_container_common.h" + +Eo *item_container; + +void +fill_items(const Efl_Class *klass) +{ + for (int i = 0; i < 3; ++i) + { + char buf[PATH_MAX]; + Eo *it = efl_add(klass, item_container); + + snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13); + efl_text_set(it, buf); + efl_pack_end(item_container, it); + } +} + +static void +_set_pointer_quit(void *data, const Efl_Event *ev) +{ + Efl_Ui_Item **b = data; + + ck_assert_ptr_eq(*b, NULL); + *b = ev->info; +} + +static void +_iterator_to_array(Eina_Array **arr, Eina_Iterator *iter) +{ + Efl_Ui_Widget *widget; + + *arr = eina_array_new(10); + + EINA_ITERATOR_FOREACH(iter, widget) + { + eina_array_push(*arr, widget); + } + eina_iterator_free(iter); +} + +EFL_START_TEST(test_multi_select) +{ + Efl_Ui_Item *selected = NULL; + Efl_Ui_Item *unselected = NULL; + Eina_Array *arr_selected; + efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); + fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0)); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2)); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_TRUE); + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE); + ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2)); + _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container)); + ck_assert_int_eq(eina_array_count(arr_selected), 2); + ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 0)); + ck_assert_ptr_eq(eina_array_data_get(arr_selected, 1), efl_pack_content_get(item_container, 2)); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); +} +EFL_END_TEST + +EFL_START_TEST(test_multi_select_removal) +{ + Efl_Ui_Item *selected = NULL; + Efl_Ui_Item *unselected = NULL; + Eina_Array *arr_selected; + efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); + fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE); + selected = NULL;//No need to ckeck the flag, we asserted in the tcase before + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + selected = NULL;//No need to ckeck the flag, we asserted in the tcase before + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_FALSE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0)); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_FALSE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 2)); + selected = NULL; + unselected = NULL; + + ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL); + _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container)); + ck_assert_int_eq(eina_array_count(arr_selected), 0); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); +} +EFL_END_TEST + +EFL_START_TEST(test_single_select) +{ + Efl_Ui_Item *selected = NULL; + Efl_Ui_Item *unselected = NULL; + Eina_Array *arr_selected; + + efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); + fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0)); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2)); + ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0)); + selected = NULL; + unselected = NULL; + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE); + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE); + ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2)); + _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container)); + ck_assert_int_eq(eina_array_count(arr_selected), 1); + ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2)); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); +} +EFL_END_TEST + +EFL_START_TEST(test_single_select_always) +{ + Efl_Ui_Item *selected = NULL; + Efl_Ui_Item *unselected = NULL; + Eina_Array *arr_selected; + + efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE_ALWAYS); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); + fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0)); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2)); + ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0)); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2)); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE); + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE); + ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2)); + _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container)); + ck_assert_int_eq(eina_array_count(arr_selected), 1); + ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2)); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); +} +EFL_END_TEST + +EFL_START_TEST(test_none_select) +{ + Efl_Ui_Item *selected = NULL; + Efl_Ui_Item *unselected = NULL; + Eina_Array *arr_selected; + + efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_NONE); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); + fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS); + + efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE); + ck_assert_ptr_eq(selected, NULL); + ck_assert_ptr_eq(unselected, NULL); + selected = NULL; + unselected = NULL; + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE); + ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_FALSE); + ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL); + _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container)); + ck_assert_int_eq(eina_array_count(arr_selected), 0); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected); + efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected); +} +EFL_END_TEST + +void efl_ui_test_item_container_common_add(TCase *tc) +{ + tcase_add_test(tc, test_multi_select); + tcase_add_test(tc, test_multi_select_removal); + tcase_add_test(tc, test_single_select); + tcase_add_test(tc, test_none_select); + tcase_add_test(tc, test_single_select_always); +} diff --git a/src/tests/elementary/efl_ui_test_item_container_common.h b/src/tests/elementary/efl_ui_test_item_container_common.h new file mode 100644 index 0000000000..3eb8be6b18 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_item_container_common.h @@ -0,0 +1,11 @@ +#ifndef EFL_UI_TEST_ITEM_CONTAINER_COMMON_H +#define EFL_UI_TEST_ITEM_CONTAINER_COMMON_H 1 + +extern Eo *item_container; +extern Eo *position_manager; + +void fill_items(const Efl_Class *klass); +void efl_ui_test_item_container_common_add(TCase *tc); +void efl_ui_test_position_manager_common_add(TCase *tc); + +#endif diff --git a/src/tests/elementary/efl_ui_test_list_container.c b/src/tests/elementary/efl_ui_test_list_container.c new file mode 100644 index 0000000000..85208a8de1 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_list_container.c @@ -0,0 +1,36 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include "efl_ui_suite.h" +#include "efl_ui_test_item_container_common.h" + +static Eo *win; + +static void +item_container_setup() +{ + Eo * list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS); + position_manager = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS); + win = win_add(); + item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, + efl_ui_item_container_position_manager_set(efl_added, list)); + +} + +static void +item_container_teardown() +{ + item_container = NULL; + position_manager = NULL; + win = NULL; +} + +void efl_ui_test_list_container(TCase *tc) +{ + tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown); + tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown); + efl_ui_test_item_container_common_add(tc); + efl_ui_test_position_manager_common_add(tc); +} diff --git a/src/tests/elementary/efl_ui_test_position_manager_common.c b/src/tests/elementary/efl_ui_test_position_manager_common.c new file mode 100644 index 0000000000..0aba08b147 --- /dev/null +++ b/src/tests/elementary/efl_ui_test_position_manager_common.c @@ -0,0 +1,192 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#include "efl_ui_suite.h" +#include "efl_ui_test_item_container_common.h" + +Eo *position_manager; + +static Eo* win; + +static Eina_Array *arr_obj; +static Eina_Inarray *arr_size; +static Eina_Accessor inner_size_acc; +static Eina_Accessor *size_acc; +static void +item_container_setup() +{ + win = win_add(); +} + +static void +item_container_teardown() +{ + win = NULL; +} + +static Eina_Bool +_get_at(Eina_Accessor *it EINA_UNUSED, unsigned int idx, void **data) +{ + Eina_Size2D *result_ptr = (void*)data; + Eina_Size2D *inner_result; + + if (!eina_accessor_data_get(size_acc, idx, (void*)&inner_result)) + return EINA_FALSE; + *result_ptr = *inner_result; + return EINA_TRUE; +} + +static void +_free_cb(Eina_Accessor *it EINA_UNUSED) +{ + eina_accessor_free(size_acc); +} + +static Eina_Bool +_lock_cb(Eina_Accessor *it EINA_UNUSED) +{ + return eina_accessor_lock(size_acc); +} + +static Eina_Accessor* +_clone_cb(Eina_Accessor *it EINA_UNUSED) +{ + return eina_accessor_clone(size_acc); +} + + +static void +_initial_setup(void) +{ + arr_obj = eina_array_new(10); + arr_size = eina_inarray_new(sizeof(Eina_Size2D), 10); + size_acc = eina_inarray_accessor_new(arr_size); + + inner_size_acc.version = EINA_ACCESSOR_VERSION; + EINA_MAGIC_SET(&inner_size_acc, EINA_MAGIC_ACCESSOR); + inner_size_acc.get_at = _get_at; + inner_size_acc.free = _free_cb; + inner_size_acc.lock = _lock_cb; + inner_size_acc.clone = _clone_cb; + + efl_ui_item_position_manager_data_access_set(position_manager, + eina_array_accessor_new(arr_obj), + &inner_size_acc, 0); +} + +static int +_add_item(Eo *obj, Eina_Size2D size) +{ + int idx = eina_array_count(arr_obj); + EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_array_count(arr_obj) == eina_inarray_count(arr_size), -1); + + eina_array_push(arr_obj, (void*)0x1); //wtf + eina_array_data_set(arr_obj, idx, obj); + eina_inarray_push(arr_size, &size); + + + efl_ui_item_position_manager_item_added(position_manager, idx, obj); + + return idx; +} + +static void +_update_item(int index, Eo *obj, Eina_Size2D size) +{ + Eina_Size2D *s; + + eina_array_data_set(arr_obj, index, obj); + s = eina_inarray_nth(arr_size, index); + *s = size; +} + +static void +_ticker(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED) +{ + efl_loop_quit(efl_main_loop_get(), EINA_VALUE_EMPTY); +} + +static void +_iterate_a_few(void) +{ + efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(), + efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _ticker, NULL), + efl_loop_timer_interval_set(efl_added, 0.1)); + efl_loop_begin(efl_main_loop_get()); +} + +EFL_START_TEST(no_crash1) +{ + _initial_setup(); + + //try to resize the viewport while we have no item + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0)); + _iterate_a_few(); + _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0)); + _iterate_a_few(); +} +EFL_END_TEST + +EFL_START_TEST(no_crash2) +{ + _initial_setup(); + + //try to resize the viewport while we have no item + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0)); + _iterate_a_few(); + _add_item(NULL, EINA_SIZE2D(20, 20)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0)); + _iterate_a_few(); + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200)); + _iterate_a_few(); + _update_item(0, efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20)); + _iterate_a_few(); +} +EFL_END_TEST + +static void +_content_size_cb(void *data, const Efl_Event *ev) +{ + Eina_Size2D *size = data; + *size = *((Eina_Size2D*)ev->info); +} + +EFL_START_TEST(viewport_newsize_event_result) +{ + Eina_Size2D size = EINA_SIZE2D(-2, -2), min_size = EINA_SIZE2D(-2, -2); + efl_event_callback_add(position_manager, + EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _content_size_cb, &size); + efl_event_callback_add(position_manager, + EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _content_size_cb, &min_size); + _initial_setup(); + _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20)); + _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 30)); + + efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(0, 0, 200, 200)); + _iterate_a_few(); + + ck_assert_int_ne(size.w, -2); + ck_assert_int_ne(size.h, -2); + ck_assert_int_ne(min_size.w, -2); + ck_assert_int_ne(min_size.h, -2); +} +EFL_END_TEST + +void efl_ui_test_position_manager_common_add(TCase *tc) +{ + tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown); + tcase_add_test(tc, no_crash1); + tcase_add_test(tc, no_crash2); + tcase_add_test(tc, viewport_newsize_event_result); +} diff --git a/src/tests/elementary/meson.build b/src/tests/elementary/meson.build index 7e9454fd95..7623a5cc52 100644 --- a/src/tests/elementary/meson.build +++ b/src/tests/elementary/meson.build @@ -146,6 +146,11 @@ efl_ui_suite_src = [ 'efl_ui_test_spin.c', 'efl_ui_test_spin_button.c', 'efl_ui_test_slider.c', + 'efl_ui_test_item_container_common.c', + 'efl_ui_test_item_container.c', + 'efl_ui_test_list_container.c', + 'efl_ui_test_grid_container.c', + 'efl_ui_test_position_manager_common.c', ] efl_ui_suite = executable('efl_ui_suite', diff --git a/src/tests/elementary/spec/efl_test_pack_linear.c b/src/tests/elementary/spec/efl_test_pack_linear.c index 720d090925..4da2f8538e 100644 --- a/src/tests/elementary/spec/efl_test_pack_linear.c +++ b/src/tests/elementary/spec/efl_test_pack_linear.c @@ -9,9 +9,10 @@ /* spec-meta-start {"test-interface":"Efl.Pack_Linear", - "test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container"], + "test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container", "Test.Efl.Ui.Item_Container_List"], "custom-mapping" : { - "Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS" + "Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS", + "Test.Efl.Ui.Item_Container_List" : "EFL_UI_LIST_DEFAULT_ITEM_CLASS" } } spec-meta-end */ diff --git a/src/tests/elementary/spec/efl_ui_spec_suite.c b/src/tests/elementary/spec/efl_ui_spec_suite.c index dc5462a89e..c20d974465 100644 --- a/src/tests/elementary/spec/efl_ui_spec_suite.c +++ b/src/tests/elementary/spec/efl_ui_spec_suite.c @@ -8,6 +8,16 @@ #include "suite_helpers.h" #include "eo_internal.h" +//helper functions for custom widget intialization +EOLIAN static Efl_Object* +_test_efl_ui_item_container_list_efl_object_constructor(Eo *obj, void *pd EINA_UNUSED) +{ + efl_constructor(efl_super(obj, TEST_EFL_UI_ITEM_CONTAINER_LIST_CLASS)); + efl_ui_item_container_position_manager_set(obj, efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS)); + + return obj; +} + Evas_Object *win = NULL; Evas_Object *widget = NULL; const Efl_Class *test_content_klass = NULL; @@ -63,3 +73,5 @@ main(int argc, char **argv) return (failed_count == 0) ? 0 : 255; } + +#include "test_efl_ui_item_container_list.eo.c" diff --git a/src/tests/elementary/spec/efl_ui_spec_suite.h b/src/tests/elementary/spec/efl_ui_spec_suite.h index 01be37652d..6ea53fc801 100644 --- a/src/tests/elementary/spec/efl_ui_spec_suite.h +++ b/src/tests/elementary/spec/efl_ui_spec_suite.h @@ -5,6 +5,7 @@ #define EFL_NOLEGACY_API_SUPPORT #include +#include "test_efl_ui_item_container_list.eo.h" #include "../efl_check.h" extern Evas_Object *win; diff --git a/src/tests/elementary/spec/meson.build b/src/tests/elementary/spec/meson.build index eb047ed957..d9fadd9c68 100644 --- a/src/tests/elementary/spec/meson.build +++ b/src/tests/elementary/spec/meson.build @@ -1,3 +1,6 @@ +priv_eo_files = [ + 'test_efl_ui_item_container_list.eo', +] efl_ui_suite_behavior_test_files = files([ 'efl_test_pack.c', @@ -21,6 +24,20 @@ efl_ui_suite_behavior_src = files([ test_generator = find_program('generator.py') + +priv_eo_file_target = [] +foreach eo_file : priv_eo_files + priv_eo_file_target += custom_target('eolian_gen_' + eo_file, + input : eo_file, + output : [eo_file + '.h'], + depfile : eo_file + '.d', + command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories, + '-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'), + '-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'), + '-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'), + '-gchd', '@INPUT@']) +endforeach + generated_test_parts = custom_target('generate_test_suite', input: efl_ui_suite_behavior_test_files, output: 'efl_ui_spec_suite_gen.x', @@ -28,7 +45,7 @@ generated_test_parts = custom_target('generate_test_suite', ) efl_ui_behavior_suite = executable('efl_ui_spec_suite', - efl_ui_suite_behavior_src + [generated_test_parts], + efl_ui_suite_behavior_src + [generated_test_parts] + [priv_eo_file_target], dependencies: [check, eina, elementary, elementary_deps], include_directories : [config_dir] + [elementary_config_dir] + [include_directories('../')], c_args : [ diff --git a/src/tests/elementary/spec/test_efl_ui_item_container_list.eo b/src/tests/elementary/spec/test_efl_ui_item_container_list.eo new file mode 100644 index 0000000000..d61bc0fcaa --- /dev/null +++ b/src/tests/elementary/spec/test_efl_ui_item_container_list.eo @@ -0,0 +1,6 @@ +class @beta Test.Efl.Ui.Item_Container_List extends Efl.Ui.Item_Container { + data: null; + implements { + Efl.Object.constructor; + } +}