summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2019-06-29 16:19:08 +0200
committerCedric BAIL <cedric.bail@free.fr>2019-07-24 10:38:22 -0700
commit577b82dad69c7f07856cbb254fcc60aab6f90999 (patch)
tree6292ee6c022553d826026362f33fc692f8f67e2c
parent0cd5427c3f556135fffff79193fb03de6036eab4 (diff)
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 <michael.blumenkrantz@gmail.com> Reviewed-by: Cedric BAIL <cedric.bail@free.fr> Differential Revision: https://phab.enlightenment.org/D9285
-rw-r--r--data/elementary/themes/default.edc1
-rw-r--r--data/elementary/themes/edc/efl/item_container.edc3
-rw-r--r--src/benchmarks/elementary/item_container.c100
-rw-r--r--src/benchmarks/elementary/meson.build7
-rw-r--r--src/bin/elementary/meson.build1
-rw-r--r--src/bin/elementary/test.c5
-rw-r--r--src/bin/elementary/test_ui_item_container.c272
-rw-r--r--src/lib/elementary/Efl_Ui.h9
-rw-r--r--src/lib/elementary/efl_ui_grid_position_manager.c321
-rw-r--r--src/lib/elementary/efl_ui_grid_position_manager.eo17
-rw-r--r--src/lib/elementary/efl_ui_item_container.c857
-rw-r--r--src/lib/elementary/efl_ui_item_container.eo94
-rw-r--r--src/lib/elementary/efl_ui_item_position_manager.c14
-rw-r--r--src/lib/elementary/efl_ui_item_position_manager.eo96
-rw-r--r--src/lib/elementary/efl_ui_list_position_manager.c389
-rw-r--r--src/lib/elementary/efl_ui_list_position_manager.eo18
-rw-r--r--src/lib/elementary/meson.build8
-rw-r--r--src/tests/elementary/efl_ui_suite.c3
-rw-r--r--src/tests/elementary/efl_ui_suite.h3
-rw-r--r--src/tests/elementary/efl_ui_test_grid_container.c36
-rw-r--r--src/tests/elementary/efl_ui_test_item_container.c54
-rw-r--r--src/tests/elementary/efl_ui_test_item_container_common.c233
-rw-r--r--src/tests/elementary/efl_ui_test_item_container_common.h11
-rw-r--r--src/tests/elementary/efl_ui_test_list_container.c36
-rw-r--r--src/tests/elementary/efl_ui_test_position_manager_common.c192
-rw-r--r--src/tests/elementary/meson.build5
-rw-r--r--src/tests/elementary/spec/efl_test_pack_linear.c5
-rw-r--r--src/tests/elementary/spec/efl_ui_spec_suite.c12
-rw-r--r--src/tests/elementary/spec/efl_ui_spec_suite.h1
-rw-r--r--src/tests/elementary/spec/meson.build19
-rw-r--r--src/tests/elementary/spec/test_efl_ui_item_container_list.eo6
31 files changed, 2824 insertions, 4 deletions
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 {
207#include "edc/efl/tab_pager.edc" 207#include "edc/efl/tab_pager.edc"
208#include "edc/efl/tab_bar.edc" 208#include "edc/efl/tab_bar.edc"
209#include "edc/efl/tab_page.edc" 209#include "edc/efl/tab_page.edc"
210#include "edc/efl/item_container.edc"
210} 211}
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 @@
1group { "efl/item_container";
2 inherit: "efl/scroller";
3}
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 @@
1#include <Efl_Ui.h>
2
3static Eo *first, *last, *middle;
4static int timer = 15;
5static int frames = 0;
6static double start_time;
7
8static void
9_timer_tick(void *data, const Efl_Event *ev)
10{
11 if (timer % 2 == 0)
12 {
13 efl_ui_item_container_item_scroll(data, last, EINA_TRUE);
14 }
15 else
16 {
17 efl_ui_item_container_item_scroll(data, first, EINA_TRUE);
18 }
19
20 timer--;
21
22 if (timer == 0)
23 {
24 double runtime = ecore_time_get() - start_time;
25 efl_loop_quit(efl_app_main_get(), EINA_VALUE_EMPTY);
26 efl_del(ev->object);
27 printf("We did %d frames in %f s seconds\n", frames, runtime);
28 printf("FPS: %f\n", ((double)frames / runtime));
29
30 }
31}
32
33static void
34_caputure(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
35{
36 frames ++;
37}
38
39static void
40_started_cb(void *data, const Efl_Event *ev EINA_UNUSED)
41{
42 efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
43 efl_loop_timer_interval_set(efl_added, 1.0),
44 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _timer_tick, data)
45 );
46 start_time = ecore_time_get();
47 efl_event_callback_add(evas_object_evas_get(data), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _caputure, data);
48 efl_del(ev->object);
49}
50
51static void
52_first_frame_cb(void *data, const Efl_Event *ev EINA_UNUSED)
53{
54 efl_ui_item_container_item_scroll(data, middle, EINA_FALSE);
55 //give time to stabelize
56 efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
57 efl_loop_timer_interval_set(efl_added, 15.0),
58 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _started_cb, data)
59 );
60 efl_event_callback_del(ev->object, EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, data);
61}
62
63EAPI_MAIN void
64efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
65{
66 Eo *win, *item_container, *list;
67
68 win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
69 efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC),
70 efl_text_set(efl_added, "Efl.Ui.Item_Container benchmark"),
71 efl_ui_win_autodel_set(efl_added, EINA_TRUE)
72 );
73
74 list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
75 item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
76 efl_ui_item_container_position_manager_set(efl_added, list));
77 efl_content_set(win, item_container);
78
79 printf("Building 5000 objects\n");
80 for (int i = 0; i < 5000; ++i)
81 {
82 Eo *il = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container);
83 double r = 10+((double)190/(double)10)*(i%10);
84
85 if (i == 0)
86 first = il;
87 else if (i == 2500)
88 middle = il;
89 else if (i == 4999)
90 last = il;
91 efl_gfx_color_set(il, r, 10, 10, 255);
92 efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40));
93 efl_pack_end(item_container, il);
94 }
95 printf("Done!\n");
96 efl_gfx_entity_size_set(win, EINA_SIZE2D(200, 200));
97
98 efl_event_callback_add(evas_object_evas_get(win), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, item_container);
99}
100EFL_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',
6benchmark('focus_widget_tree', focus_widget_tree_bench, 6benchmark('focus_widget_tree', focus_widget_tree_bench,
7 args: ['5'], 7 args: ['5'],
8) 8)
9
10item_container = executable('item_container',
11 'item_container.c',
12 dependencies: [elementary, ecore_input_evas, eio],
13)
14
15benchmark('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 = [
156 'test_gesture_framework.c', 156 'test_gesture_framework.c',
157 'test_ui_tab_pager.c', 157 'test_ui_tab_pager.c',
158 'test_ui_relative_layout.c', 158 'test_ui_relative_layout.c',
159 'test_ui_item_container.c',
159 'test.h' 160 'test.h'
160] 161]
161 162
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);
394 394
395void test_ui_relative_layout(void *data, Evas_Object *obj, void *event_info); 395void test_ui_relative_layout(void *data, Evas_Object *obj, void *event_info);
396void test_efl_ui_radio(void *data, Evas_Object *obj, void *event_info); 396void test_efl_ui_radio(void *data, Evas_Object *obj, void *event_info);
397 397void test_efl_ui_item_container_list(void *data, Evas_Object *obj, void *event_info );
398void test_efl_ui_item_container_grid(void *data, Evas_Object *obj, void *event_info);
398static void _list_udpate(void); 399static void _list_udpate(void);
399 400
400static Evas_Object *win, *tbx, *entry; // TODO: refactoring 401static Evas_Object *win, *tbx, *entry; // TODO: refactoring
@@ -868,6 +869,8 @@ add_tests:
868 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table (Linear API)", test_ui_table_linear); 869 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table (Linear API)", test_ui_table_linear);
869 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table_Static", test_ui_table_static); 870 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table_Static", test_ui_table_static);
870 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Relative_Layout", test_ui_relative_layout); 871 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Relative_Layout", test_ui_relative_layout);
872 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container List", test_efl_ui_item_container_list);
873 ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container Grid", test_efl_ui_item_container_grid);
871 874
872 //------------------------------// 875 //------------------------------//
873 ADD_TEST_EO(NULL, "Events", "Event Refeed", test_events); 876 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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6
7static void
8_selection_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
9{
10 if (efl_ui_check_selected_get(ev->object))
11 efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_VERTICAL);
12 else
13 efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL);
14}
15
16static void
17_scroll_to_animated_cb(void *data, const Efl_Event *ev EINA_UNUSED)
18{
19 Efl_Ui_Widget *element_1154 = efl_pack_content_get(data, 1154);
20
21 EINA_SAFETY_ON_NULL_RETURN(element_1154);
22
23 efl_ui_item_container_item_scroll(data, element_1154, EINA_TRUE);
24}
25
26static void
27_scroll_to_cb(void *data, const Efl_Event *ev EINA_UNUSED)
28{
29 Efl_Ui_Widget *element_10 = efl_pack_content_get(data, 10);
30
31 EINA_SAFETY_ON_NULL_RETURN(element_10);
32
33 efl_ui_item_container_item_scroll(data, element_10, EINA_FALSE);
34}
35
36static void
37_change_min_size_cb(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
38{
39 static Eina_Bool b = EINA_FALSE;
40 Efl_Ui_Widget *element_0 = efl_pack_content_get(data, 0);
41
42 EINA_SAFETY_ON_NULL_RETURN(element_0);
43
44 if (!b)
45 {
46 efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 200));
47 }
48 else
49 {
50 efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 40));
51 }
52 b = !b;
53}
54
55typedef struct {
56 Efl_Ui_Check *v, *h;
57 Efl_Ui_Item_Container *c;
58} Match_Content_Ctx;
59
60static void
61_selection_changed_match_content_cb(void *data, const Efl_Event *ev EINA_UNUSED)
62{
63 Match_Content_Ctx *c = data;
64 Eina_Bool v,h;
65
66 v = efl_ui_check_selected_get(c->v);
67 h = efl_ui_check_selected_get(c->h);
68
69 efl_ui_scrollable_match_content_set(c->c, v, h);
70}
71
72static void
73_widget_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
74{
75 free(data);
76}
77
78static void
79_add_item(Efl_Ui_Item_Container *c)
80{
81 Efl_Class *itc = efl_key_data_get(c, "__item_class");
82 char buf[PATH_MAX];
83 int r = 0, g = 0, b = 0;
84 int i = efl_content_count(c);
85 Eo *rect;
86 Eo *il;
87
88
89 il = efl_add(itc, c);
90
91 snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13);
92 efl_text_set(il, buf);
93
94 rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(c));
95 switch (i % 5)
96 {
97 case 0:
98 r = 255;
99 break;
100 case 1:
101 g = 255;
102 break;
103 case 2:
104 b = 255;
105 break;
106 case 3:
107 r = g = b = 255;
108 break;
109 case 4:
110 r = g = b = 0;
111 break;
112 }
113 efl_gfx_color_set(rect, r, g, b, 255);
114 efl_content_set(il, rect);
115 if (itc == EFL_UI_GRID_DEFAULT_ITEM_CLASS)
116 efl_gfx_hint_size_min_set(il, EINA_SIZE2D(100, 180));
117 else
118 efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40));
119 efl_pack_end(c, il);
120}
121
122static void
123_remove_all_cb(void *data, const Efl_Event *ev EINA_UNUSED)
124{
125 efl_pack_clear(data);
126}
127
128static void
129_add_one_item(void *data, const Efl_Event *ev EINA_UNUSED)
130{
131 _add_item(data);
132}
133
134static void
135_add_thousend_items(void *data, const Efl_Event *ev EINA_UNUSED)
136{
137 for (int i = 0; i < 1000; ++i)
138 {
139 _add_item(data);
140 }
141}
142
143static void
144_select_value_cb(void *data, const Efl_Event *ev)
145{
146 Efl_Ui_Item_Container *c = data;
147
148 efl_ui_select_mode_set(c, efl_ui_radio_group_selected_value_get(ev->object));
149}
150
151void create_item_container_ui(Efl_Ui_Item_Position_Manager *manager, const Efl_Class *item, const char *name)
152{
153 Efl_Ui_Win *win, *o, *tbl, *item_container, *bx;
154 Match_Content_Ctx *ctx = calloc(1, sizeof(*ctx));
155
156 win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
157 efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC),
158 efl_text_set(efl_added, name),
159 efl_ui_win_autodel_set(efl_added, EINA_TRUE));
160 tbl = efl_add(EFL_UI_TABLE_CLASS, win);
161 efl_content_set(win, tbl);
162
163 item_container = o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
164 efl_ui_item_container_position_manager_set(efl_added, manager));
165 efl_key_data_set(o, "__item_class", item);
166 efl_event_callback_add(o, EFL_EVENT_DEL, _widget_del_cb, ctx);
167 for (int i = 0; i < 2000; ++i)
168 {
169 _add_item(o);
170 }
171 efl_pack_table(tbl, o, 1, 0, 1, 11);
172 ctx->c = o;
173
174 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
175 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
176 efl_gfx_hint_align_set(efl_added, 0, 0.5));
177 efl_text_set(o, "Scroll to 1154 ANIMATED");
178 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_animated_cb, item_container);
179 efl_pack_table(tbl, o, 0, 1, 1, 1);
180
181 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
182 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
183 efl_gfx_hint_align_set(efl_added, 0, 0.5));
184 efl_text_set(o, "Scroll to 10");
185 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_cb, item_container);
186 efl_pack_table(tbl, o, 0, 2, 1, 1);
187
188 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
189 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
190 efl_gfx_hint_align_set(efl_added, 0, 0.5));
191 efl_text_set(o, "Change min size of 0");
192 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _change_min_size_cb, item_container);
193 efl_pack_table(tbl, o, 0, 3, 1, 1);
194
195 o = efl_add(EFL_UI_CHECK_CLASS, tbl,
196 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
197 efl_gfx_hint_align_set(efl_added, 0, 0.5));
198 efl_text_set(o, "Vertical");
199 efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_cb, item_container);
200 efl_ui_check_selected_set(o, EINA_TRUE);
201 efl_pack_table(tbl, o, 0, 4, 1, 1);
202
203 o = efl_add(EFL_UI_CHECK_CLASS, tbl,
204 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
205 efl_gfx_hint_align_set(efl_added, 0, 0.5));
206 efl_text_set(o, "Match Vertical");
207 efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
208 efl_pack_table(tbl, o, 0, 5, 1, 1);
209 ctx->v = o;
210
211 o = efl_add(EFL_UI_CHECK_CLASS, tbl,
212 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
213 efl_gfx_hint_align_set(efl_added, 0, 0.5));
214 efl_text_set(o, "Match Horizontal");
215 efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
216 efl_pack_table(tbl, o, 0, 6, 1, 1);
217 efl_gfx_entity_size_set(win, EINA_SIZE2D(260, 200));
218 ctx->h = o;
219
220 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
221 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
222 efl_gfx_hint_align_set(efl_added, 0, 0.5));
223 efl_text_set(o, "Remove all items");
224 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _remove_all_cb, item_container);
225 efl_pack_table(tbl, o, 0, 7, 1, 1);
226
227 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
228 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
229 efl_gfx_hint_align_set(efl_added, 0, 0.5));
230 efl_text_set(o, "Add 1 item");
231 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_one_item, item_container);
232 efl_pack_table(tbl, o, 0, 8, 1, 1);
233
234 o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
235 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
236 efl_gfx_hint_align_set(efl_added, 0, 0.5));
237 efl_text_set(o, "Add 1000 item");
238 efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_thousend_items, item_container);
239 efl_pack_table(tbl, o, 0, 9, 1, 1);
240
241 bx = efl_add(EFL_UI_RADIO_BOX_CLASS, tbl,
242 efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
243 efl_gfx_hint_align_set(efl_added, 0, 0.5));
244 efl_event_callback_add(bx, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _select_value_cb, item_container);
245 efl_pack_table(tbl, bx, 0, 10, 1, 1);
246 o = efl_add(EFL_UI_RADIO_CLASS, bx,
247 efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE));
248 efl_text_set(o, "Singleselect");
249 efl_pack_end(bx, o);
250 o = efl_add(EFL_UI_RADIO_CLASS, bx,
251 efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE_ALWAYS));
252 efl_text_set(o, "Singleselect Always");
253 efl_pack_end(bx, o);
254 o = efl_add(EFL_UI_RADIO_CLASS, bx,
255 efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_MULTI));
256 efl_text_set(o, "Multiselect");
257 efl_pack_end(bx, o);
258}
259
260void test_efl_ui_item_container_grid(void *data EINA_UNUSED,
261 Evas_Object *obj EINA_UNUSED,
262 void *event_info EINA_UNUSED)
263{
264 create_item_container_ui(efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS), EFL_UI_GRID_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container Grid");
265}
266
267void test_efl_ui_item_container_list(void *data EINA_UNUSED,
268 Evas_Object *obj EINA_UNUSED,
269 void *event_info EINA_UNUSED)
270{
271 create_item_container_ui(efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS), EFL_UI_LIST_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container List");
272}
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;
250# include <efl_ui_spin.eo.h> 250# include <efl_ui_spin.eo.h>
251# include <efl_ui_spin_button.eo.h> 251# include <efl_ui_spin_button.eo.h>
252# include <efl_ui_slider.eo.h> 252# include <efl_ui_slider.eo.h>
253# include <efl_ui_item.eo.h>
254# include <efl_ui_item_position_manager.eo.h>
255# include <efl_ui_item_container.eo.h>
256# include <efl_ui_list_position_manager.eo.h>
257# include <efl_ui_grid_position_manager.eo.h>
258# include <efl_ui_list_item.eo.h>
259# include <efl_ui_list_default_item.eo.h>
260# include <efl_ui_grid_item.eo.h>
261# include <efl_ui_grid_default_item.eo.h>
253 262
254/** 263/**
255 * Initialize Elementary 264 * 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 @@
1#ifdef HAVE_CONFIG_H
2#include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include <Elementary.h>
7#include "elm_widget.h"
8#include "elm_priv.h"
9
10#define MY_CLASS EFL_UI_GRID_POSITION_MANAGER_CLASS
11#define MY_DATA_GET(obj, pd) \
12 Efl_Ui_Grid_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS);
13
14typedef struct {
15 Eina_Accessor *content_acc, *size_acc;
16 unsigned int size;
17 Eina_Rect viewport;
18 Eina_Vector2 scroll_position;
19 Efl_Ui_Layout_Orientation dir;
20 struct {
21 unsigned int start_id, end_id;
22 } prev_run;
23 Eina_Size2D max_min_size;
24 Eina_Size2D last_viewport_size;
25 Eina_Size2D prev_min_size;
26 struct {
27 int columns;
28 int rows;
29 } current_display_table;
30} Efl_Ui_Grid_Position_Manager_Data;
31
32static inline void
33vis_change_segment(Efl_Ui_Grid_Position_Manager_Data *pd, int a, int b, Eina_Bool flag)
34{
35 for (int i = MIN(a, b); i < MAX(a, b); ++i)
36 {
37 Efl_Gfx_Entity *ent;
38
39 EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
40 if (ent && !efl_ui_focus_object_focus_get(ent))
41 {
42 efl_gfx_entity_visible_set(ent, flag);
43 }
44 }
45}
46
47static void
48_reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd)
49{
50 Eina_Size2D space_size;
51 int relevant_space_size, relevant_viewport;
52 unsigned int start_id, end_id, step;
53
54 if (!pd->size) return;
55 if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
56 if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return;
57
58 //space size contains the amount of space that is outside the viewport (either to the top or to the left)
59 space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
60 space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
61
62 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
63 {
64 relevant_space_size = space_size.h;
65 relevant_viewport = pd->viewport.h;
66 step = pd->max_min_size.h;
67 }
68 else
69 {
70 relevant_space_size = space_size.w;
71 relevant_viewport = pd->viewport.w;
72 step = pd->max_min_size.w;
73 }
74 start_id = MIN((MAX(relevant_space_size,0) / step)*pd->current_display_table.columns, pd->size);
75 end_id = MIN((((MAX(relevant_space_size,0) + relevant_viewport + step) / step)*pd->current_display_table.columns)+1, pd->size);
76
77 EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
78 EINA_SAFETY_ON_FALSE_RETURN(start_id <= pd->size);
79
80 //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
81 //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
82 if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id)
83 {
84 //it is important to first make the segment visible here, and then hide the rest
85 //otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
86 vis_change_segment(pd, start_id, end_id, EINA_TRUE);
87 vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
88 }
89 else
90 {
91 vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
92 vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
93 }
94
95 for (unsigned int i = start_id; i < end_id; ++i)
96 {
97 Eina_Rect geom;
98 Efl_Gfx_Entity *ent;
99 geom.size = pd->max_min_size;
100 geom.pos = pd->viewport.pos;
101
102 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
103 {
104 geom.x += pd->max_min_size.w*(i%pd->current_display_table.columns);
105 geom.y += pd->max_min_size.h*(i/pd->current_display_table.columns);
106 geom.y -= (relevant_space_size);
107 }
108 else
109 {
110 geom.x += pd->max_min_size.w*(i/pd->current_display_table.columns);
111 geom.y += pd->max_min_size.h*(i%pd->current_display_table.columns);
112 geom.x -= (relevant_space_size);
113 }
114
115 EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
116 //printf(">%d (%d, %d, %d, %d) %p\n", i, geom.x, geom.y, geom.w, geom.h, ent);
117 efl_gfx_entity_geometry_set(ent, geom);
118 }
119 pd->prev_run.start_id = start_id;
120 pd->prev_run.end_id = end_id;
121}
122
123static inline void
124_flush_abs_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd)
125{
126 int minor, major;
127 Eina_Size2D vp_size;
128
129 if (!pd->size) return;
130 if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
131
132 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
133 {
134 major = pd->viewport.w/pd->max_min_size.w;
135 pd->current_display_table.columns = major;
136 }
137 else
138 {
139 major = pd->viewport.h/pd->max_min_size.h;
140 pd->current_display_table.columns = major;
141 }
142
143 if (major <= 0) return;
144 minor = ceil((double)pd->size/(double)major);
145
146 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
147 pd->current_display_table.rows = minor;
148 else
149 pd->current_display_table.rows = minor;
150
151 /*
152 * calculate how much size we need with major in the given orientation.
153 * The
154 */
155 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
156 {
157 vp_size.w = pd->viewport.w;
158 vp_size.h = minor*pd->max_min_size.h;
159 }
160 else
161 {
162 vp_size.h = pd->viewport.h;
163 vp_size.w = minor*pd->max_min_size.w;
164 }
165 if (vp_size.h != pd->last_viewport_size.h || vp_size.w != pd->last_viewport_size.w)
166 {
167 pd->last_viewport_size = vp_size;
168 efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, &vp_size);
169 }
170}
171
172static inline void
173_update_min_size(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int added_index)
174{
175 Eina_Size2D elemsize;
176
177 EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, added_index, (void*)&elemsize));
178 pd->max_min_size.w = MAX(pd->max_min_size.w, elemsize.w);
179 pd->max_min_size.h = MAX(pd->max_min_size.h, elemsize.h);
180}
181
182static inline void
183_flush_min_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd)
184{
185 Eina_Size2D min_size = pd->max_min_size;
186
187 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
188 min_size.h = -1;
189 else
190 min_size.w = -1;
191
192 if (pd->prev_min_size.w != min_size.w || pd->prev_min_size.h != min_size.h)
193 {
194 pd->prev_min_size = min_size;
195 efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
196 }
197}
198
199EOLIAN static void
200_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)
201{
202 pd->size_acc = size_access;
203 pd->content_acc = obj_access;
204 pd->size = size;
205}
206
207EOLIAN static void
208_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)
209{
210 pd->viewport = viewport;
211 _flush_abs_size(obj, pd);
212 _reposition_content(obj, pd);
213}
214
215EOLIAN static void
216_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)
217{
218 pd->scroll_position.x = x;
219 pd->scroll_position.y = y;
220 _reposition_content(obj, pd);
221}
222
223EOLIAN static void
224_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)
225{
226 pd->size ++;
227
228 efl_gfx_entity_visible_set(subobj, EINA_FALSE);
229 _update_min_size(obj, pd, added_index);
230 _flush_min_size(obj, pd);
231 _flush_abs_size(obj, pd);
232 _reposition_content(obj, pd); //FIXME we might can skip that
233}
234
235EOLIAN static void
236_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)
237{
238 EINA_SAFETY_ON_FALSE_RETURN(pd->size > 0);
239 pd->size --;
240 pd->prev_run.start_id = MIN(pd->prev_run.start_id, pd->size);
241 pd->prev_run.end_id = MIN(pd->prev_run.end_id, pd->size);
242 //we ignore here that we might loose the item giving the current max min size
243 _flush_abs_size(obj, pd);
244 _reposition_content(obj, pd); //FIXME we might can skip that
245 efl_gfx_entity_visible_set(subobj, EINA_TRUE);
246}
247
248
249EOLIAN static void
250_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)
251{
252 for (int i = start_id; i <= end_id; ++i)
253 {
254 _update_min_size(obj, pd, i);
255 }
256
257 _flush_min_size(obj, pd);
258 _flush_abs_size(obj, pd);
259 _reposition_content(obj, pd); //FIXME we could check if this is needed or not
260}
261
262EOLIAN static void
263_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)
264{
265 pd->dir = dir;
266 _flush_min_size(obj, pd);
267 _flush_abs_size(obj, pd);
268 _reposition_content(obj, pd); //FIXME we could check if this is needed or not
269}
270
271
272EOLIAN static Efl_Ui_Layout_Orientation
273_efl_ui_grid_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd)
274{
275 return pd->dir;
276}
277
278EOLIAN static Eina_Rect
279_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)
280{
281 Eina_Rect geom;
282 Eina_Size2D space_size;
283 unsigned int relevant_space_size;
284
285 if (!pd->size) return EINA_RECT(0, 0, 0, 0);
286 if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return EINA_RECT(0, 0, 0, 0);
287 if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return EINA_RECT(0, 0, 0, 0);
288
289 //space size contains the amount of space that is outside the viewport (either to the top or to the left)
290 space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
291 space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
292
293 EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0));
294 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
295 {
296 relevant_space_size = space_size.h;
297 }
298 else
299 {
300 relevant_space_size = space_size.w;
301 }
302 geom.size = pd->max_min_size;
303 geom.pos = pd->viewport.pos;
304
305 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
306 {
307 geom.x += pd->max_min_size.w*(idx%pd->current_display_table.columns);
308 geom.y += pd->max_min_size.h*(idx/pd->current_display_table.columns);
309 geom.y -= (relevant_space_size);
310 }
311 else
312 {
313 geom.x += pd->max_min_size.w*(idx/pd->current_display_table.columns);
314 geom.y += pd->max_min_size.h*(idx%pd->current_display_table.columns);
315 geom.x -= (relevant_space_size);
316 }
317
318 return geom;
319}
320
321#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 @@
1class @beta Efl.Ui.Grid_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager
2{
3 [[Implementation of @Efl.Ui.Item_Position_Manager for two-dimensional grids.
4
5 Every item in the grid has the same size, which is the biggest minimum size of all items.
6 ]]
7 implements {
8 Efl.Ui.Item_Position_Manager.data_access {set;}
9 Efl.Ui.Item_Position_Manager.viewport {set;}
10 Efl.Ui.Item_Position_Manager.scroll_position {set;}
11 Efl.Ui.Item_Position_Manager.item_added;
12 Efl.Ui.Item_Position_Manager.item_removed;
13 Efl.Ui.Item_Position_Manager.position_single_item;
14 Efl.Ui.Item_Position_Manager.item_size_changed;
15 Efl.Ui.Layout_Orientable.orientation {set; get;}
16 }
17}
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 @@
1#ifdef HAVE_CONFIG_H
2#include "elementary_config.h"
3#endif
4
5#define ELM_LAYOUT_PROTECTED
6#define EFL_UI_SCROLL_MANAGER_PROTECTED
7#define EFL_UI_SCROLLBAR_PROTECTED
8#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
9
10#include <Efl_Ui.h>
11#include <Elementary.h>
12#include "elm_widget.h"
13#include "elm_priv.h"
14
15#define MY_CLASS EFL_UI_ITEM_CONTAINER_CLASS
16
17#define MY_DATA_GET(obj, pd) \
18 Efl_Ui_Item_Container_Data *pd = efl_data_scope_get(obj, MY_CLASS);
19
20typedef struct {
21 Efl_Ui_Scroll_Manager *smanager;
22 Efl_Ui_Pan *pan;
23 Eina_List *selected;
24 Eina_List *items;
25 Efl_Ui_Select_Mode mode;
26 Efl_Ui_Layout_Orientation dir;
27 Eina_Size2D content_min_size;
28 Efl_Ui_Item_Position_Manager *pos_man;
29 struct {
30 Eina_Accessor pass_on;
31 unsigned int last_index;
32 const Eina_List *current;
33 } obj_accessor;
34 struct {
35 Eina_Bool w;
36 Eina_Bool h;
37 } match_content;
38 Eina_Accessor size_accessor;
39 Efl_Gfx_Entity *sizer;
40} Efl_Ui_Item_Container_Data;
41
42static Eina_Bool register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item);
43static Eina_Bool unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item);
44
45static void
46flush_min_size(Eo *obj, Efl_Ui_Item_Container_Data *pd)
47{
48 Eina_Size2D tmp = pd->content_min_size;
49
50 if (!pd->match_content.w)
51 tmp.w = -1;
52
53 if (!pd->match_content.h)
54 tmp.h = -1;
55
56 efl_gfx_hint_size_min_set(obj, tmp);
57}
58
59static int
60clamp_index(Efl_Ui_Item_Container_Data *pd, int index)
61{
62 if (index < ((int)eina_list_count(pd->items)) * -1)
63 return -1;
64 else if (index > (int)eina_list_count(pd->items) - 1)
65 return 1;
66 return 0;
67}
68
69static int
70index_adjust(Efl_Ui_Item_Container_Data *pd, int index)
71{
72 int c = eina_list_count(pd->items);
73 if (index < c * -1)
74 return 0;
75 else if (index > c - 1)
76 return c - 1;
77 else if (index < 0)
78 return index + c;
79 return index;
80}
81
82static Eina_Bool
83_obj_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data)
84{
85 ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor);
86 Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
87 const Eina_List *over;
88 unsigned int middle;
89 unsigned int i;
90
91 if (idx >= eina_list_count(pd->items))
92 return EINA_FALSE;
93
94 if (pd->obj_accessor.last_index == idx)
95 over = pd->obj_accessor.current;
96 else if (idx > pd->obj_accessor.last_index)
97 {
98 /* After current position. */
99 middle = ((eina_list_count(pd->items) - pd->obj_accessor.last_index))/2;
100
101 if (idx > middle)
102 /* Go backward from the end. */
103 for (i = eina_list_count(pd->items) - 1,
104 over = eina_list_last(pd->items);
105 i > idx && over;
106 --i, over = eina_list_prev(over))
107 ;
108 else
109 /* Go forward from current. */
110 for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current;
111 i < idx && over;
112 ++i, over = eina_list_next(over))
113 ;
114 }
115 else
116 {
117 /* Before current position. */
118 middle = pd->obj_accessor.last_index/2;
119
120 if (idx > middle)
121 /* Go backward from current. */
122 for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current;
123 i > idx && over;
124 --i, over = eina_list_prev(over))
125 ;
126 else
127 /* Go forward from start. */
128 for (i = 0, over = pd->items;
129 i < idx && over;
130 ++i, over = eina_list_next(over))
131 ;
132 }
133
134 if (!over)
135 return EINA_FALSE;
136
137 pd->obj_accessor.last_index = idx;
138 pd->obj_accessor.current = over;
139
140 *data = eina_list_data_get(over);
141 return EINA_TRUE;
142}
143
144static Eina_Accessor*
145_obj_clone(Eina_Accessor *accessor)
146{
147 ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor);
148 Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
149
150 return eina_list_accessor_new(pd->items);
151}
152
153static Eina_List *
154_null_container(Eina_Accessor *accessor EINA_UNUSED)
155{
156 ERR("Not allowed to get a container!");
157 return NULL;
158}
159
160static void
161_free(Eina_Accessor *accessor EINA_UNUSED)
162{
163 ERR("Freeing this accessor is not supported");
164}
165
166static void
167_obj_accessor_init(Eina_Accessor *accessor)
168{
169 //this is the accessor for accessing the items
170 //we have to workarround here the problem that
171 //no accessor can be created for a not yet created list.
172 accessor->version = EINA_ACCESSOR_VERSION;
173 accessor->get_at = FUNC_ACCESSOR_GET_AT(_obj_accessor_get_at);
174 accessor->clone = FUNC_ACCESSOR_CLONE(_obj_clone);
175 accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container);
176 accessor->free = FUNC_ACCESSOR_FREE(_free);
177 EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR);
178}
179
180static Eina_Bool
181_size_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data)
182{
183 Eina_Size2D *size = (Eina_Size2D*)data;
184 ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, size_accessor);
185 Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
186
187 if (idx > eina_list_count(pd->items))
188 return EINA_FALSE;
189
190 Eo *subobj = eina_list_nth(pd->items, idx);
191
192 *size = efl_gfx_hint_size_combined_min_get(subobj);
193
194 return EINA_TRUE;
195}
196
197static Eina_Accessor*
198_size_clone(Eina_Accessor *accessor EINA_UNUSED)
199{
200 return NULL;
201}
202
203static void
204_size_accessor_init(Eina_Accessor *accessor)
205{
206 accessor->version = EINA_ACCESSOR_VERSION;
207 accessor->get_at = FUNC_ACCESSOR_GET_AT(_size_accessor_get_at);
208 accessor->clone = FUNC_ACCESSOR_CLONE(_size_clone);
209 accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container);
210 accessor->free = FUNC_ACCESSOR_FREE(_free);
211 EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR);
212}
213
214static void
215_pan_viewport_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
216{
217 MY_DATA_GET(data, pd);
218 Eina_Rect rect = efl_ui_scrollable_viewport_geometry_get(data);
219
220 efl_ui_item_position_manager_viewport_set(pd->pos_man, rect);
221}
222
223static void
224_pan_position_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
225{
226 MY_DATA_GET(data, pd);
227 Eina_Position2D pos = efl_ui_pan_position_get(pd->pan);
228 Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
229 Eina_Vector2 rpos = {0.0, 0.0};
230
231 if (max.x > 0.0)
232 rpos.x = (double)pos.x/(double)max.x;
233 if (max.y > 0.0)
234 rpos.y = (double)pos.y/(double)max.y;
235
236 efl_ui_item_position_manager_scroll_position_set(pd->pos_man, rpos.x, rpos.y);
237}
238
239EFL_CALLBACKS_ARRAY_DEFINE(pan_events_cb,
240 {EFL_UI_PAN_EVENT_PAN_POSITION_CHANGED, _pan_position_changed_cb},
241 {EFL_UI_PAN_EVENT_PAN_VIEWPORT_CHANGED, _pan_viewport_changed_cb},
242)
243
244static void
245_item_scroll_internal(Eo *obj EINA_UNUSED,
246 Efl_Ui_Item_Container_Data *pd,
247 Efl_Ui_Item *item,
248 double align,
249 Eina_Bool anim)
250{
251 Eina_Rect ipos, view;
252 Eina_Position2D vpos;
253
254 if (!pd->smanager) return;
255
256 ipos = efl_ui_item_position_manager_position_single_item(pd->pos_man, eina_list_data_idx(pd->items, item));
257 view = efl_ui_scrollable_viewport_geometry_get(pd->smanager);
258 vpos = efl_ui_scrollable_content_pos_get(pd->smanager);
259
260 ipos.x = ipos.x + vpos.x - view.x;
261 ipos.y = ipos.y + vpos.y - view.y;
262
263 //FIXME scrollable needs some sort of align, the docs do not even garantee to completly move in the element
264 efl_ui_scrollable_scroll(pd->smanager, ipos, anim);
265}
266
267EOLIAN static void
268_efl_ui_item_container_item_scroll(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, Eina_Bool animation)
269{
270 _item_scroll_internal(obj, pd, item, -1.0, animation);
271}
272
273EOLIAN static void
274_efl_ui_item_container_item_scroll_align(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, double align, Eina_Bool animation)
275{
276 _item_scroll_internal(obj, pd, item, align, animation);
277}
278
279EOLIAN static Efl_Ui_Item*
280_efl_ui_item_container_last_selected_item_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
281{
282 return eina_list_last_data_get(pd->selected);
283}
284
285EOLIAN static Eina_Iterator*
286_efl_ui_item_container_selected_items_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
287{
288 return eina_list_iterator_new(pd->selected);
289}
290
291EOLIAN static Efl_Object*
292_efl_ui_item_container_efl_object_constructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
293{
294 Eo *o;
295
296 pd->dir = EFL_UI_LAYOUT_ORIENTATION_VERTICAL;
297
298 _obj_accessor_init(&pd->obj_accessor.pass_on);
299 _size_accessor_init(&pd->size_accessor);
300
301 if (!elm_widget_theme_klass_get(obj))
302 elm_widget_theme_klass_set(obj, "item_container");
303
304 o = efl_constructor(efl_super(obj, MY_CLASS));
305
306 pd->sizer = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(obj));
307 efl_gfx_color_set(pd->sizer, 0, 0, 0, 0);
308
309 pd->pan = efl_add(EFL_UI_PAN_CLASS, obj);
310 efl_content_set(pd->pan, pd->sizer);
311 efl_event_callback_array_add(pd->pan, pan_events_cb(), obj);
312
313 pd->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj);
314 efl_composite_attach(obj, pd->smanager);
315 efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj));
316 efl_ui_scroll_manager_pan_set(pd->smanager, pd->pan);
317
318 efl_ui_scroll_connector_bind(obj, pd->smanager);
319
320 return o;
321}
322
323EOLIAN static Efl_Object*
324_efl_ui_item_container_efl_object_finalize(Eo *obj, Efl_Ui_Item_Container_Data *pd)
325{
326 EINA_SAFETY_ON_NULL_RETURN_VAL(pd->pos_man, NULL);
327
328 return efl_finalize(efl_super(obj, MY_CLASS));
329}
330
331EOLIAN static Eina_Error
332_efl_ui_item_container_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Item_Container_Data *pd)
333{
334 Eina_Error res;
335
336 ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC);
337 res = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
338 if (res == EFL_UI_THEME_APPLY_ERROR_GENERIC) return res;
339 efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj));
340 efl_content_set(efl_part(wd->resize_obj, "efl.content"), pd->pan);
341
342 return res;
343}
344
345EOLIAN static void
346_efl_ui_item_container_efl_object_destructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
347{
348 efl_destructor(efl_super(obj, MY_CLASS));
349}
350
351static void
352deselect_all(Efl_Ui_Item_Container_Data *pd)
353{
354 while(pd->selected)
355 {
356 Eo *item = eina_list_data_get(pd->selected);
357 efl_ui_item_selected_set(item, EINA_FALSE);
358 EINA_SAFETY_ON_TRUE_RETURN(eina_list_data_get(pd->selected) == item);
359 }
360}
361
362EOLIAN static void
363_efl_ui_item_container_efl_object_invalidate(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
364{
365 efl_ui_item_container_position_manager_set(obj, NULL);
366
367 deselect_all(pd);
368
369 while(pd->items)
370 efl_del(pd->items->data);
371
372 efl_invalidate(efl_super(obj, MY_CLASS));
373}
374
375EOLIAN static Eina_Iterator*
376_efl_ui_item_container_efl_container_content_iterate(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
377{
378 return eina_list_iterator_new(pd->items);
379}
380
381EOLIAN static int
382_efl_ui_item_container_efl_container_content_count(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
383{
384 return eina_list_count(pd->items);
385}
386
387EOLIAN static void
388_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)
389{
390 if (pd->dir == dir) return;
391
392 pd->dir = dir;
393 if (pd->pos_man)
394 efl_ui_layout_orientation_set(pd->pos_man, dir);
395}
396
397EOLIAN static Efl_Ui_Layout_Orientation
398_efl_ui_item_container_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
399{
400 return pd->dir;
401}
402
403EOLIAN static void
404_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)
405{
406 if (pd->match_content.w == w && pd->match_content.h == h)
407 return;
408
409 pd->match_content.w = w;
410 pd->match_content.h = h;
411
412 efl_ui_scrollable_match_content_set(pd->smanager, w, h);
413 flush_min_size(obj, pd);
414}
415
416EOLIAN static void
417_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)
418{
419 pd->mode = mode;
420 if ((mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || mode == EFL_UI_SELECT_MODE_SINGLE) &&
421 eina_list_count(pd->selected) > 0)
422 {
423 Efl_Ui_Item *last = eina_list_last_data_get(pd->selected);
424
425 pd->selected = eina_list_remove_list(pd->selected, eina_list_last(pd->selected));
426 deselect_all(pd);
427 pd->selected = eina_list_append(pd->selected, last);
428 }
429 else if (mode == EFL_UI_SELECT_MODE_NONE && pd->selected)
430 {
431 deselect_all(pd);
432 }
433}
434
435EOLIAN static Efl_Ui_Select_Mode
436_efl_ui_item_container_efl_ui_multi_selectable_select_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
437{
438 return pd->mode;
439}
440
441static void
442_selected_cb(void *data, const Efl_Event *ev)
443{
444 Eo *obj = data;
445 MY_DATA_GET(obj, pd);
446
447 if (pd->mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || pd->mode == EFL_UI_SELECT_MODE_SINGLE)
448 {
449 //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
450 if (eina_list_data_get(pd->selected) == ev->object)
451 {
452 pd->selected = eina_list_free(pd->selected);
453 }
454 else
455 {
456 deselect_all(pd);
457 }
458
459 }
460 else if (pd->mode == EFL_UI_SELECT_MODE_NONE)
461 {
462 ERR("Selection while mode is NONE, uncaught state!");
463 return;
464 }
465 pd->selected = eina_list_append(pd->selected, ev->object);
466 efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_SELECTED, ev->object);
467}
468
469static void
470_unselected_cb(void *data, const Efl_Event *ev)
471{
472 Eo *obj = data;
473 MY_DATA_GET(obj, pd);
474
475 pd->selected = eina_list_remove(pd->selected, ev->object);
476 efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_UNSELECTED, ev->object);
477}
478
479static void
480_invalidate_cb(void *data, const Efl_Event *ev)
481{
482 Eo *obj = data;
483 MY_DATA_GET(obj, pd);
484
485 unregister_item(obj, pd, ev->object);
486}
487
488static void
489_hints_changed_cb(void *data, const Efl_Event *ev)
490{
491 Eo *obj = data;
492 MY_DATA_GET(obj, pd);
493 int idx = eina_list_data_idx(pd->items, ev->object);
494
495 efl_ui_item_position_manager_item_size_changed(pd->pos_man, idx, idx);
496}
497
498static void
499_redirect_cb(void *data, const Efl_Event *ev)
500{
501 Eo *obj = data;
502
503#define REDIRECT_EVT(item_evt, item) \
504 if (item_evt == ev->desc) efl_event_callback_call(obj, item, ev->object);
505 REDIRECT_EVT(EFL_UI_EVENT_PRESSED, EFL_UI_EVENT_ITEM_PRESSED);
506 REDIRECT_EVT(EFL_UI_EVENT_UNPRESSED, EFL_UI_EVENT_ITEM_UNPRESSED);
507 REDIRECT_EVT(EFL_UI_EVENT_LONGPRESSED, EFL_UI_EVENT_ITEM_LONGPRESSED);
508 REDIRECT_EVT(EFL_UI_EVENT_CLICKED_ANY, EFL_UI_EVENT_ITEM_CLICKED_ANY);
509 REDIRECT_EVT(EFL_UI_EVENT_CLICKED, EFL_UI_EVENT_ITEM_CLICKED);
510#undef REDIRECT_EVT
511}
512
513EFL_CALLBACKS_ARRAY_DEFINE(active_item,
514 {EFL_GFX_ENTITY_EVENT_HINTS_CHANGED, _hints_changed_cb},
515 {EFL_UI_EVENT_ITEM_SELECTED, _selected_cb},
516 {EFL_UI_EVENT_ITEM_UNSELECTED, _unselected_cb},
517 {EFL_UI_EVENT_PRESSED, _redirect_cb},
518 {EFL_UI_EVENT_UNPRESSED, _redirect_cb},
519 {EFL_UI_EVENT_LONGPRESSED, _redirect_cb},
520 {EFL_UI_EVENT_CLICKED, _redirect_cb},
521 {EFL_UI_EVENT_CLICKED_ANY, _redirect_cb},
522 {EFL_EVENT_INVALIDATE, _invalidate_cb},
523)
524
525static Eina_Bool
526register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item)
527{
528 EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(item, EFL_UI_ITEM_CLASS), EINA_FALSE);
529 EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_list_data_find(pd->items, item), EINA_FALSE);
530
531 if (!efl_ui_widget_sub_object_add(obj, item))
532 return EINA_FALSE;
533
534 efl_ui_item_container_set(item, obj);
535 efl_canvas_group_member_add(pd->pan, item);
536 efl_event_callback_array_add(item, active_item(), obj);
537 efl_ui_mirrored_set(item, efl_ui_mirrored_get(obj));
538
539 return EINA_TRUE;
540}
541
542static Eina_Bool
543unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item)
544{
545 Eina_List *elem = eina_list_data_find_list(pd->items, item);
546 if (!elem)
547 {
548 ERR("Item %p is not part of this widget", item);
549 return EINA_FALSE;
550 }
551
552 if (!efl_ui_widget_sub_object_del(obj, item))
553 return EINA_FALSE;
554
555 unsigned int id = eina_list_data_idx(pd->items, item);
556 if (pd->obj_accessor.last_index == id)
557 {
558 if (eina_list_next(elem))
559 {
560 pd->obj_accessor.current = eina_list_next(elem);
561 }
562 else if (eina_list_prev(elem))
563 {
564 pd->obj_accessor.last_index = id-1;
565 pd->obj_accessor.current = eina_list_prev(elem);
566 }
567 else
568 {
569 //everything >= length is invalid, and we need that.
570 pd->obj_accessor.last_index = eina_list_count(pd->items);
571 pd->obj_accessor.current = NULL;
572 }
573
574 }
575
576 pd->items = eina_list_remove(pd->items, item);
577 pd->selected = eina_list_remove(pd->selected, item);
578 efl_event_callback_array_del(item, active_item(), obj);
579 efl_ui_item_position_manager_item_removed(pd->pos_man, id, item);
580
581 return EINA_TRUE;
582}
583
584static void
585update_pos_man(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
586{
587 int id = eina_list_data_idx(pd->items, subobj);
588 if (id == 0)
589 {
590 pd->obj_accessor.last_index = id;
591 pd->obj_accessor.current = pd->items;
592 }
593 efl_ui_item_position_manager_item_added(pd->pos_man, id, subobj);
594}
595
596EOLIAN static Eina_Bool
597_efl_ui_item_container_efl_pack_pack_clear(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
598{
599 while(pd->items)
600 {
601 efl_del(pd->items->data);
602 }
603
604 return EINA_TRUE;
605}
606
607EOLIAN static Eina_Bool
608_efl_ui_item_container_efl_pack_unpack_all(Eo *obj, Efl_Ui_Item_Container_Data *pd)
609{
610 while(pd->items)
611 {
612 if (!unregister_item(obj, pd, pd->items->data))
613 return EINA_FALSE;
614 }
615 return EINA_TRUE;
616}
617
618EOLIAN static Efl_Gfx_Entity*
619_efl_ui_item_container_efl_pack_linear_pack_unpack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, int index)
620{
621 Efl_Ui_Item *it = eina_list_nth(pd->items, index_adjust(pd, index));
622
623 EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
624
625 if (!unregister_item(obj, pd, it))
626 return NULL;
627
628 return it;
629}
630
631EOLIAN static Eina_Bool
632_efl_ui_item_container_efl_pack_unpack(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
633{
634 return unregister_item(obj, pd, subobj);
635}
636
637
638EOLIAN static Eina_Bool
639_efl_ui_item_container_efl_pack_pack(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Gfx_Entity *subobj)
640{
641 return efl_pack_end(obj, subobj);
642}
643
644
645EOLIAN static Eina_Bool
646_efl_ui_item_container_efl_pack_linear_pack_end(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
647{
648 if (!register_item(obj, pd, subobj))
649 return EINA_FALSE;
650 pd->items = eina_list_append(pd->items, subobj);
651 update_pos_man(obj, pd, subobj);
652 return EINA_TRUE;
653}
654
655
656EOLIAN static Eina_Bool
657_efl_ui_item_container_efl_pack_linear_pack_begin(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
658{
659 if (!register_item(obj, pd, subobj))
660 return EINA_FALSE;
661 pd->items = eina_list_prepend(pd->items, subobj);
662 update_pos_man(obj, pd, subobj);
663 return EINA_TRUE;
664}
665
666EOLIAN static Eina_Bool
667_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)
668{
669 Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing);
670 EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
671
672 if (!register_item(obj, pd, subobj))
673 return EINA_FALSE;
674 pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list);
675 update_pos_man(obj, pd, subobj);
676 return EINA_TRUE;
677}
678
679EOLIAN static Eina_Bool
680_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)
681{
682 Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing);
683 EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
684
685 if (!register_item(obj, pd, subobj))
686 return EINA_FALSE;
687 pd->items = eina_list_append_relative_list(pd->items, subobj, subobj_list);
688 update_pos_man(obj, pd, subobj);
689 return EINA_TRUE;
690}
691
692EOLIAN static Eina_Bool
693_efl_ui_item_container_efl_pack_linear_pack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, int index)
694{
695 Eina_List *subobj_list;
696 int clamp;
697
698 clamp = clamp_index(pd, index);
699 index = index_adjust(pd, index);
700 subobj_list = eina_list_nth_list(pd->items, index);
701 EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
702 if (!register_item(obj, pd, subobj))
703 return EINA_FALSE;
704 if (clamp == 0)
705 pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list);
706 else if (clamp == 1)
707 pd->items = eina_list_append(pd->items, subobj);
708 else
709 pd->items = eina_list_prepend(pd->items, subobj);
710 update_pos_man(obj, pd, subobj);
711 return EINA_TRUE;
712}
713
714EOLIAN static int
715_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)
716{
717 return eina_list_data_idx(pd->items, (void*)subobj);
718}
719
720EOLIAN static Efl_Gfx_Entity*
721_efl_ui_item_container_efl_pack_linear_pack_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, int index)
722{
723 return eina_list_nth(pd->items, index_adjust(pd, index));
724}
725
726static void
727_pos_content_size_changed_cb(void *data, const Efl_Event *ev)
728{
729 Eina_Size2D *size = ev->info;
730 MY_DATA_GET(data, pd);
731
732 efl_gfx_entity_size_set(pd->sizer, *size);
733}
734
735static void
736_pos_content_min_size_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
737{
738 Eina_Size2D *size = ev->info;
739 MY_DATA_GET(data, pd);
740
741 pd->content_min_size = *size;
742
743 flush_min_size(data, pd);
744}
745
746EFL_CALLBACKS_ARRAY_DEFINE(pos_manager_cbs,
747 {EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _pos_content_size_changed_cb},
748 {EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _pos_content_min_size_changed_cb},
749)
750
751EOLIAN static void
752_efl_ui_item_container_position_manager_set(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item_Position_Manager *layouter)
753{
754 if (layouter)
755 EINA_SAFETY_ON_FALSE_RETURN(efl_isa(layouter, EFL_UI_ITEM_POSITION_MANAGER_INTERFACE));
756
757 if (pd->pos_man)
758 {
759 efl_event_callback_array_del(pd->pos_man, pos_manager_cbs(), obj);
760 efl_ui_item_position_manager_data_access_set(pd->pos_man, NULL, NULL, 0);
761 efl_del(pd->pos_man);
762 }
763 pd->pos_man = layouter;
764 if (pd->pos_man)
765 {
766 efl_parent_set(pd->pos_man, obj);
767 efl_event_callback_array_add(pd->pos_man, pos_manager_cbs(), obj);
768 efl_ui_item_position_manager_data_access_set(pd->pos_man, &pd->obj_accessor.pass_on, &pd->size_accessor, eina_list_count(pd->items));
769 efl_ui_item_position_manager_viewport_set(pd->pos_man, efl_ui_scrollable_viewport_geometry_get(obj));
770 efl_ui_layout_orientation_set(pd->pos_man, pd->dir);
771 }
772}
773
774EOLIAN static Efl_Ui_Item_Position_Manager*
775_efl_ui_item_container_position_manager_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
776{
777 return pd->pos_man;
778}
779
780EOLIAN static Efl_Ui_Focus_Manager*
781_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)
782{
783 return efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj,
784 efl_ui_focus_manager_root_set(efl_added, root));
785}
786
787EOLIAN static Eina_Bool
788_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)
789{
790 return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
791}
792
793EOLIAN static Efl_Ui_Focus_Object*
794_efl_ui_item_container_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Focus_Direction direction)
795{
796 Eo *new_obj, *focus;
797 Eina_Size2D step;
798
799 new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
800 focus = efl_ui_focus_manager_focus_get(obj);
801 step = efl_gfx_hint_size_min_get(focus);
802 if (!new_obj)
803 {
804 Eina_Rect pos = efl_gfx_entity_geometry_get(focus);
805 Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(pd->smanager);
806 Eina_Position2D vpos = efl_ui_scrollable_content_pos_get(pd->smanager);
807
808 pos.x = pos.x + vpos.x - view.x;
809 pos.y = pos.y + vpos.y - view.y;
810 Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
811
812 if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT)
813 {
814 if (pos.x < max.x)
815 {
816 pos.x = MIN(max.x, pos.x + step.w);
817 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
818 new_obj = focus;
819 }
820 }
821 else if (direction == EFL_UI_FOCUS_DIRECTION_LEFT)
822 {
823 if (pos.x > 0)
824 {
825 pos.x = MAX(0, pos.x - step.w);
826 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
827 new_obj = focus;
828 }
829 }
830 else if (direction == EFL_UI_FOCUS_DIRECTION_UP)
831 {
832 if (pos.y > 0)
833 {
834 pos.y = MAX(0, pos.y - step.h);
835 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
836 new_obj = focus;
837 }
838 }
839 else if (direction == EFL_UI_FOCUS_DIRECTION_DOWN)
840 {
841 if (pos.y < max.y)
842 {
843 pos.y = MAX(0, pos.y + step.h);
844 efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
845 new_obj = focus;
846 }
847 }
848 }
849 else
850 {
851 _item_scroll_internal(obj, pd, new_obj, .0, EINA_TRUE);
852 }
853
854 return new_obj;
855}
856
857#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 @@
1class @beta Efl.Ui.Item_Container extends Efl.Ui.Layout_Base implements
2 Efl.Ui.Scrollable_Interactive,
3 Efl.Ui.Scrollbar,
4 Efl.Pack_Linear, Efl.Pack_Layout,
5 Efl.Ui.Layout_Orientable,
6 Efl.Ui.Selectable,
7 Efl.Ui.Multi_Selectable,
8 Efl.Ui.Focus.Manager_Sub,
9 Efl.Ui.Widget_Focus_Manager
10{
11 [[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.
12
13 Items inside this widget can be selected according to the $select_mode policy, and retrieved with $selected_items_get.
14 ]]
15 event_prefix:efl_ui;
16 methods {
17 item_scroll {
18 [[Bring the passed item into the viewport.]]
19 params {
20 @in item: Efl.Ui.Item; [[The target to move in.]]
21 @in animation: bool; [[If you want to have an animation or not.]]
22 }
23 }
24 item_scroll_align {
25 [[Bring the passed item into the viewport, place the item accordingly to align in the viewport.
26
27 $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.
28 ]]
29 params {
30 @in item: Efl.Ui.Item; [[The target to move in.]]
31 @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]]
32 @in animation: bool; [[If you want to have an animation or not.]]
33 }
34 }
35 @property last_selected_item {
36 [[The item that was selected most recently.
37 ]]
38 get {}
39 values {
40 item: Efl.Ui.Item; [[The latest selected item.]]
41 }
42 }
43 selected_items_get {
44 [[Get the selected items iterator. The iterator sequence will be decided by selection.]]
45 return: iterator<Efl.Ui.Item> @owned @no_unused; [[User has to free the iterator after usage.]]
46 }
47 @property position_manager {
48 [[Position manager object that handles placement of items.]]
49 values {
50 position_manager : Efl.Ui.Item_Position_Manager @owned; [[The objects ownership is passed to the item container.]]
51 }
52 }
53 }
54 implements {
55 Efl.Object.constructor;
56 Efl.Object.finalize;
57 Efl.Object.destructor;
58 Efl.Object.invalidate;
59 Efl.Container.content_iterate;
60 Efl.Container.content_count;
61 Efl.Ui.Layout_Orientable.orientation { get; set; }
62 Efl.Ui.Widget.theme_apply;
63 Efl.Pack.pack_clear;
64 Efl.Pack.unpack_all;
65 Efl.Pack.unpack;
66 Efl.Pack.pack;
67 Efl.Pack_Linear.pack_end;
68 Efl.Pack_Linear.pack_begin;
69 Efl.Pack_Linear.pack_before;
70 Efl.Pack_Linear.pack_after;
71 Efl.Pack_Linear.pack_at;
72 Efl.Pack_Linear.pack_unpack_at;
73 Efl.Pack_Linear.pack_index_get;
74 Efl.Pack_Linear.pack_content_get;
75 Efl.Ui.Scrollable_Interactive.match_content { set; }
76 Efl.Ui.Multi_Selectable.select_mode {get; set;}
77 Efl.Ui.Widget_Focus_Manager.focus_manager_create;
78 Efl.Ui.Widget.focus_state_apply;
79 Efl.Ui.Focus.Manager.move;
80 }
81 events {
82 item,pressed : Efl.Ui.Item; [[A $press event occurred over an item.]]
83 item,unpressed : Efl.Ui.Item; [[A $unpress event occurred over an item.]]
84 item,longpressed : Efl.Ui.Item; [[A $longpressed event occurred over an item.]]
85 item,clicked : Efl.Ui.Item; [[A $clicked event occurred over an item.]]
86 item,clicked,any : Efl.Ui.Item; [[A $clicked,any event occurred over an item.]]
87 }
88 composite {
89 Efl.Ui.Scrollable_Interactive;
90 Efl.Ui.Scrollbar;
91 Efl.Ui.Focus.Manager;
92 }
93}
94
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 @@
1#ifdef HAVE_CONFIG_H
2#include "elementary_config.h"
3#endif
4
5#define ELM_LAYOUT_PROTECTED
6#define EFL_UI_SCROLL_MANAGER_PROTECTED
7#define EFL_UI_SCROLLBAR_PROTECTED
8
9#include <Efl_Ui.h>
10#include "elm_priv.h"
11
12#define MY_CLASS EFL_UI_ITEM_POSITION_MANAGER_CLASS
13
14#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 @@
1
2interface @beta Efl.Ui.Item_Position_Manager extends Efl.Ui.Layout_Orientable
3{
4 [[
5 This abstracts the basic placement of items in a not defined form under a viewport.
6
7 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.
8 ]]
9 methods {
10 @property data_access {
11 [[This gives access to items to be managed. The manager reads this information and modifies the retrieved items' positions and sizes.
12
13 $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.
14 Typically, only entities inside the viewport will be retrieved.
15
16 $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).
17 The whole range might need to be traversed in order to calculate the position of all items in some arrangements.
18 ]]
19 set {
20
21 }
22 values {
23 obj_access : accessor<Efl.Gfx.Entity>; [[The accessor for canvas obejcts, even if the id is valid, the returned object may be NULL]]
24 size_access : accessor<Eina.Size2D>; [[Accessor for the size, returned values are always valid, but might be changed / updated]]
25 size : int; [[valid size for accessors, 0 <= i < size]]
26 }
27 }
28 @property viewport {
29 [[This is the position and size of the viewport, where elements are displayed in.
30 Entities outside this viewport will not be shown.]]
31 set {
32
33 }
34 values {
35 viewport : Eina.Rect;
36 }
37 }
38 @property scroll_position {
39 [[Move the items relative to the viewport.
40
41 The items that are managed with this position manager might be bigger than the actual viewport.
42 The positioning of the layer where all items are on is described by these values.
43 0.0,0.0 means that layer is moved that the top left items are shown,
44 1.0,1.0 means, that the lower right items are shown.
45 ]]
46 set {
47
48 }
49 values {
50 x : double; [[X position of the scroller, valid form 0 to 1.0]]
51 y : double; [[Y position of the scroller, valid form 0 to 1.0]]
52 }
53 }
54 position_single_item {
55 [[Return the position and size of item idx
56
57 This method returns the size and position of the item at $idx.
58 Even if the item is outside the viewport, the returned rectangle must be valid. The result can be used for scrolling calculations.
59 ]]
60 params {
61 idx : int; [[The id for the item]]
62 }
63 return : Eina.Rect; [[Position and Size in canvas coordinations]]
64 }
65 item_added {
66 [[The new item $subobj has been added at the $added_index field.
67
68 The accessor provided through @.data_access will contain updated Entities.]]
69 params {
70 added_index : int;
71 subobj : Efl.Gfx.Entity;
72 }
73 }
74 item_removed {
75 [[The item $subobj previously at position $removed_index has been removed.
76 The accessor provided through @.data_access will contain updated Entities.
77 ]]
78 params {
79 removed_index : int;
80 subobj : Efl.Gfx.Entity;
81 }
82 }
83 item_size_changed {
84 [[The size of the items from $start_id to $end_id have been changed.
85 The positioning and sizing of all items will be updated]]
86 params {
87 start_id : int; [[The first item that has a new size]]
88 end_id : int; [[The last item that has a new size]]
89 }
90 }
91 }
92 events {
93 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.]]
94 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.]]
95 }
96}
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 @@
1#ifdef HAVE_CONFIG_H
2#include "elementary_config.h"
3#endif
4
5
6#include <Efl_Ui.h>
7#include <Elementary.h>
8#include "elm_widget.h"
9#include "elm_priv.h"
10
11#define MY_CLASS EFL_UI_LIST_POSITION_MANAGER_CLASS
12#define MY_DATA_GET(obj, pd) \
13 Efl_Ui_List_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS);
14
15typedef struct {
16 Eina_Accessor *content_acc, *size_acc;
17 unsigned int size;
18 Eina_Future *rebuild_absolut_size;
19 Eina_Rect viewport;
20 Eina_Size2D abs_size;
21 Eina_Vector2 scroll_position;
22 Efl_Ui_Layout_Orientation dir;
23 int *size_cache;
24 int average_item_size;
25 int maximum_min_size;
26 struct {
27 unsigned int start_id, end_id;
28 } prev_run;
29} Efl_Ui_List_Position_Manager_Data;
30
31/*
32 * The here used cache is a sum map
33 * Every element in the cache contains the sum of the previous element, and the size of the current item
34 * This is usefull as a lookup of all previous items is O(1).
35 * The tradeoff that makes the cache performant here is, that we only need to walk the whole list of items once in the beginning.
36 * 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.
37 */
38
39static void
40cache_require(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
41{
42 unsigned int i;
43
44 if (pd->size_cache) return;
45
46 if (pd->size == 0)
47 {
48 pd->size_cache = NULL;
49 pd->average_item_size = 0;
50 return;
51 }
52
53 pd->size_cache = calloc(pd->size + 1, sizeof(int));
54 pd->size_cache[0] = 0;
55 pd->maximum_min_size = 0;
56
57 for (i = 0; i < pd->size; ++i)
58 {
59 Eina_Size2D size;
60 int step;
61 int min;
62
63 eina_accessor_data_get(pd->size_acc, i, (void**) &size);
64 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
65 {
66 step = size.h;
67 min = size.w;
68 }
69 else
70 {
71 step = size.w;
72 min = size.h;
73 }
74 pd->size_cache[i + 1] = pd->size_cache[i] + step;
75 pd->maximum_min_size = MAX(pd->maximum_min_size, min);
76 }
77 pd->average_item_size = pd->size_cache[pd->size]/pd->size;
78}
79
80static void
81cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
82{
83 if (pd->size_cache)
84 free(pd->size_cache);
85 pd->size_cache = NULL;
86}
87
88static inline int
89cache_access(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, unsigned int idx)
90{
91 EINA_SAFETY_ON_FALSE_RETURN_VAL(idx <= pd->size, 0);
92 return pd->size_cache[idx];
93}
94
95static void
96recalc_absolut_size(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
97{
98 Eina_Size2D min_size = EINA_SIZE2D(-1, -1);
99 cache_require(obj, pd);
100
101 pd->abs_size = pd->viewport.size;
102
103 if (pd->size)
104 {
105 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
106 pd->abs_size.h = MAX(cache_access(obj, pd, pd->size), pd->abs_size.h);
107 else
108 pd->abs_size.w = MAX(cache_access(obj, pd, pd->size), pd->abs_size.w);
109 }
110
111 efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, &pd->abs_size);
112
113 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
114 {
115 min_size.w = pd->maximum_min_size;
116 }
117 else
118 {
119 min_size.h = pd->maximum_min_size;
120 }
121
122 efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
123}
124
125static inline void
126vis_change_segment(Efl_Ui_List_Position_Manager_Data *pd, int a, int b, Eina_Bool flag)
127{
128 for (int i = MIN(a, b); i < MAX(a, b); ++i)
129 {
130 Efl_Gfx_Entity *ent = NULL;
131
132 eina_accessor_data_get(pd->content_acc, i, (void**) &ent);
133 if (ent && !efl_ui_focus_object_focus_get(ent))
134 {
135 efl_gfx_entity_visible_set(ent, flag);
136 }
137 }
138}
139
140static void
141position_content(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
142{
143 Eina_Rect geom;
144 Eina_Size2D space_size;
145 unsigned int start_id = 0, end_id = 0, i;
146 int relevant_space_size, relevant_viewport;
147
148 if (!pd->size) return;
149 if (pd->average_item_size <= 0) return;
150
151 //space size contains the amount of space that is outside the viewport (either to the top or to the left)
152 space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
153 space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
154
155 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
156 {
157 relevant_space_size = space_size.h;
158 relevant_viewport = pd->viewport.h;
159 }
160 else
161 {
162 relevant_space_size = space_size.w;
163 relevant_viewport = pd->viewport.w;
164 }
165
166 //based on the average item size, we jump somewhere into the sum cache.
167 //After beeing in there, we are walking back, until we have less space then viewport size
168 start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size);
169 for (; cache_access(obj, pd, start_id) >= relevant_space_size && start_id > 0; start_id --) { }
170
171 //starting on the start id, we are walking down until the sum of elements is bigger than the lower part of the viewport.
172 end_id = start_id;
173 for (; end_id <= pd->size && cache_access(obj, pd, end_id) <= relevant_space_size + relevant_viewport ; end_id ++) { }
174 end_id = MAX(end_id, start_id + 1);
175 end_id = MIN(end_id, pd->size);
176
177 #ifdef DEBUG
178 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]);
179 #endif
180 if (relevant_space_size > 0)
181 EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, start_id) <= relevant_space_size);
182 if (end_id != pd->size)
183 EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, end_id) >= relevant_space_size + relevant_viewport);
184 EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
185
186 //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
187 //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
188 if (end_id <= pd->prev_run.start_id || start_id >= pd->prev_run.end_id)
189 {
190 //it is important to first make the segment visible here, and then hide the rest
191 //otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
192 vis_change_segment(pd, start_id, end_id, EINA_TRUE);
193 vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
194 }
195 else
196 {
197 vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
198 vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
199 }
200
201 geom = pd->viewport;
202
203 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
204 geom.y -= (relevant_space_size - cache_access(obj, pd, start_id));
205 else
206 geom.x -= (relevant_space_size - cache_access(obj, pd, start_id));
207
208 for (i = start_id; i < end_id; ++i)
209 {
210 Eina_Size2D size;
211 Efl_Gfx_Entity *ent = NULL;
212
213 EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, i, (void**) &size));
214 EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
215
216 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
217 geom.h = size.h;
218 else
219 geom.w = size.w;
220 if (ent)
221 efl_gfx_entity_geometry_set(ent, geom);
222 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
223 geom.y += size.h;
224 else
225 geom.x += size.w;
226 }
227 pd->prev_run.start_id = start_id;
228 pd->prev_run.end_id = end_id;
229}
230
231static Eina_Value
232_rebuild_job_cb(void *data, Eina_Value v EINA_UNUSED, const Eina_Future *f EINA_UNUSED)
233{
234 MY_DATA_GET(data, pd);
235
236 if (!efl_alive_get(data)) return EINA_VALUE_EMPTY;
237
238 cache_require(data, pd);
239 recalc_absolut_size(data, pd);
240 position_content(data, pd);
241 pd->rebuild_absolut_size = NULL;
242
243 return EINA_VALUE_EMPTY;
244}
245
246static void
247schedule_recalc_absolut_size(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
248{
249 if (pd->rebuild_absolut_size) return;
250
251 pd->rebuild_absolut_size = efl_loop_job(efl_app_main_get());
252 eina_future_then(pd->rebuild_absolut_size, _rebuild_job_cb, obj);
253}
254
255EOLIAN static void
256_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)
257{
258 cache_invalidate(obj, pd);
259 pd->content_acc = content_access;
260 pd->size_acc = size_access;
261 pd->size = size;
262}
263
264EOLIAN static void
265_efl_ui_list_position_manager_efl_ui_item_position_manager_viewport_set(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, Eina_Rect size)
266{
267 pd->viewport = size;
268
269 recalc_absolut_size(obj, pd);
270 position_content(obj, pd);
271}
272
273EOLIAN static void
274_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)
275{
276 pd->scroll_position.x = x;
277 pd->scroll_position.y = y;
278 position_content(obj, pd);
279}
280
281EOLIAN static void
282_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)
283{
284 if (pd->size == 0)
285 {
286 pd->prev_run.start_id = 0;
287 pd->prev_run.end_id = 0;
288 }
289 pd->size ++;
290 if (subobj)
291 {
292 efl_gfx_entity_visible_set(subobj, EINA_FALSE);
293 }
294 cache_invalidate(obj, pd);
295 schedule_recalc_absolut_size(obj, pd);
296}
297
298EOLIAN static void
299_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)
300{
301 pd->size --;
302 if (subobj)
303 {
304 efl_gfx_entity_visible_set(subobj, EINA_TRUE);
305 }
306 cache_invalidate(obj, pd);
307 schedule_recalc_absolut_size(obj, pd);
308}
309
310EOLIAN static Eina_Rect
311_efl_ui_list_position_manager_efl_ui_item_position_manager_position_single_item(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, int idx)
312{
313 Eina_Rect geom;
314 Eina_Size2D space_size;
315 int relevant_space_size;
316 Eina_Size2D size;
317
318 if (!pd->size) return EINA_RECT(0,0,0,0);
319
320 //space size contains the amount of space that is outside the viewport (either to the top or to the left)
321 space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
322 space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
323
324 EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0));
325 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
326 {
327 relevant_space_size = space_size.h;
328 }
329 else
330 {
331 relevant_space_size = space_size.w;
332 }
333
334 geom = pd->viewport;
335
336 eina_accessor_data_get(pd->size_acc, idx, (void**)&size);
337
338 if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
339 {
340 geom.y -= (relevant_space_size - cache_access(obj, pd, idx));
341 geom.h = size.h;
342 }
343 else
344 {
345 geom.x -= (relevant_space_size - cache_access(obj, pd, idx));
346 geom.w = size.w;
347 }
348 return geom;
349}
350
351EOLIAN static void
352_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)
353{
354 cache_invalidate(obj, pd);
355 schedule_recalc_absolut_size(obj, pd);
356}
357
358EOLIAN static void
359_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)
360{
361 pd->dir = dir;
362 //in order to reset the state of the visible items, just hide everything and set the old segment accordingly
363 vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
364 pd->prev_run.start_id = 0;
365 pd->prev_run.end_id = 0;
366
367 cache_invalidate(obj, pd);
368 cache_require(obj,pd);
369 recalc_absolut_size(obj, pd);
370 position_content(obj, pd);
371}
372
373EOLIAN static Efl_Ui_Layout_Orientation
374_efl_ui_list_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
375{
376 return pd->dir;
377}
378
379EOLIAN static void
380_efl_ui_list_position_manager_efl_object_destructor(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
381{
382 if (pd->rebuild_absolut_size)
383 eina_future_cancel(pd->rebuild_absolut_size);
384
385 efl_destructor(efl_super(obj, MY_CLASS));
386}
387
388
389#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 @@
1class @beta Efl.Ui.List_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager
2{
3 [[Implementation of Efl.Ui.Item_Position_manager for a list
4
5 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.
6 ]]
7 implements {
8 Efl.Object.destructor;
9 Efl.Ui.Item_Position_Manager.data_access {set;}
10 Efl.Ui.Item_Position_Manager.viewport {set;}
11 Efl.Ui.Item_Position_Manager.scroll_position {set;}
12 Efl.Ui.Item_Position_Manager.item_added;
13 Efl.Ui.Item_Position_Manager.item_removed;
14 Efl.Ui.Item_Position_Manager.position_single_item;
15 Efl.Ui.Item_Position_Manager.item_size_changed;
16 Efl.Ui.Layout_Orientable.orientation {set; get;}
17 }
18}
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 = [
181 'efl_ui_clickable.eo', 181 'efl_ui_clickable.eo',
182 'efl_ui_clickable_util.eo', 182 'efl_ui_clickable_util.eo',
183 'efl_ui_format.eo', 183 'efl_ui_format.eo',
184 'efl_ui_item_container.eo',
185 'efl_ui_item_position_manager.eo',
186 'efl_ui_list_position_manager.eo',
187 'efl_ui_grid_position_manager.eo',
184] 188]
185 189
186foreach eo_file : pub_eo_files 190foreach eo_file : pub_eo_files
@@ -938,6 +942,10 @@ elementary_src = [
938 'efl_ui_clickable_util.c', 942 'efl_ui_clickable_util.c',
939 'efl_ui_format.c', 943 'efl_ui_format.c',
940 'efl_ui_scroll_util.c', 944 'efl_ui_scroll_util.c',
945 'efl_ui_item_container.c',
946 'efl_ui_item_position_manager.c',
947 'efl_ui_list_position_manager.c',
948 'efl_ui_grid_position_manager.c',
941] 949]
942 950
943elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl] 951elementary_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[] = {
32 { "efl_ui_win", efl_ui_test_win }, 32 { "efl_ui_win", efl_ui_test_win },
33 { "efl_ui_spin", efl_ui_test_spin }, 33 { "efl_ui_spin", efl_ui_test_spin },
34 { "efl_ui_spin_button", efl_ui_test_spin_button }, 34 { "efl_ui_spin_button", efl_ui_test_spin_button },
35 { "efl_ui_item_container", efl_ui_test_item_container },
36 { "efl_ui_grid_container", efl_ui_test_grid_container },
37 { "efl_ui_list_container", efl_ui_test_list_container },
35 { NULL, NULL } 38 { NULL, NULL }
36}; 39};
37 40
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);
43void efl_ui_test_win(TCase *tc); 43void efl_ui_test_win(TCase *tc);
44void efl_ui_test_spin(TCase *tc); 44void efl_ui_test_spin(TCase *tc);
45void efl_ui_test_spin_button(TCase *tc); 45void efl_ui_test_spin_button(TCase *tc);
46void efl_ui_test_item_container(TCase *tc);
47void efl_ui_test_list_container(TCase *tc);
48void efl_ui_test_grid_container(TCase *tc);
46 49
47void loop_timer_interval_set(Eo *obj, double in); 50void loop_timer_interval_set(Eo *obj, double in);
48 51
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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include "efl_ui_suite.h"
7#include "efl_ui_test_item_container_common.h"
8
9static Eo *win;
10
11static void
12item_container_setup()
13{
14 Eo * list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
15 position_manager = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
16 win = win_add();
17 item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
18 efl_ui_item_container_position_manager_set(efl_added, list));
19
20}
21
22static void
23item_container_teardown()
24{
25 item_container = NULL;
26 position_manager = NULL;
27 win = NULL;
28}
29
30void efl_ui_test_grid_container(TCase *tc)
31{
32 tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
33 tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
34 efl_ui_test_item_container_common_add(tc);
35 efl_ui_test_position_manager_common_add(tc);
36}
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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include "efl_ui_suite.h"
7#include "efl_ui_test_item_container_common.h"
8
9static Eo* win;
10
11static void
12item_container_setup()
13{
14 win = win_add();
15}
16
17static void
18item_container_teardown()
19{
20 win = NULL;
21}
22
23EFL_START_TEST(finalizer_check)
24{
25 Eo *grid = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
26 Eo *list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
27 Eo *random_obj = efl_add(EFL_UI_BUTTON_CLASS, win);
28 Eo *o;
29
30 EXPECT_ERROR_START;
31 ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win), NULL);
32 EXPECT_ERROR_END;
33
34 EXPECT_ERROR_START;
35 ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, random_obj)), NULL);
36 EXPECT_ERROR_END;
37
38 o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, grid));
39 ck_assert_ptr_ne(o, NULL);
40 ck_assert_ptr_eq(efl_parent_get(grid), o);
41 efl_del(o);
42 o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, list));
43 ck_assert_ptr_ne(o, NULL);
44 ck_assert_ptr_eq(efl_parent_get(list), o);
45 efl_del(o);
46}
47EFL_END_TEST
48
49void efl_ui_test_item_container(TCase *tc)
50{
51 tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
52 tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
53 tcase_add_test(tc, finalizer_check);
54}
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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include "efl_ui_suite.h"
7#include "efl_ui_test_item_container_common.h"
8
9Eo *item_container;
10
11void
12fill_items(const Efl_Class *klass)
13{
14 for (int i = 0; i < 3; ++i)
15 {
16 char buf[PATH_MAX];
17 Eo *it = efl_add(klass, item_container);
18
19 snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13);
20 efl_text_set(it, buf);
21 efl_pack_end(item_container, it);
22 }
23}
24
25static void
26_set_pointer_quit(void *data, const Efl_Event *ev)
27{
28 Efl_Ui_Item **b = data;
29
30 ck_assert_ptr_eq(*b, NULL);
31 *b = ev->info;
32}
33
34static void
35_iterator_to_array(Eina_Array **arr, Eina_Iterator *iter)
36{
37 Efl_Ui_Widget *widget;
38
39 *arr = eina_array_new(10);
40
41 EINA_ITERATOR_FOREACH(iter, widget)
42 {
43 eina_array_push(*arr, widget);
44 }
45 eina_iterator_free(iter);
46}
47
48EFL_START_TEST(test_multi_select)
49{
50 Efl_Ui_Item *selected = NULL;
51 Efl_Ui_Item *unselected = NULL;
52 Eina_Array *arr_selected;
53 efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI);
54 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
55 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
56 fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
57
58 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
59 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
60 ck_assert_ptr_eq(unselected, NULL);
61 selected = NULL;
62 unselected = NULL;
63 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
64 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
65 ck_assert_ptr_eq(unselected, NULL);
66 selected = NULL;
67 unselected = NULL;
68 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_TRUE);
69 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
70 ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
71 _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
72 ck_assert_int_eq(eina_array_count(arr_selected), 2);
73 ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 0));
74 ck_assert_ptr_eq(eina_array_data_get(arr_selected, 1), efl_pack_content_get(item_container, 2));
75
76 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
77 ck_assert_ptr_eq(selected, NULL);
78 ck_assert_ptr_eq(unselected, NULL);
79 selected = NULL;
80 unselected = NULL;
81 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
82 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
83}
84EFL_END_TEST
85
86EFL_START_TEST(test_multi_select_removal)
87{
88 Efl_Ui_Item *selected = NULL;
89 Efl_Ui_Item *unselected = NULL;
90 Eina_Array *arr_selected;
91 efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI);
92 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
93 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
94 fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
95
96 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
97 selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
98 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
99 selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
100 unselected = NULL;
101 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_FALSE);
102 ck_assert_ptr_eq(selected, NULL);
103 ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
104 selected = NULL;
105 unselected = NULL;
106 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_FALSE);
107 ck_assert_ptr_eq(selected, NULL);
108 ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 2));
109 selected = NULL;
110 unselected = NULL;
111
112 ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL);
113 _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
114 ck_assert_int_eq(eina_array_count(arr_selected), 0);
115 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
116 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
117}
118EFL_END_TEST
119
120EFL_START_TEST(test_single_select)
121{
122 Efl_Ui_Item *selected = NULL;
123 Efl_Ui_Item *unselected = NULL;
124 Eina_Array *arr_selected;
125
126 efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE);
127 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
128 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
129 fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
130
131 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
132 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
133 ck_assert_ptr_eq(unselected, NULL);
134 selected = NULL;
135 unselected = NULL;
136 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
137 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
138 ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
139 selected = NULL;
140 unselected = NULL;
141 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
142 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
143 ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
144 _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
145 ck_assert_int_eq(eina_array_count(arr_selected), 1);
146 ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2));
147
148 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
149 ck_assert_ptr_eq(selected, NULL);
150 ck_assert_ptr_eq(unselected, NULL);
151 selected = NULL;
152 unselected = NULL;
153 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
154 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
155}
156EFL_END_TEST
157
158EFL_START_TEST(test_single_select_always)
159{
160 Efl_Ui_Item *selected = NULL;
161 Efl_Ui_Item *unselected = NULL;
162 Eina_Array *arr_selected;
163
164 efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE_ALWAYS);
165 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
166 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
167 fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
168
169 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
170 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
171 ck_assert_ptr_eq(unselected, NULL);
172 selected = NULL;
173 unselected = NULL;
174 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
175 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
176 ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
177 selected = NULL;
178 unselected = NULL;
179 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
180 ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
181 ck_assert_ptr_eq(unselected, NULL);
182 selected = NULL;
183 unselected = NULL;
184 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
185 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
186 ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
187 _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
188 ck_assert_int_eq(eina_array_count(arr_selected), 1);
189 ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2));
190 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
191 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
192}
193EFL_END_TEST
194
195EFL_START_TEST(test_none_select)
196{
197 Efl_Ui_Item *selected = NULL;
198 Efl_Ui_Item *unselected = NULL;
199 Eina_Array *arr_selected;
200
201 efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_NONE);
202 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
203 efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
204 fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
205
206 efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
207 ck_assert_ptr_eq(selected, NULL);
208 ck_assert_ptr_eq(unselected, NULL);
209 selected = NULL;
210 unselected = NULL;
211 efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
212 ck_assert_ptr_eq(selected, NULL);
213 ck_assert_ptr_eq(unselected, NULL);
214 selected = NULL;
215 unselected = NULL;
216 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
217 ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_FALSE);
218 ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL);
219 _iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
220 ck_assert_int_eq(eina_array_count(arr_selected), 0);
221 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
222 efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
223}
224EFL_END_TEST
225
226void efl_ui_test_item_container_common_add(TCase *tc)
227{
228 tcase_add_test(tc, test_multi_select);
229 tcase_add_test(tc, test_multi_select_removal);
230 tcase_add_test(tc, test_single_select);
231 tcase_add_test(tc, test_none_select);
232 tcase_add_test(tc, test_single_select_always);
233}
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 @@
1#ifndef EFL_UI_TEST_ITEM_CONTAINER_COMMON_H
2#define EFL_UI_TEST_ITEM_CONTAINER_COMMON_H 1
3
4extern Eo *item_container;
5extern Eo *position_manager;
6
7void fill_items(const Efl_Class *klass);
8void efl_ui_test_item_container_common_add(TCase *tc);
9void efl_ui_test_position_manager_common_add(TCase *tc);
10
11#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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include "efl_ui_suite.h"
7#include "efl_ui_test_item_container_common.h"
8
9static Eo *win;
10
11static void
12item_container_setup()
13{
14 Eo * list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
15 position_manager = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
16 win = win_add();
17 item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
18 efl_ui_item_container_position_manager_set(efl_added, list));
19
20}
21
22static void
23item_container_teardown()
24{
25 item_container = NULL;
26 position_manager = NULL;
27 win = NULL;
28}
29
30void efl_ui_test_list_container(TCase *tc)
31{
32 tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
33 tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
34 efl_ui_test_item_container_common_add(tc);
35 efl_ui_test_position_manager_common_add(tc);
36}
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 @@
1#ifdef HAVE_CONFIG_H
2# include "elementary_config.h"
3#endif
4
5#include <Efl_Ui.h>
6#include "efl_ui_suite.h"
7#include "efl_ui_test_item_container_common.h"
8
9Eo *position_manager;
10
11static Eo* win;
12
13static Eina_Array *arr_obj;
14static Eina_Inarray *arr_size;
15static Eina_Accessor inner_size_acc;
16static Eina_Accessor *size_acc;
17static void
18item_container_setup()
19{
20 win = win_add();
21}
22
23static void
24item_container_teardown()
25{
26 win = NULL;
27}
28
29static Eina_Bool
30_get_at(Eina_Accessor *it EINA_UNUSED, unsigned int idx, void **data)
31{
32 Eina_Size2D *result_ptr = (void*)data;
33 Eina_Size2D *inner_result;
34
35 if (!eina_accessor_data_get(size_acc, idx, (void*)&inner_result))
36 return EINA_FALSE;
37 *result_ptr = *inner_result;
38 return EINA_TRUE;
39}
40
41static void
42_free_cb(Eina_Accessor *it EINA_UNUSED)
43{
44 eina_accessor_free(size_acc);
45}
46
47static Eina_Bool
48_lock_cb(Eina_Accessor *it EINA_UNUSED)
49{
50 return eina_accessor_lock(size_acc);
51}
52
53static Eina_Accessor*
54_clone_cb(Eina_Accessor *it EINA_UNUSED)
55{
56 return eina_accessor_clone(size_acc);
57}
58
59
60static void
61_initial_setup(void)
62{
63 arr_obj = eina_array_new(10);
64 arr_size = eina_inarray_new(sizeof(Eina_Size2D), 10);
65 size_acc = eina_inarray_accessor_new(arr_size);
66
67 inner_size_acc.version = EINA_ACCESSOR_VERSION;
68 EINA_MAGIC_SET(&inner_size_acc, EINA_MAGIC_ACCESSOR);
69 inner_size_acc.get_at = _get_at;
70 inner_size_acc.free = _free_cb;
71 inner_size_acc.lock = _lock_cb;
72 inner_size_acc.clone = _clone_cb;
73
74 efl_ui_item_position_manager_data_access_set(position_manager,
75 eina_array_accessor_new(arr_obj),
76 &inner_size_acc, 0);
77}
78
79static int
80_add_item(Eo *obj, Eina_Size2D size)
81{
82 int idx = eina_array_count(arr_obj);
83 EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_array_count(arr_obj) == eina_inarray_count(arr_size), -1);
84
85 eina_array_push(arr_obj, (void*)0x1); //wtf
86 eina_array_data_set(arr_obj, idx, obj);
87 eina_inarray_push(arr_size, &size);
88
89
90 efl_ui_item_position_manager_item_added(position_manager, idx, obj);
91
92 return idx;
93}
94
95static void
96_update_item(int index, Eo *obj, Eina_Size2D size)
97{
98 Eina_Size2D *s;
99
100 eina_array_data_set(arr_obj, index, obj);
101 s = eina_inarray_nth(arr_size, index);
102 *s = size;
103}
104
105static void
106_ticker(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
107{
108 efl_loop_quit(efl_main_loop_get(), EINA_VALUE_EMPTY);
109}
110
111static void
112_iterate_a_few(void)
113{
114 efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
115 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _ticker, NULL),
116 efl_loop_timer_interval_set(efl_added, 0.1));
117 efl_loop_begin(efl_main_loop_get());
118}
119
120EFL_START_TEST(no_crash1)
121{
122 _initial_setup();
123
124 //try to resize the viewport while we have no item
125 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
126 _iterate_a_few();
127 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
128 _iterate_a_few();
129 _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
130 _iterate_a_few();
131 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
132 _iterate_a_few();
133 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
134 _iterate_a_few();
135}
136EFL_END_TEST
137
138EFL_START_TEST(no_crash2)
139{
140 _initial_setup();
141
142 //try to resize the viewport while we have no item
143 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
144 _iterate_a_few();
145 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
146 _iterate_a_few();
147 _add_item(NULL, EINA_SIZE2D(20, 20));
148 _iterate_a_few();
149 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
150 _iterate_a_few();
151 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
152 _iterate_a_few();
153 _update_item(0, efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
154 _iterate_a_few();
155}
156EFL_END_TEST
157
158static void
159_content_size_cb(void *data, const Efl_Event *ev)
160{
161 Eina_Size2D *size = data;
162 *size = *((Eina_Size2D*)ev->info);
163}
164
165EFL_START_TEST(viewport_newsize_event_result)
166{
167 Eina_Size2D size = EINA_SIZE2D(-2, -2), min_size = EINA_SIZE2D(-2, -2);
168 efl_event_callback_add(position_manager,
169 EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _content_size_cb, &size);
170 efl_event_callback_add(position_manager,
171 EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _content_size_cb, &min_size);
172 _initial_setup();
173 _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
174 _add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 30));
175
176 efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(0, 0, 200, 200));
177 _iterate_a_few();
178
179 ck_assert_int_ne(size.w, -2);
180 ck_assert_int_ne(size.h, -2);
181 ck_assert_int_ne(min_size.w, -2);
182 ck_assert_int_ne(min_size.h, -2);
183}
184EFL_END_TEST
185
186void efl_ui_test_position_manager_common_add(TCase *tc)
187{
188 tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
189 tcase_add_test(tc, no_crash1);
190 tcase_add_test(tc, no_crash2);
191 tcase_add_test(tc, viewport_newsize_event_result);
192}
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 = [
146 'efl_ui_test_spin.c', 146 'efl_ui_test_spin.c',
147 'efl_ui_test_spin_button.c', 147 'efl_ui_test_spin_button.c',
148 'efl_ui_test_slider.c', 148 'efl_ui_test_slider.c',
149 'efl_ui_test_item_container_common.c',
150 'efl_ui_test_item_container.c',
151 'efl_ui_test_list_container.c',
152 'efl_ui_test_grid_container.c',
153 'efl_ui_test_position_manager_common.c',
149] 154]
150 155
151efl_ui_suite = executable('efl_ui_suite', 156efl_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 @@
9 9
10/* spec-meta-start 10/* spec-meta-start
11 {"test-interface":"Efl.Pack_Linear", 11 {"test-interface":"Efl.Pack_Linear",
12 "test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container"], 12 "test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container", "Test.Efl.Ui.Item_Container_List"],
13 "custom-mapping" : { 13 "custom-mapping" : {
14 "Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS" 14 "Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS",
15 "Test.Efl.Ui.Item_Container_List" : "EFL_UI_LIST_DEFAULT_ITEM_CLASS"
15 } 16 }
16 } 17 }
17 spec-meta-end */ 18 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 @@
8#include "suite_helpers.h" 8#include "suite_helpers.h"
9#include "eo_internal.h" 9#include "eo_internal.h"
10 10
11//helper functions for custom widget intialization
12EOLIAN static Efl_Object*
13_test_efl_ui_item_container_list_efl_object_constructor(Eo *obj, void *pd EINA_UNUSED)
14{
15 efl_constructor(efl_super(obj, TEST_EFL_UI_ITEM_CONTAINER_LIST_CLASS));
16 efl_ui_item_container_position_manager_set(obj, efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS));
17
18 return obj;
19}
20
11Evas_Object *win = NULL; 21Evas_Object *win = NULL;
12Evas_Object *widget = NULL; 22Evas_Object *widget = NULL;
13const Efl_Class *test_content_klass = NULL; 23const Efl_Class *test_content_klass = NULL;
@@ -63,3 +73,5 @@ main(int argc, char **argv)
63 73
64 return (failed_count == 0) ? 0 : 255; 74 return (failed_count == 0) ? 0 : 255;
65} 75}
76
77#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 @@
5 5
6#define EFL_NOLEGACY_API_SUPPORT 6#define EFL_NOLEGACY_API_SUPPORT
7#include <Efl_Ui.h> 7#include <Efl_Ui.h>
8#include "test_efl_ui_item_container_list.eo.h"
8#include "../efl_check.h" 9#include "../efl_check.h"
9 10
10extern Evas_Object *win; 11extern 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 @@
1priv_eo_files = [
2 'test_efl_ui_item_container_list.eo',
3]
1 4
2efl_ui_suite_behavior_test_files = files([ 5efl_ui_suite_behavior_test_files = files([
3 'efl_test_pack.c', 6 'efl_test_pack.c',
@@ -21,6 +24,20 @@ efl_ui_suite_behavior_src = files([
21 24
22test_generator = find_program('generator.py') 25test_generator = find_program('generator.py')
23 26
27
28priv_eo_file_target = []
29foreach eo_file : priv_eo_files
30 priv_eo_file_target += custom_target('eolian_gen_' + eo_file,
31 input : eo_file,
32 output : [eo_file + '.h'],
33 depfile : eo_file + '.d',
34 command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories,
35 '-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'),
36 '-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'),
37 '-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'),
38 '-gchd', '@INPUT@'])
39endforeach
40
24generated_test_parts = custom_target('generate_test_suite', 41generated_test_parts = custom_target('generate_test_suite',
25 input: efl_ui_suite_behavior_test_files, 42 input: efl_ui_suite_behavior_test_files,
26 output: 'efl_ui_spec_suite_gen.x', 43 output: 'efl_ui_spec_suite_gen.x',
@@ -28,7 +45,7 @@ generated_test_parts = custom_target('generate_test_suite',
28) 45)
29 46
30efl_ui_behavior_suite = executable('efl_ui_spec_suite', 47efl_ui_behavior_suite = executable('efl_ui_spec_suite',
31 efl_ui_suite_behavior_src + [generated_test_parts], 48 efl_ui_suite_behavior_src + [generated_test_parts] + [priv_eo_file_target],
32 dependencies: [check, eina, elementary, elementary_deps], 49 dependencies: [check, eina, elementary, elementary_deps],
33 include_directories : [config_dir] + [elementary_config_dir] + [include_directories('../')], 50 include_directories : [config_dir] + [elementary_config_dir] + [include_directories('../')],
34 c_args : [ 51 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 @@
1class @beta Test.Efl.Ui.Item_Container_List extends Efl.Ui.Item_Container {
2 data: null;
3 implements {
4 Efl.Object.constructor;
5 }
6}