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
This commit is contained in:
Marcel Hollerbach 2019-06-29 16:19:08 +02:00 committed by Cedric BAIL
parent 0cd5427c3f
commit 577b82dad6
31 changed files with 2824 additions and 4 deletions

View File

@ -207,4 +207,5 @@ collections {
#include "edc/efl/tab_pager.edc"
#include "edc/efl/tab_bar.edc"
#include "edc/efl/tab_page.edc"
#include "edc/efl/item_container.edc"
}

View File

@ -0,0 +1,3 @@
group { "efl/item_container";
inherit: "efl/scroller";
}

View File

@ -0,0 +1,100 @@
#include <Efl_Ui.h>
static Eo *first, *last, *middle;
static int timer = 15;
static int frames = 0;
static double start_time;
static void
_timer_tick(void *data, const Efl_Event *ev)
{
if (timer % 2 == 0)
{
efl_ui_item_container_item_scroll(data, last, EINA_TRUE);
}
else
{
efl_ui_item_container_item_scroll(data, first, EINA_TRUE);
}
timer--;
if (timer == 0)
{
double runtime = ecore_time_get() - start_time;
efl_loop_quit(efl_app_main_get(), EINA_VALUE_EMPTY);
efl_del(ev->object);
printf("We did %d frames in %f s seconds\n", frames, runtime);
printf("FPS: %f\n", ((double)frames / runtime));
}
}
static void
_caputure(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
frames ++;
}
static void
_started_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
efl_loop_timer_interval_set(efl_added, 1.0),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _timer_tick, data)
);
start_time = ecore_time_get();
efl_event_callback_add(evas_object_evas_get(data), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _caputure, data);
efl_del(ev->object);
}
static void
_first_frame_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
efl_ui_item_container_item_scroll(data, middle, EINA_FALSE);
//give time to stabelize
efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
efl_loop_timer_interval_set(efl_added, 15.0),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _started_cb, data)
);
efl_event_callback_del(ev->object, EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, data);
}
EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
Eo *win, *item_container, *list;
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC),
efl_text_set(efl_added, "Efl.Ui.Item_Container benchmark"),
efl_ui_win_autodel_set(efl_added, EINA_TRUE)
);
list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
efl_ui_item_container_position_manager_set(efl_added, list));
efl_content_set(win, item_container);
printf("Building 5000 objects\n");
for (int i = 0; i < 5000; ++i)
{
Eo *il = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container);
double r = 10+((double)190/(double)10)*(i%10);
if (i == 0)
first = il;
else if (i == 2500)
middle = il;
else if (i == 4999)
last = il;
efl_gfx_color_set(il, r, 10, 10, 255);
efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40));
efl_pack_end(item_container, il);
}
printf("Done!\n");
efl_gfx_entity_size_set(win, EINA_SIZE2D(200, 200));
efl_event_callback_add(evas_object_evas_get(win), EFL_CANVAS_SCENE_EVENT_RENDER_POST, _first_frame_cb, item_container);
}
EFL_MAIN()

View File

@ -6,3 +6,10 @@ focus_widget_tree_bench = executable('focus_widget_tree_bench',
benchmark('focus_widget_tree', focus_widget_tree_bench,
args: ['5'],
)
item_container = executable('item_container',
'item_container.c',
dependencies: [elementary, ecore_input_evas, eio],
)
benchmark('item_container', item_container)

View File

@ -156,6 +156,7 @@ elementary_test_src = [
'test_gesture_framework.c',
'test_ui_tab_pager.c',
'test_ui_relative_layout.c',
'test_ui_item_container.c',
'test.h'
]

View File

@ -394,7 +394,8 @@ void test_ui_spotlight_scroll(void *data, Evas_Object *obj, void *event_info);
void test_ui_relative_layout(void *data, Evas_Object *obj, void *event_info);
void test_efl_ui_radio(void *data, Evas_Object *obj, void *event_info);
void test_efl_ui_item_container_list(void *data, Evas_Object *obj, void *event_info );
void test_efl_ui_item_container_grid(void *data, Evas_Object *obj, void *event_info);
static void _list_udpate(void);
static Evas_Object *win, *tbx, *entry; // TODO: refactoring
@ -868,6 +869,8 @@ add_tests:
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table (Linear API)", test_ui_table_linear);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Table_Static", test_ui_table_static);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Relative_Layout", test_ui_relative_layout);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container List", test_efl_ui_item_container_list);
ADD_TEST_EO(NULL, "Containers", "Efl.Ui.Item_Container Grid", test_efl_ui_item_container_grid);
//------------------------------//
ADD_TEST_EO(NULL, "Events", "Event Refeed", test_events);

View File

@ -0,0 +1,272 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
static void
_selection_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
if (efl_ui_check_selected_get(ev->object))
efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_VERTICAL);
else
efl_ui_layout_orientation_set(data, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL);
}
static void
_scroll_to_animated_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Ui_Widget *element_1154 = efl_pack_content_get(data, 1154);
EINA_SAFETY_ON_NULL_RETURN(element_1154);
efl_ui_item_container_item_scroll(data, element_1154, EINA_TRUE);
}
static void
_scroll_to_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Ui_Widget *element_10 = efl_pack_content_get(data, 10);
EINA_SAFETY_ON_NULL_RETURN(element_10);
efl_ui_item_container_item_scroll(data, element_10, EINA_FALSE);
}
static void
_change_min_size_cb(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
static Eina_Bool b = EINA_FALSE;
Efl_Ui_Widget *element_0 = efl_pack_content_get(data, 0);
EINA_SAFETY_ON_NULL_RETURN(element_0);
if (!b)
{
efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 200));
}
else
{
efl_gfx_hint_size_min_set(element_0, EINA_SIZE2D(40, 40));
}
b = !b;
}
typedef struct {
Efl_Ui_Check *v, *h;
Efl_Ui_Item_Container *c;
} Match_Content_Ctx;
static void
_selection_changed_match_content_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Match_Content_Ctx *c = data;
Eina_Bool v,h;
v = efl_ui_check_selected_get(c->v);
h = efl_ui_check_selected_get(c->h);
efl_ui_scrollable_match_content_set(c->c, v, h);
}
static void
_widget_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
free(data);
}
static void
_add_item(Efl_Ui_Item_Container *c)
{
Efl_Class *itc = efl_key_data_get(c, "__item_class");
char buf[PATH_MAX];
int r = 0, g = 0, b = 0;
int i = efl_content_count(c);
Eo *rect;
Eo *il;
il = efl_add(itc, c);
snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13);
efl_text_set(il, buf);
rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(c));
switch (i % 5)
{
case 0:
r = 255;
break;
case 1:
g = 255;
break;
case 2:
b = 255;
break;
case 3:
r = g = b = 255;
break;
case 4:
r = g = b = 0;
break;
}
efl_gfx_color_set(rect, r, g, b, 255);
efl_content_set(il, rect);
if (itc == EFL_UI_GRID_DEFAULT_ITEM_CLASS)
efl_gfx_hint_size_min_set(il, EINA_SIZE2D(100, 180));
else
efl_gfx_hint_size_min_set(il, EINA_SIZE2D(40, 40+(i%2)*40));
efl_pack_end(c, il);
}
static void
_remove_all_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
efl_pack_clear(data);
}
static void
_add_one_item(void *data, const Efl_Event *ev EINA_UNUSED)
{
_add_item(data);
}
static void
_add_thousend_items(void *data, const Efl_Event *ev EINA_UNUSED)
{
for (int i = 0; i < 1000; ++i)
{
_add_item(data);
}
}
static void
_select_value_cb(void *data, const Efl_Event *ev)
{
Efl_Ui_Item_Container *c = data;
efl_ui_select_mode_set(c, efl_ui_radio_group_selected_value_get(ev->object));
}
void create_item_container_ui(Efl_Ui_Item_Position_Manager *manager, const Efl_Class *item, const char *name)
{
Efl_Ui_Win *win, *o, *tbl, *item_container, *bx;
Match_Content_Ctx *ctx = calloc(1, sizeof(*ctx));
win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC),
efl_text_set(efl_added, name),
efl_ui_win_autodel_set(efl_added, EINA_TRUE));
tbl = efl_add(EFL_UI_TABLE_CLASS, win);
efl_content_set(win, tbl);
item_container = o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
efl_ui_item_container_position_manager_set(efl_added, manager));
efl_key_data_set(o, "__item_class", item);
efl_event_callback_add(o, EFL_EVENT_DEL, _widget_del_cb, ctx);
for (int i = 0; i < 2000; ++i)
{
_add_item(o);
}
efl_pack_table(tbl, o, 1, 0, 1, 11);
ctx->c = o;
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Scroll to 1154 ANIMATED");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_animated_cb, item_container);
efl_pack_table(tbl, o, 0, 1, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Scroll to 10");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _scroll_to_cb, item_container);
efl_pack_table(tbl, o, 0, 2, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Change min size of 0");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _change_min_size_cb, item_container);
efl_pack_table(tbl, o, 0, 3, 1, 1);
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Vertical");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_cb, item_container);
efl_ui_check_selected_set(o, EINA_TRUE);
efl_pack_table(tbl, o, 0, 4, 1, 1);
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Match Vertical");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
efl_pack_table(tbl, o, 0, 5, 1, 1);
ctx->v = o;
o = efl_add(EFL_UI_CHECK_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Match Horizontal");
efl_event_callback_add(o, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selection_changed_match_content_cb, ctx);
efl_pack_table(tbl, o, 0, 6, 1, 1);
efl_gfx_entity_size_set(win, EINA_SIZE2D(260, 200));
ctx->h = o;
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Remove all items");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _remove_all_cb, item_container);
efl_pack_table(tbl, o, 0, 7, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Add 1 item");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_one_item, item_container);
efl_pack_table(tbl, o, 0, 8, 1, 1);
o = efl_add(EFL_UI_BUTTON_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_text_set(o, "Add 1000 item");
efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _add_thousend_items, item_container);
efl_pack_table(tbl, o, 0, 9, 1, 1);
bx = efl_add(EFL_UI_RADIO_BOX_CLASS, tbl,
efl_gfx_hint_weight_set(efl_added, 0.0, 0.0),
efl_gfx_hint_align_set(efl_added, 0, 0.5));
efl_event_callback_add(bx, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _select_value_cb, item_container);
efl_pack_table(tbl, bx, 0, 10, 1, 1);
o = efl_add(EFL_UI_RADIO_CLASS, bx,
efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE));
efl_text_set(o, "Singleselect");
efl_pack_end(bx, o);
o = efl_add(EFL_UI_RADIO_CLASS, bx,
efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_SINGLE_ALWAYS));
efl_text_set(o, "Singleselect Always");
efl_pack_end(bx, o);
o = efl_add(EFL_UI_RADIO_CLASS, bx,
efl_ui_radio_state_value_set(efl_added, EFL_UI_SELECT_MODE_MULTI));
efl_text_set(o, "Multiselect");
efl_pack_end(bx, o);
}
void test_efl_ui_item_container_grid(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
create_item_container_ui(efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS), EFL_UI_GRID_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container Grid");
}
void test_efl_ui_item_container_list(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
create_item_container_ui(efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS), EFL_UI_LIST_DEFAULT_ITEM_CLASS, "Efl.Ui.Item_Container List");
}

View File

@ -250,6 +250,15 @@ typedef Eo Efl_Ui_Spotlight_Indicator;
# include <efl_ui_spin.eo.h>
# include <efl_ui_spin_button.eo.h>
# include <efl_ui_slider.eo.h>
# include <efl_ui_item.eo.h>
# include <efl_ui_item_position_manager.eo.h>
# include <efl_ui_item_container.eo.h>
# include <efl_ui_list_position_manager.eo.h>
# include <efl_ui_grid_position_manager.eo.h>
# include <efl_ui_list_item.eo.h>
# include <efl_ui_list_default_item.eo.h>
# include <efl_ui_grid_item.eo.h>
# include <efl_ui_grid_default_item.eo.h>
/**
* Initialize Elementary

View File

@ -0,0 +1,321 @@
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include <Elementary.h>
#include "elm_widget.h"
#include "elm_priv.h"
#define MY_CLASS EFL_UI_GRID_POSITION_MANAGER_CLASS
#define MY_DATA_GET(obj, pd) \
Efl_Ui_Grid_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS);
typedef struct {
Eina_Accessor *content_acc, *size_acc;
unsigned int size;
Eina_Rect viewport;
Eina_Vector2 scroll_position;
Efl_Ui_Layout_Orientation dir;
struct {
unsigned int start_id, end_id;
} prev_run;
Eina_Size2D max_min_size;
Eina_Size2D last_viewport_size;
Eina_Size2D prev_min_size;
struct {
int columns;
int rows;
} current_display_table;
} Efl_Ui_Grid_Position_Manager_Data;
static inline void
vis_change_segment(Efl_Ui_Grid_Position_Manager_Data *pd, int a, int b, Eina_Bool flag)
{
for (int i = MIN(a, b); i < MAX(a, b); ++i)
{
Efl_Gfx_Entity *ent;
EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
if (ent && !efl_ui_focus_object_focus_get(ent))
{
efl_gfx_entity_visible_set(ent, flag);
}
}
}
static void
_reposition_content(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd)
{
Eina_Size2D space_size;
int relevant_space_size, relevant_viewport;
unsigned int start_id, end_id, step;
if (!pd->size) return;
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return;
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
relevant_space_size = space_size.h;
relevant_viewport = pd->viewport.h;
step = pd->max_min_size.h;
}
else
{
relevant_space_size = space_size.w;
relevant_viewport = pd->viewport.w;
step = pd->max_min_size.w;
}
start_id = MIN((MAX(relevant_space_size,0) / step)*pd->current_display_table.columns, pd->size);
end_id = MIN((((MAX(relevant_space_size,0) + relevant_viewport + step) / step)*pd->current_display_table.columns)+1, pd->size);
EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
EINA_SAFETY_ON_FALSE_RETURN(start_id <= pd->size);
//to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
//The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
if (end_id < pd->prev_run.start_id || start_id > pd->prev_run.end_id)
{
//it is important to first make the segment visible here, and then hide the rest
//otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
vis_change_segment(pd, start_id, end_id, EINA_TRUE);
vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
}
else
{
vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
}
for (unsigned int i = start_id; i < end_id; ++i)
{
Eina_Rect geom;
Efl_Gfx_Entity *ent;
geom.size = pd->max_min_size;
geom.pos = pd->viewport.pos;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.x += pd->max_min_size.w*(i%pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(i/pd->current_display_table.columns);
geom.y -= (relevant_space_size);
}
else
{
geom.x += pd->max_min_size.w*(i/pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(i%pd->current_display_table.columns);
geom.x -= (relevant_space_size);
}
EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
//printf(">%d (%d, %d, %d, %d) %p\n", i, geom.x, geom.y, geom.w, geom.h, ent);
efl_gfx_entity_geometry_set(ent, geom);
}
pd->prev_run.start_id = start_id;
pd->prev_run.end_id = end_id;
}
static inline void
_flush_abs_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd)
{
int minor, major;
Eina_Size2D vp_size;
if (!pd->size) return;
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
major = pd->viewport.w/pd->max_min_size.w;
pd->current_display_table.columns = major;
}
else
{
major = pd->viewport.h/pd->max_min_size.h;
pd->current_display_table.columns = major;
}
if (major <= 0) return;
minor = ceil((double)pd->size/(double)major);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
pd->current_display_table.rows = minor;
else
pd->current_display_table.rows = minor;
/*
* calculate how much size we need with major in the given orientation.
* The
*/
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
vp_size.w = pd->viewport.w;
vp_size.h = minor*pd->max_min_size.h;
}
else
{
vp_size.h = pd->viewport.h;
vp_size.w = minor*pd->max_min_size.w;
}
if (vp_size.h != pd->last_viewport_size.h || vp_size.w != pd->last_viewport_size.w)
{
pd->last_viewport_size = vp_size;
efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, &vp_size);
}
}
static inline void
_update_min_size(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int added_index)
{
Eina_Size2D elemsize;
EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, added_index, (void*)&elemsize));
pd->max_min_size.w = MAX(pd->max_min_size.w, elemsize.w);
pd->max_min_size.h = MAX(pd->max_min_size.h, elemsize.h);
}
static inline void
_flush_min_size(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd)
{
Eina_Size2D min_size = pd->max_min_size;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
min_size.h = -1;
else
min_size.w = -1;
if (pd->prev_min_size.w != min_size.w || pd->prev_min_size.h != min_size.h)
{
pd->prev_min_size = min_size;
efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
}
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_data_access_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Eina_Accessor *obj_access, Eina_Accessor *size_access, int size)
{
pd->size_acc = size_access;
pd->content_acc = obj_access;
pd->size = size;
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_viewport_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Eina_Rect viewport)
{
pd->viewport = viewport;
_flush_abs_size(obj, pd);
_reposition_content(obj, pd);
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_scroll_position_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, double x, double y)
{
pd->scroll_position.x = x;
pd->scroll_position.y = y;
_reposition_content(obj, pd);
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_added(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd, int added_index, Efl_Gfx_Entity *subobj EINA_UNUSED)
{
pd->size ++;
efl_gfx_entity_visible_set(subobj, EINA_FALSE);
_update_min_size(obj, pd, added_index);
_flush_min_size(obj, pd);
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we might can skip that
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj EINA_UNUSED)
{
EINA_SAFETY_ON_FALSE_RETURN(pd->size > 0);
pd->size --;
pd->prev_run.start_id = MIN(pd->prev_run.start_id, pd->size);
pd->prev_run.end_id = MIN(pd->prev_run.end_id, pd->size);
//we ignore here that we might loose the item giving the current max min size
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we might can skip that
efl_gfx_entity_visible_set(subobj, EINA_TRUE);
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_item_position_manager_item_size_changed(Eo *obj, Efl_Ui_Grid_Position_Manager_Data *pd, int start_id, int end_id)
{
for (int i = start_id; i <= end_id; ++i)
{
_update_min_size(obj, pd, i);
}
_flush_min_size(obj, pd);
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we could check if this is needed or not
}
EOLIAN static void
_efl_ui_grid_position_manager_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, Efl_Ui_Layout_Orientation dir)
{
pd->dir = dir;
_flush_min_size(obj, pd);
_flush_abs_size(obj, pd);
_reposition_content(obj, pd); //FIXME we could check if this is needed or not
}
EOLIAN static Efl_Ui_Layout_Orientation
_efl_ui_grid_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd)
{
return pd->dir;
}
EOLIAN static Eina_Rect
_efl_ui_grid_position_manager_efl_ui_item_position_manager_position_single_item(Eo *obj EINA_UNUSED, Efl_Ui_Grid_Position_Manager_Data *pd, int idx)
{
Eina_Rect geom;
Eina_Size2D space_size;
unsigned int relevant_space_size;
if (!pd->size) return EINA_RECT(0, 0, 0, 0);
if (pd->max_min_size.w <= 0 || pd->max_min_size.h <= 0) return EINA_RECT(0, 0, 0, 0);
if (pd->current_display_table.columns <= 0 || pd->current_display_table.rows <= 0) return EINA_RECT(0, 0, 0, 0);
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->last_viewport_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
space_size.h = (MAX(pd->last_viewport_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0));
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
relevant_space_size = space_size.h;
}
else
{
relevant_space_size = space_size.w;
}
geom.size = pd->max_min_size;
geom.pos = pd->viewport.pos;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.x += pd->max_min_size.w*(idx%pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(idx/pd->current_display_table.columns);
geom.y -= (relevant_space_size);
}
else
{
geom.x += pd->max_min_size.w*(idx/pd->current_display_table.columns);
geom.y += pd->max_min_size.h*(idx%pd->current_display_table.columns);
geom.x -= (relevant_space_size);
}
return geom;
}
#include "efl_ui_grid_position_manager.eo.c"

View File

@ -0,0 +1,17 @@
class @beta Efl.Ui.Grid_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager
{
[[Implementation of @Efl.Ui.Item_Position_Manager for two-dimensional grids.
Every item in the grid has the same size, which is the biggest minimum size of all items.
]]
implements {
Efl.Ui.Item_Position_Manager.data_access {set;}
Efl.Ui.Item_Position_Manager.viewport {set;}
Efl.Ui.Item_Position_Manager.scroll_position {set;}
Efl.Ui.Item_Position_Manager.item_added;
Efl.Ui.Item_Position_Manager.item_removed;
Efl.Ui.Item_Position_Manager.position_single_item;
Efl.Ui.Item_Position_Manager.item_size_changed;
Efl.Ui.Layout_Orientable.orientation {set; get;}
}
}

View File

@ -0,0 +1,857 @@
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#endif
#define ELM_LAYOUT_PROTECTED
#define EFL_UI_SCROLL_MANAGER_PROTECTED
#define EFL_UI_SCROLLBAR_PROTECTED
#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
#include <Efl_Ui.h>
#include <Elementary.h>
#include "elm_widget.h"
#include "elm_priv.h"
#define MY_CLASS EFL_UI_ITEM_CONTAINER_CLASS
#define MY_DATA_GET(obj, pd) \
Efl_Ui_Item_Container_Data *pd = efl_data_scope_get(obj, MY_CLASS);
typedef struct {
Efl_Ui_Scroll_Manager *smanager;
Efl_Ui_Pan *pan;
Eina_List *selected;
Eina_List *items;
Efl_Ui_Select_Mode mode;
Efl_Ui_Layout_Orientation dir;
Eina_Size2D content_min_size;
Efl_Ui_Item_Position_Manager *pos_man;
struct {
Eina_Accessor pass_on;
unsigned int last_index;
const Eina_List *current;
} obj_accessor;
struct {
Eina_Bool w;
Eina_Bool h;
} match_content;
Eina_Accessor size_accessor;
Efl_Gfx_Entity *sizer;
} Efl_Ui_Item_Container_Data;
static Eina_Bool register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item);
static Eina_Bool unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item);
static void
flush_min_size(Eo *obj, Efl_Ui_Item_Container_Data *pd)
{
Eina_Size2D tmp = pd->content_min_size;
if (!pd->match_content.w)
tmp.w = -1;
if (!pd->match_content.h)
tmp.h = -1;
efl_gfx_hint_size_min_set(obj, tmp);
}
static int
clamp_index(Efl_Ui_Item_Container_Data *pd, int index)
{
if (index < ((int)eina_list_count(pd->items)) * -1)
return -1;
else if (index > (int)eina_list_count(pd->items) - 1)
return 1;
return 0;
}
static int
index_adjust(Efl_Ui_Item_Container_Data *pd, int index)
{
int c = eina_list_count(pd->items);
if (index < c * -1)
return 0;
else if (index > c - 1)
return c - 1;
else if (index < 0)
return index + c;
return index;
}
static Eina_Bool
_obj_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data)
{
ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor);
Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
const Eina_List *over;
unsigned int middle;
unsigned int i;
if (idx >= eina_list_count(pd->items))
return EINA_FALSE;
if (pd->obj_accessor.last_index == idx)
over = pd->obj_accessor.current;
else if (idx > pd->obj_accessor.last_index)
{
/* After current position. */
middle = ((eina_list_count(pd->items) - pd->obj_accessor.last_index))/2;
if (idx > middle)
/* Go backward from the end. */
for (i = eina_list_count(pd->items) - 1,
over = eina_list_last(pd->items);
i > idx && over;
--i, over = eina_list_prev(over))
;
else
/* Go forward from current. */
for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current;
i < idx && over;
++i, over = eina_list_next(over))
;
}
else
{
/* Before current position. */
middle = pd->obj_accessor.last_index/2;
if (idx > middle)
/* Go backward from current. */
for (i = pd->obj_accessor.last_index, over = pd->obj_accessor.current;
i > idx && over;
--i, over = eina_list_prev(over))
;
else
/* Go forward from start. */
for (i = 0, over = pd->items;
i < idx && over;
++i, over = eina_list_next(over))
;
}
if (!over)
return EINA_FALSE;
pd->obj_accessor.last_index = idx;
pd->obj_accessor.current = over;
*data = eina_list_data_get(over);
return EINA_TRUE;
}
static Eina_Accessor*
_obj_clone(Eina_Accessor *accessor)
{
ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, obj_accessor);
Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
return eina_list_accessor_new(pd->items);
}
static Eina_List *
_null_container(Eina_Accessor *accessor EINA_UNUSED)
{
ERR("Not allowed to get a container!");
return NULL;
}
static void
_free(Eina_Accessor *accessor EINA_UNUSED)
{
ERR("Freeing this accessor is not supported");
}
static void
_obj_accessor_init(Eina_Accessor *accessor)
{
//this is the accessor for accessing the items
//we have to workarround here the problem that
//no accessor can be created for a not yet created list.
accessor->version = EINA_ACCESSOR_VERSION;
accessor->get_at = FUNC_ACCESSOR_GET_AT(_obj_accessor_get_at);
accessor->clone = FUNC_ACCESSOR_CLONE(_obj_clone);
accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container);
accessor->free = FUNC_ACCESSOR_FREE(_free);
EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR);
}
static Eina_Bool
_size_accessor_get_at(Eina_Accessor *accessor, unsigned int idx, void **data)
{
Eina_Size2D *size = (Eina_Size2D*)data;
ptrdiff_t offset = offsetof(Efl_Ui_Item_Container_Data, size_accessor);
Efl_Ui_Item_Container_Data *pd = (void*)accessor - offset;
if (idx > eina_list_count(pd->items))
return EINA_FALSE;
Eo *subobj = eina_list_nth(pd->items, idx);
*size = efl_gfx_hint_size_combined_min_get(subobj);
return EINA_TRUE;
}
static Eina_Accessor*
_size_clone(Eina_Accessor *accessor EINA_UNUSED)
{
return NULL;
}
static void
_size_accessor_init(Eina_Accessor *accessor)
{
accessor->version = EINA_ACCESSOR_VERSION;
accessor->get_at = FUNC_ACCESSOR_GET_AT(_size_accessor_get_at);
accessor->clone = FUNC_ACCESSOR_CLONE(_size_clone);
accessor->get_container = FUNC_ACCESSOR_GET_CONTAINER(_null_container);
accessor->free = FUNC_ACCESSOR_FREE(_free);
EINA_MAGIC_SET(accessor, EINA_MAGIC_ACCESSOR);
}
static void
_pan_viewport_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
MY_DATA_GET(data, pd);
Eina_Rect rect = efl_ui_scrollable_viewport_geometry_get(data);
efl_ui_item_position_manager_viewport_set(pd->pos_man, rect);
}
static void
_pan_position_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
MY_DATA_GET(data, pd);
Eina_Position2D pos = efl_ui_pan_position_get(pd->pan);
Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
Eina_Vector2 rpos = {0.0, 0.0};
if (max.x > 0.0)
rpos.x = (double)pos.x/(double)max.x;
if (max.y > 0.0)
rpos.y = (double)pos.y/(double)max.y;
efl_ui_item_position_manager_scroll_position_set(pd->pos_man, rpos.x, rpos.y);
}
EFL_CALLBACKS_ARRAY_DEFINE(pan_events_cb,
{EFL_UI_PAN_EVENT_PAN_POSITION_CHANGED, _pan_position_changed_cb},
{EFL_UI_PAN_EVENT_PAN_VIEWPORT_CHANGED, _pan_viewport_changed_cb},
)
static void
_item_scroll_internal(Eo *obj EINA_UNUSED,
Efl_Ui_Item_Container_Data *pd,
Efl_Ui_Item *item,
double align,
Eina_Bool anim)
{
Eina_Rect ipos, view;
Eina_Position2D vpos;
if (!pd->smanager) return;
ipos = efl_ui_item_position_manager_position_single_item(pd->pos_man, eina_list_data_idx(pd->items, item));
view = efl_ui_scrollable_viewport_geometry_get(pd->smanager);
vpos = efl_ui_scrollable_content_pos_get(pd->smanager);
ipos.x = ipos.x + vpos.x - view.x;
ipos.y = ipos.y + vpos.y - view.y;
//FIXME scrollable needs some sort of align, the docs do not even garantee to completly move in the element
efl_ui_scrollable_scroll(pd->smanager, ipos, anim);
}
EOLIAN static void
_efl_ui_item_container_item_scroll(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, Eina_Bool animation)
{
_item_scroll_internal(obj, pd, item, -1.0, animation);
}
EOLIAN static void
_efl_ui_item_container_item_scroll_align(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item, double align, Eina_Bool animation)
{
_item_scroll_internal(obj, pd, item, align, animation);
}
EOLIAN static Efl_Ui_Item*
_efl_ui_item_container_last_selected_item_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return eina_list_last_data_get(pd->selected);
}
EOLIAN static Eina_Iterator*
_efl_ui_item_container_selected_items_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return eina_list_iterator_new(pd->selected);
}
EOLIAN static Efl_Object*
_efl_ui_item_container_efl_object_constructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
{
Eo *o;
pd->dir = EFL_UI_LAYOUT_ORIENTATION_VERTICAL;
_obj_accessor_init(&pd->obj_accessor.pass_on);
_size_accessor_init(&pd->size_accessor);
if (!elm_widget_theme_klass_get(obj))
elm_widget_theme_klass_set(obj, "item_container");
o = efl_constructor(efl_super(obj, MY_CLASS));
pd->sizer = efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(obj));
efl_gfx_color_set(pd->sizer, 0, 0, 0, 0);
pd->pan = efl_add(EFL_UI_PAN_CLASS, obj);
efl_content_set(pd->pan, pd->sizer);
efl_event_callback_array_add(pd->pan, pan_events_cb(), obj);
pd->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj);
efl_composite_attach(obj, pd->smanager);
efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj));
efl_ui_scroll_manager_pan_set(pd->smanager, pd->pan);
efl_ui_scroll_connector_bind(obj, pd->smanager);
return o;
}
EOLIAN static Efl_Object*
_efl_ui_item_container_efl_object_finalize(Eo *obj, Efl_Ui_Item_Container_Data *pd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->pos_man, NULL);
return efl_finalize(efl_super(obj, MY_CLASS));
}
EOLIAN static Eina_Error
_efl_ui_item_container_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Item_Container_Data *pd)
{
Eina_Error res;
ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_ERROR_GENERIC);
res = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
if (res == EFL_UI_THEME_APPLY_ERROR_GENERIC) return res;
efl_ui_mirrored_set(pd->smanager, efl_ui_mirrored_get(obj));
efl_content_set(efl_part(wd->resize_obj, "efl.content"), pd->pan);
return res;
}
EOLIAN static void
_efl_ui_item_container_efl_object_destructor(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
{
efl_destructor(efl_super(obj, MY_CLASS));
}
static void
deselect_all(Efl_Ui_Item_Container_Data *pd)
{
while(pd->selected)
{
Eo *item = eina_list_data_get(pd->selected);
efl_ui_item_selected_set(item, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN(eina_list_data_get(pd->selected) == item);
}
}
EOLIAN static void
_efl_ui_item_container_efl_object_invalidate(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED)
{
efl_ui_item_container_position_manager_set(obj, NULL);
deselect_all(pd);
while(pd->items)
efl_del(pd->items->data);
efl_invalidate(efl_super(obj, MY_CLASS));
}
EOLIAN static Eina_Iterator*
_efl_ui_item_container_efl_container_content_iterate(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return eina_list_iterator_new(pd->items);
}
EOLIAN static int
_efl_ui_item_container_efl_container_content_count(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return eina_list_count(pd->items);
}
EOLIAN static void
_efl_ui_item_container_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Layout_Orientation dir)
{
if (pd->dir == dir) return;
pd->dir = dir;
if (pd->pos_man)
efl_ui_layout_orientation_set(pd->pos_man, dir);
}
EOLIAN static Efl_Ui_Layout_Orientation
_efl_ui_item_container_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return pd->dir;
}
EOLIAN static void
_efl_ui_item_container_efl_ui_scrollable_interactive_match_content_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Eina_Bool w, Eina_Bool h)
{
if (pd->match_content.w == w && pd->match_content.h == h)
return;
pd->match_content.w = w;
pd->match_content.h = h;
efl_ui_scrollable_match_content_set(pd->smanager, w, h);
flush_min_size(obj, pd);
}
EOLIAN static void
_efl_ui_item_container_efl_ui_multi_selectable_select_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Select_Mode mode)
{
pd->mode = mode;
if ((mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || mode == EFL_UI_SELECT_MODE_SINGLE) &&
eina_list_count(pd->selected) > 0)
{
Efl_Ui_Item *last = eina_list_last_data_get(pd->selected);
pd->selected = eina_list_remove_list(pd->selected, eina_list_last(pd->selected));
deselect_all(pd);
pd->selected = eina_list_append(pd->selected, last);
}
else if (mode == EFL_UI_SELECT_MODE_NONE && pd->selected)
{
deselect_all(pd);
}
}
EOLIAN static Efl_Ui_Select_Mode
_efl_ui_item_container_efl_ui_multi_selectable_select_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return pd->mode;
}
static void
_selected_cb(void *data, const Efl_Event *ev)
{
Eo *obj = data;
MY_DATA_GET(obj, pd);
if (pd->mode == EFL_UI_SELECT_MODE_SINGLE_ALWAYS || pd->mode == EFL_UI_SELECT_MODE_SINGLE)
{
//we might get the situation that the item is already in the list and selected again, so just free the list, it will be rebuild below
if (eina_list_data_get(pd->selected) == ev->object)
{
pd->selected = eina_list_free(pd->selected);
}
else
{
deselect_all(pd);
}
}
else if (pd->mode == EFL_UI_SELECT_MODE_NONE)
{
ERR("Selection while mode is NONE, uncaught state!");
return;
}
pd->selected = eina_list_append(pd->selected, ev->object);
efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_SELECTED, ev->object);
}
static void
_unselected_cb(void *data, const Efl_Event *ev)
{
Eo *obj = data;
MY_DATA_GET(obj, pd);
pd->selected = eina_list_remove(pd->selected, ev->object);
efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_UNSELECTED, ev->object);
}
static void
_invalidate_cb(void *data, const Efl_Event *ev)
{
Eo *obj = data;
MY_DATA_GET(obj, pd);
unregister_item(obj, pd, ev->object);
}
static void
_hints_changed_cb(void *data, const Efl_Event *ev)
{
Eo *obj = data;
MY_DATA_GET(obj, pd);
int idx = eina_list_data_idx(pd->items, ev->object);
efl_ui_item_position_manager_item_size_changed(pd->pos_man, idx, idx);
}
static void
_redirect_cb(void *data, const Efl_Event *ev)
{
Eo *obj = data;
#define REDIRECT_EVT(item_evt, item) \
if (item_evt == ev->desc) efl_event_callback_call(obj, item, ev->object);
REDIRECT_EVT(EFL_UI_EVENT_PRESSED, EFL_UI_EVENT_ITEM_PRESSED);
REDIRECT_EVT(EFL_UI_EVENT_UNPRESSED, EFL_UI_EVENT_ITEM_UNPRESSED);
REDIRECT_EVT(EFL_UI_EVENT_LONGPRESSED, EFL_UI_EVENT_ITEM_LONGPRESSED);
REDIRECT_EVT(EFL_UI_EVENT_CLICKED_ANY, EFL_UI_EVENT_ITEM_CLICKED_ANY);
REDIRECT_EVT(EFL_UI_EVENT_CLICKED, EFL_UI_EVENT_ITEM_CLICKED);
#undef REDIRECT_EVT
}
EFL_CALLBACKS_ARRAY_DEFINE(active_item,
{EFL_GFX_ENTITY_EVENT_HINTS_CHANGED, _hints_changed_cb},
{EFL_UI_EVENT_ITEM_SELECTED, _selected_cb},
{EFL_UI_EVENT_ITEM_UNSELECTED, _unselected_cb},
{EFL_UI_EVENT_PRESSED, _redirect_cb},
{EFL_UI_EVENT_UNPRESSED, _redirect_cb},
{EFL_UI_EVENT_LONGPRESSED, _redirect_cb},
{EFL_UI_EVENT_CLICKED, _redirect_cb},
{EFL_UI_EVENT_CLICKED_ANY, _redirect_cb},
{EFL_EVENT_INVALIDATE, _invalidate_cb},
)
static Eina_Bool
register_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item)
{
EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(item, EFL_UI_ITEM_CLASS), EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_list_data_find(pd->items, item), EINA_FALSE);
if (!efl_ui_widget_sub_object_add(obj, item))
return EINA_FALSE;
efl_ui_item_container_set(item, obj);
efl_canvas_group_member_add(pd->pan, item);
efl_event_callback_array_add(item, active_item(), obj);
efl_ui_mirrored_set(item, efl_ui_mirrored_get(obj));
return EINA_TRUE;
}
static Eina_Bool
unregister_item(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item *item)
{
Eina_List *elem = eina_list_data_find_list(pd->items, item);
if (!elem)
{
ERR("Item %p is not part of this widget", item);
return EINA_FALSE;
}
if (!efl_ui_widget_sub_object_del(obj, item))
return EINA_FALSE;
unsigned int id = eina_list_data_idx(pd->items, item);
if (pd->obj_accessor.last_index == id)
{
if (eina_list_next(elem))
{
pd->obj_accessor.current = eina_list_next(elem);
}
else if (eina_list_prev(elem))
{
pd->obj_accessor.last_index = id-1;
pd->obj_accessor.current = eina_list_prev(elem);
}
else
{
//everything >= length is invalid, and we need that.
pd->obj_accessor.last_index = eina_list_count(pd->items);
pd->obj_accessor.current = NULL;
}
}
pd->items = eina_list_remove(pd->items, item);
pd->selected = eina_list_remove(pd->selected, item);
efl_event_callback_array_del(item, active_item(), obj);
efl_ui_item_position_manager_item_removed(pd->pos_man, id, item);
return EINA_TRUE;
}
static void
update_pos_man(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
{
int id = eina_list_data_idx(pd->items, subobj);
if (id == 0)
{
pd->obj_accessor.last_index = id;
pd->obj_accessor.current = pd->items;
}
efl_ui_item_position_manager_item_added(pd->pos_man, id, subobj);
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_pack_clear(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
while(pd->items)
{
efl_del(pd->items->data);
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_unpack_all(Eo *obj, Efl_Ui_Item_Container_Data *pd)
{
while(pd->items)
{
if (!unregister_item(obj, pd, pd->items->data))
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Efl_Gfx_Entity*
_efl_ui_item_container_efl_pack_linear_pack_unpack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, int index)
{
Efl_Ui_Item *it = eina_list_nth(pd->items, index_adjust(pd, index));
EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL);
if (!unregister_item(obj, pd, it))
return NULL;
return it;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_unpack(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
{
return unregister_item(obj, pd, subobj);
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_pack(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Gfx_Entity *subobj)
{
return efl_pack_end(obj, subobj);
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_linear_pack_end(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
{
if (!register_item(obj, pd, subobj))
return EINA_FALSE;
pd->items = eina_list_append(pd->items, subobj);
update_pos_man(obj, pd, subobj);
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_linear_pack_begin(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj)
{
if (!register_item(obj, pd, subobj))
return EINA_FALSE;
pd->items = eina_list_prepend(pd->items, subobj);
update_pos_man(obj, pd, subobj);
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_linear_pack_before(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing)
{
Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing);
EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
if (!register_item(obj, pd, subobj))
return EINA_FALSE;
pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list);
update_pos_man(obj, pd, subobj);
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_linear_pack_after(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing)
{
Eina_List *subobj_list = eina_list_data_find_list(pd->items, existing);
EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
if (!register_item(obj, pd, subobj))
return EINA_FALSE;
pd->items = eina_list_append_relative_list(pd->items, subobj, subobj_list);
update_pos_man(obj, pd, subobj);
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_pack_linear_pack_at(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Gfx_Entity *subobj, int index)
{
Eina_List *subobj_list;
int clamp;
clamp = clamp_index(pd, index);
index = index_adjust(pd, index);
subobj_list = eina_list_nth_list(pd->items, index);
EINA_SAFETY_ON_NULL_RETURN_VAL(subobj_list, EINA_FALSE);
if (!register_item(obj, pd, subobj))
return EINA_FALSE;
if (clamp == 0)
pd->items = eina_list_prepend_relative_list(pd->items, subobj, subobj_list);
else if (clamp == 1)
pd->items = eina_list_append(pd->items, subobj);
else
pd->items = eina_list_prepend(pd->items, subobj);
update_pos_man(obj, pd, subobj);
return EINA_TRUE;
}
EOLIAN static int
_efl_ui_item_container_efl_pack_linear_pack_index_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, const Efl_Gfx_Entity *subobj)
{
return eina_list_data_idx(pd->items, (void*)subobj);
}
EOLIAN static Efl_Gfx_Entity*
_efl_ui_item_container_efl_pack_linear_pack_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd, int index)
{
return eina_list_nth(pd->items, index_adjust(pd, index));
}
static void
_pos_content_size_changed_cb(void *data, const Efl_Event *ev)
{
Eina_Size2D *size = ev->info;
MY_DATA_GET(data, pd);
efl_gfx_entity_size_set(pd->sizer, *size);
}
static void
_pos_content_min_size_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
{
Eina_Size2D *size = ev->info;
MY_DATA_GET(data, pd);
pd->content_min_size = *size;
flush_min_size(data, pd);
}
EFL_CALLBACKS_ARRAY_DEFINE(pos_manager_cbs,
{EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _pos_content_size_changed_cb},
{EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _pos_content_min_size_changed_cb},
)
EOLIAN static void
_efl_ui_item_container_position_manager_set(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Item_Position_Manager *layouter)
{
if (layouter)
EINA_SAFETY_ON_FALSE_RETURN(efl_isa(layouter, EFL_UI_ITEM_POSITION_MANAGER_INTERFACE));
if (pd->pos_man)
{
efl_event_callback_array_del(pd->pos_man, pos_manager_cbs(), obj);
efl_ui_item_position_manager_data_access_set(pd->pos_man, NULL, NULL, 0);
efl_del(pd->pos_man);
}
pd->pos_man = layouter;
if (pd->pos_man)
{
efl_parent_set(pd->pos_man, obj);
efl_event_callback_array_add(pd->pos_man, pos_manager_cbs(), obj);
efl_ui_item_position_manager_data_access_set(pd->pos_man, &pd->obj_accessor.pass_on, &pd->size_accessor, eina_list_count(pd->items));
efl_ui_item_position_manager_viewport_set(pd->pos_man, efl_ui_scrollable_viewport_geometry_get(obj));
efl_ui_layout_orientation_set(pd->pos_man, pd->dir);
}
}
EOLIAN static Efl_Ui_Item_Position_Manager*
_efl_ui_item_container_position_manager_get(const Eo *obj EINA_UNUSED, Efl_Ui_Item_Container_Data *pd)
{
return pd->pos_man;
}
EOLIAN static Efl_Ui_Focus_Manager*
_efl_ui_item_container_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
{
return efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj,
efl_ui_focus_manager_root_set(efl_added, root));
}
EOLIAN static Eina_Bool
_efl_ui_item_container_efl_ui_widget_focus_state_apply(Eo *obj, Efl_Ui_Item_Container_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect EINA_UNUSED)
{
return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
}
EOLIAN static Efl_Ui_Focus_Object*
_efl_ui_item_container_efl_ui_focus_manager_move(Eo *obj, Efl_Ui_Item_Container_Data *pd, Efl_Ui_Focus_Direction direction)
{
Eo *new_obj, *focus;
Eina_Size2D step;
new_obj = efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
focus = efl_ui_focus_manager_focus_get(obj);
step = efl_gfx_hint_size_min_get(focus);
if (!new_obj)
{
Eina_Rect pos = efl_gfx_entity_geometry_get(focus);
Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(pd->smanager);
Eina_Position2D vpos = efl_ui_scrollable_content_pos_get(pd->smanager);
pos.x = pos.x + vpos.x - view.x;
pos.y = pos.y + vpos.y - view.y;
Eina_Position2D max = efl_ui_pan_position_max_get(pd->pan);
if (direction == EFL_UI_FOCUS_DIRECTION_RIGHT)
{
if (pos.x < max.x)
{
pos.x = MIN(max.x, pos.x + step.w);
efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
new_obj = focus;
}
}
else if (direction == EFL_UI_FOCUS_DIRECTION_LEFT)
{
if (pos.x > 0)
{
pos.x = MAX(0, pos.x - step.w);
efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
new_obj = focus;
}
}
else if (direction == EFL_UI_FOCUS_DIRECTION_UP)
{
if (pos.y > 0)
{
pos.y = MAX(0, pos.y - step.h);
efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
new_obj = focus;
}
}
else if (direction == EFL_UI_FOCUS_DIRECTION_DOWN)
{
if (pos.y < max.y)
{
pos.y = MAX(0, pos.y + step.h);
efl_ui_scrollable_scroll(obj, pos, EINA_TRUE);
new_obj = focus;
}
}
}
else
{
_item_scroll_internal(obj, pd, new_obj, .0, EINA_TRUE);
}
return new_obj;
}
#include "efl_ui_item_container.eo.c"

View File

@ -0,0 +1,94 @@
class @beta Efl.Ui.Item_Container extends Efl.Ui.Layout_Base implements
Efl.Ui.Scrollable_Interactive,
Efl.Ui.Scrollbar,
Efl.Pack_Linear, Efl.Pack_Layout,
Efl.Ui.Layout_Orientable,
Efl.Ui.Selectable,
Efl.Ui.Multi_Selectable,
Efl.Ui.Focus.Manager_Sub,
Efl.Ui.Widget_Focus_Manager
{
[[This widget displays a list of items in an arrangement controlled by an external $position_manager object. By using different $position_manager objects this widget can show lists of items or two-dimensional grids of items, for example.
Items inside this widget can be selected according to the $select_mode policy, and retrieved with $selected_items_get.
]]
event_prefix:efl_ui;
methods {
item_scroll {
[[Bring the passed item into the viewport.]]
params {
@in item: Efl.Ui.Item; [[The target to move in.]]
@in animation: bool; [[If you want to have an animation or not.]]
}
}
item_scroll_align {
[[Bring the passed item into the viewport, place the item accordingly to align in the viewport.
$align selects the final position of the object inside the viewport. 0.0 will move the object to the first visible position inside the viewport, 1.0 will move it to the last visible position, and values in between will move it accordingly to positions in between, along the scrolling axis.
]]
params {
@in item: Efl.Ui.Item; [[The target to move in.]]
@in align: double; [[0.0 to have this item at the upper or left side of the viewport, 1.0 to have this item at the lower or right side of the viewport]]
@in animation: bool; [[If you want to have an animation or not.]]
}
}
@property last_selected_item {
[[The item that was selected most recently.
]]
get {}
values {
item: Efl.Ui.Item; [[The latest selected item.]]
}
}
selected_items_get {
[[Get the selected items iterator. The iterator sequence will be decided by selection.]]
return: iterator<Efl.Ui.Item> @owned @no_unused; [[User has to free the iterator after usage.]]
}
@property position_manager {
[[Position manager object that handles placement of items.]]
values {
position_manager : Efl.Ui.Item_Position_Manager @owned; [[The objects ownership is passed to the item container.]]
}
}
}
implements {
Efl.Object.constructor;
Efl.Object.finalize;
Efl.Object.destructor;
Efl.Object.invalidate;
Efl.Container.content_iterate;
Efl.Container.content_count;
Efl.Ui.Layout_Orientable.orientation { get; set; }
Efl.Ui.Widget.theme_apply;
Efl.Pack.pack_clear;
Efl.Pack.unpack_all;
Efl.Pack.unpack;
Efl.Pack.pack;
Efl.Pack_Linear.pack_end;
Efl.Pack_Linear.pack_begin;
Efl.Pack_Linear.pack_before;
Efl.Pack_Linear.pack_after;
Efl.Pack_Linear.pack_at;
Efl.Pack_Linear.pack_unpack_at;
Efl.Pack_Linear.pack_index_get;
Efl.Pack_Linear.pack_content_get;
Efl.Ui.Scrollable_Interactive.match_content { set; }
Efl.Ui.Multi_Selectable.select_mode {get; set;}
Efl.Ui.Widget_Focus_Manager.focus_manager_create;
Efl.Ui.Widget.focus_state_apply;
Efl.Ui.Focus.Manager.move;
}
events {
item,pressed : Efl.Ui.Item; [[A $press event occurred over an item.]]
item,unpressed : Efl.Ui.Item; [[A $unpress event occurred over an item.]]
item,longpressed : Efl.Ui.Item; [[A $longpressed event occurred over an item.]]
item,clicked : Efl.Ui.Item; [[A $clicked event occurred over an item.]]
item,clicked,any : Efl.Ui.Item; [[A $clicked,any event occurred over an item.]]
}
composite {
Efl.Ui.Scrollable_Interactive;
Efl.Ui.Scrollbar;
Efl.Ui.Focus.Manager;
}
}

View File

@ -0,0 +1,14 @@
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#endif
#define ELM_LAYOUT_PROTECTED
#define EFL_UI_SCROLL_MANAGER_PROTECTED
#define EFL_UI_SCROLLBAR_PROTECTED
#include <Efl_Ui.h>
#include "elm_priv.h"
#define MY_CLASS EFL_UI_ITEM_POSITION_MANAGER_CLASS
#include "efl_ui_item_position_manager.eo.c"

View File

@ -0,0 +1,96 @@
interface @beta Efl.Ui.Item_Position_Manager extends Efl.Ui.Layout_Orientable
{
[[
This abstracts the basic placement of items in a not defined form under a viewport.
The interface gets a defined set of elements that is meant to be displayed. The implementation provides a way to calculate the size that is required to display all items. Every time this absolut size of items is changed, content_size,changed is called.
]]
methods {
@property data_access {
[[This gives access to items to be managed. The manager reads this information and modifies the retrieved items' positions and sizes.
$obj_access gives access to the graphical entitites to manage. Some of them might be NULL, meaning they are not yet ready to be displayed. Their size in the $size_access array will be correct, though, so other entities can still be positioned correctly.
Typically, only entities inside the viewport will be retrieved.
$size_access gives access to the 2D sizes for the items to manage. All sizes will always be valid, and might change over time (indicated through the @.item_size_changed method).
The whole range might need to be traversed in order to calculate the position of all items in some arrangements.
]]
set {
}
values {
obj_access : accessor<Efl.Gfx.Entity>; [[The accessor for canvas obejcts, even if the id is valid, the returned object may be NULL]]
size_access : accessor<Eina.Size2D>; [[Accessor for the size, returned values are always valid, but might be changed / updated]]
size : int; [[valid size for accessors, 0 <= i < size]]
}
}
@property viewport {
[[This is the position and size of the viewport, where elements are displayed in.
Entities outside this viewport will not be shown.]]
set {
}
values {
viewport : Eina.Rect;
}
}
@property scroll_position {
[[Move the items relative to the viewport.
The items that are managed with this position manager might be bigger than the actual viewport.
The positioning of the layer where all items are on is described by these values.
0.0,0.0 means that layer is moved that the top left items are shown,
1.0,1.0 means, that the lower right items are shown.
]]
set {
}
values {
x : double; [[X position of the scroller, valid form 0 to 1.0]]
y : double; [[Y position of the scroller, valid form 0 to 1.0]]
}
}
position_single_item {
[[Return the position and size of item idx
This method returns the size and position of the item at $idx.
Even if the item is outside the viewport, the returned rectangle must be valid. The result can be used for scrolling calculations.
]]
params {
idx : int; [[The id for the item]]
}
return : Eina.Rect; [[Position and Size in canvas coordinations]]
}
item_added {
[[The new item $subobj has been added at the $added_index field.
The accessor provided through @.data_access will contain updated Entities.]]
params {
added_index : int;
subobj : Efl.Gfx.Entity;
}
}
item_removed {
[[The item $subobj previously at position $removed_index has been removed.
The accessor provided through @.data_access will contain updated Entities.
]]
params {
removed_index : int;
subobj : Efl.Gfx.Entity;
}
}
item_size_changed {
[[The size of the items from $start_id to $end_id have been changed.
The positioning and sizing of all items will be updated]]
params {
start_id : int; [[The first item that has a new size]]
end_id : int; [[The last item that has a new size]]
}
}
}
events {
content_size,changed : Eina.Size2D; [[Emitted when the aggregate size of all items has changed. This can be used to resize an enclosing Pan object.]]
content_min_size,changed : Eina.Size2D; [[Emitted when the minimum size of all items has changed. The minimum size is the size, that this position_manager needs at *least* to display a single item.]]
}
}

View File

@ -0,0 +1,389 @@
#ifdef HAVE_CONFIG_H
#include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include <Elementary.h>
#include "elm_widget.h"
#include "elm_priv.h"
#define MY_CLASS EFL_UI_LIST_POSITION_MANAGER_CLASS
#define MY_DATA_GET(obj, pd) \
Efl_Ui_List_Position_Manager_Data *pd = efl_data_scope_get(obj, MY_CLASS);
typedef struct {
Eina_Accessor *content_acc, *size_acc;
unsigned int size;
Eina_Future *rebuild_absolut_size;
Eina_Rect viewport;
Eina_Size2D abs_size;
Eina_Vector2 scroll_position;
Efl_Ui_Layout_Orientation dir;
int *size_cache;
int average_item_size;
int maximum_min_size;
struct {
unsigned int start_id, end_id;
} prev_run;
} Efl_Ui_List_Position_Manager_Data;
/*
* The here used cache is a sum map
* Every element in the cache contains the sum of the previous element, and the size of the current item
* This is usefull as a lookup of all previous items is O(1).
* The tradeoff that makes the cache performant here is, that we only need to walk the whole list of items once in the beginning.
* Every other walk of the items is at max the maximum number of items you get into the maximum distance between the average item size and a actaul item size.
*/
static void
cache_require(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
{
unsigned int i;
if (pd->size_cache) return;
if (pd->size == 0)
{
pd->size_cache = NULL;
pd->average_item_size = 0;
return;
}
pd->size_cache = calloc(pd->size + 1, sizeof(int));
pd->size_cache[0] = 0;
pd->maximum_min_size = 0;
for (i = 0; i < pd->size; ++i)
{
Eina_Size2D size;
int step;
int min;
eina_accessor_data_get(pd->size_acc, i, (void**) &size);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
step = size.h;
min = size.w;
}
else
{
step = size.w;
min = size.h;
}
pd->size_cache[i + 1] = pd->size_cache[i] + step;
pd->maximum_min_size = MAX(pd->maximum_min_size, min);
}
pd->average_item_size = pd->size_cache[pd->size]/pd->size;
}
static void
cache_invalidate(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
{
if (pd->size_cache)
free(pd->size_cache);
pd->size_cache = NULL;
}
static inline int
cache_access(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, unsigned int idx)
{
EINA_SAFETY_ON_FALSE_RETURN_VAL(idx <= pd->size, 0);
return pd->size_cache[idx];
}
static void
recalc_absolut_size(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
{
Eina_Size2D min_size = EINA_SIZE2D(-1, -1);
cache_require(obj, pd);
pd->abs_size = pd->viewport.size;
if (pd->size)
{
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
pd->abs_size.h = MAX(cache_access(obj, pd, pd->size), pd->abs_size.h);
else
pd->abs_size.w = MAX(cache_access(obj, pd, pd->size), pd->abs_size.w);
}
efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, &pd->abs_size);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
min_size.w = pd->maximum_min_size;
}
else
{
min_size.h = pd->maximum_min_size;
}
efl_event_callback_call(obj, EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
}
static inline void
vis_change_segment(Efl_Ui_List_Position_Manager_Data *pd, int a, int b, Eina_Bool flag)
{
for (int i = MIN(a, b); i < MAX(a, b); ++i)
{
Efl_Gfx_Entity *ent = NULL;
eina_accessor_data_get(pd->content_acc, i, (void**) &ent);
if (ent && !efl_ui_focus_object_focus_get(ent))
{
efl_gfx_entity_visible_set(ent, flag);
}
}
}
static void
position_content(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
{
Eina_Rect geom;
Eina_Size2D space_size;
unsigned int start_id = 0, end_id = 0, i;
int relevant_space_size, relevant_viewport;
if (!pd->size) return;
if (pd->average_item_size <= 0) return;
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
relevant_space_size = space_size.h;
relevant_viewport = pd->viewport.h;
}
else
{
relevant_space_size = space_size.w;
relevant_viewport = pd->viewport.w;
}
//based on the average item size, we jump somewhere into the sum cache.
//After beeing in there, we are walking back, until we have less space then viewport size
start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size);
for (; cache_access(obj, pd, start_id) >= relevant_space_size && start_id > 0; start_id --) { }
//starting on the start id, we are walking down until the sum of elements is bigger than the lower part of the viewport.
end_id = start_id;
for (; end_id <= pd->size && cache_access(obj, pd, end_id) <= relevant_space_size + relevant_viewport ; end_id ++) { }
end_id = MAX(end_id, start_id + 1);
end_id = MIN(end_id, pd->size);
#ifdef DEBUG
printf("space_size %d : starting point : %d : cached_space_starting_point %d end point : %d cache_space_end_point %d\n", space_size.h, start_id, pd->size_cache[start_id], end_id, pd->size_cache[end_id]);
#endif
if (relevant_space_size > 0)
EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, start_id) <= relevant_space_size);
if (end_id != pd->size)
EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, end_id) >= relevant_space_size + relevant_viewport);
EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
//to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
//The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
if (end_id <= pd->prev_run.start_id || start_id >= pd->prev_run.end_id)
{
//it is important to first make the segment visible here, and then hide the rest
//otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
vis_change_segment(pd, start_id, end_id, EINA_TRUE);
vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
}
else
{
vis_change_segment(pd, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
vis_change_segment(pd, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
}
geom = pd->viewport;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
geom.y -= (relevant_space_size - cache_access(obj, pd, start_id));
else
geom.x -= (relevant_space_size - cache_access(obj, pd, start_id));
for (i = start_id; i < end_id; ++i)
{
Eina_Size2D size;
Efl_Gfx_Entity *ent = NULL;
EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->size_acc, i, (void**) &size));
EINA_SAFETY_ON_FALSE_RETURN(eina_accessor_data_get(pd->content_acc, i, (void**) &ent));
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
geom.h = size.h;
else
geom.w = size.w;
if (ent)
efl_gfx_entity_geometry_set(ent, geom);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
geom.y += size.h;
else
geom.x += size.w;
}
pd->prev_run.start_id = start_id;
pd->prev_run.end_id = end_id;
}
static Eina_Value
_rebuild_job_cb(void *data, Eina_Value v EINA_UNUSED, const Eina_Future *f EINA_UNUSED)
{
MY_DATA_GET(data, pd);
if (!efl_alive_get(data)) return EINA_VALUE_EMPTY;
cache_require(data, pd);
recalc_absolut_size(data, pd);
position_content(data, pd);
pd->rebuild_absolut_size = NULL;
return EINA_VALUE_EMPTY;
}
static void
schedule_recalc_absolut_size(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
{
if (pd->rebuild_absolut_size) return;
pd->rebuild_absolut_size = efl_loop_job(efl_app_main_get());
eina_future_then(pd->rebuild_absolut_size, _rebuild_job_cb, obj);
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_data_access_set(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, Eina_Accessor *content_access, Eina_Accessor *size_access, int size)
{
cache_invalidate(obj, pd);
pd->content_acc = content_access;
pd->size_acc = size_access;
pd->size = size;
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_viewport_set(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, Eina_Rect size)
{
pd->viewport = size;
recalc_absolut_size(obj, pd);
position_content(obj, pd);
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_scroll_position_set(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, double x, double y)
{
pd->scroll_position.x = x;
pd->scroll_position.y = y;
position_content(obj, pd);
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_item_added(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, int added_index EINA_UNUSED, Efl_Gfx_Entity *subobj)
{
if (pd->size == 0)
{
pd->prev_run.start_id = 0;
pd->prev_run.end_id = 0;
}
pd->size ++;
if (subobj)
{
efl_gfx_entity_visible_set(subobj, EINA_FALSE);
}
cache_invalidate(obj, pd);
schedule_recalc_absolut_size(obj, pd);
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_item_removed(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, int removed_index EINA_UNUSED, Efl_Gfx_Entity *subobj)
{
pd->size --;
if (subobj)
{
efl_gfx_entity_visible_set(subobj, EINA_TRUE);
}
cache_invalidate(obj, pd);
schedule_recalc_absolut_size(obj, pd);
}
EOLIAN static Eina_Rect
_efl_ui_list_position_manager_efl_ui_item_position_manager_position_single_item(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, int idx)
{
Eina_Rect geom;
Eina_Size2D space_size;
int relevant_space_size;
Eina_Size2D size;
if (!pd->size) return EINA_RECT(0,0,0,0);
//space size contains the amount of space that is outside the viewport (either to the top or to the left)
space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
EINA_SAFETY_ON_FALSE_RETURN_VAL(space_size.w >= 0 && space_size.h >= 0, EINA_RECT(0, 0, 0, 0));
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
relevant_space_size = space_size.h;
}
else
{
relevant_space_size = space_size.w;
}
geom = pd->viewport;
eina_accessor_data_get(pd->size_acc, idx, (void**)&size);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
{
geom.y -= (relevant_space_size - cache_access(obj, pd, idx));
geom.h = size.h;
}
else
{
geom.x -= (relevant_space_size - cache_access(obj, pd, idx));
geom.w = size.w;
}
return geom;
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_item_position_manager_item_size_changed(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd, int start_id EINA_UNUSED, int end_id EINA_UNUSED)
{
cache_invalidate(obj, pd);
schedule_recalc_absolut_size(obj, pd);
}
EOLIAN static void
_efl_ui_list_position_manager_efl_ui_layout_orientable_orientation_set(Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd, Efl_Ui_Layout_Orientation dir)
{
pd->dir = dir;
//in order to reset the state of the visible items, just hide everything and set the old segment accordingly
vis_change_segment(pd, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
pd->prev_run.start_id = 0;
pd->prev_run.end_id = 0;
cache_invalidate(obj, pd);
cache_require(obj,pd);
recalc_absolut_size(obj, pd);
position_content(obj, pd);
}
EOLIAN static Efl_Ui_Layout_Orientation
_efl_ui_list_position_manager_efl_ui_layout_orientable_orientation_get(const Eo *obj EINA_UNUSED, Efl_Ui_List_Position_Manager_Data *pd)
{
return pd->dir;
}
EOLIAN static void
_efl_ui_list_position_manager_efl_object_destructor(Eo *obj, Efl_Ui_List_Position_Manager_Data *pd)
{
if (pd->rebuild_absolut_size)
eina_future_cancel(pd->rebuild_absolut_size);
efl_destructor(efl_super(obj, MY_CLASS));
}
#include "efl_ui_list_position_manager.eo.c"

View File

@ -0,0 +1,18 @@
class @beta Efl.Ui.List_Position_Manager extends Efl.Object implements Efl.Ui.Item_Position_Manager
{
[[Implementation of Efl.Ui.Item_Position_manager for a list
Every item in the list will get at least his minsize applied, changes to the misize are listend to and change the layouting of all items. This supports the vertical and horizontal orientation.
]]
implements {
Efl.Object.destructor;
Efl.Ui.Item_Position_Manager.data_access {set;}
Efl.Ui.Item_Position_Manager.viewport {set;}
Efl.Ui.Item_Position_Manager.scroll_position {set;}
Efl.Ui.Item_Position_Manager.item_added;
Efl.Ui.Item_Position_Manager.item_removed;
Efl.Ui.Item_Position_Manager.position_single_item;
Efl.Ui.Item_Position_Manager.item_size_changed;
Efl.Ui.Layout_Orientable.orientation {set; get;}
}
}

View File

@ -181,6 +181,10 @@ pub_eo_files = [
'efl_ui_clickable.eo',
'efl_ui_clickable_util.eo',
'efl_ui_format.eo',
'efl_ui_item_container.eo',
'efl_ui_item_position_manager.eo',
'efl_ui_list_position_manager.eo',
'efl_ui_grid_position_manager.eo',
]
foreach eo_file : pub_eo_files
@ -938,6 +942,10 @@ elementary_src = [
'efl_ui_clickable_util.c',
'efl_ui_format.c',
'efl_ui_scroll_util.c',
'efl_ui_item_container.c',
'efl_ui_item_position_manager.c',
'efl_ui_list_position_manager.c',
'efl_ui_grid_position_manager.c',
]
elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl]

View File

@ -32,6 +32,9 @@ static const Efl_Test_Case etc[] = {
{ "efl_ui_win", efl_ui_test_win },
{ "efl_ui_spin", efl_ui_test_spin },
{ "efl_ui_spin_button", efl_ui_test_spin_button },
{ "efl_ui_item_container", efl_ui_test_item_container },
{ "efl_ui_grid_container", efl_ui_test_grid_container },
{ "efl_ui_list_container", efl_ui_test_list_container },
{ NULL, NULL }
};

View File

@ -43,6 +43,9 @@ void efl_ui_test_slider(TCase *tc);
void efl_ui_test_win(TCase *tc);
void efl_ui_test_spin(TCase *tc);
void efl_ui_test_spin_button(TCase *tc);
void efl_ui_test_item_container(TCase *tc);
void efl_ui_test_list_container(TCase *tc);
void efl_ui_test_grid_container(TCase *tc);
void loop_timer_interval_set(Eo *obj, double in);

View File

@ -0,0 +1,36 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include "efl_ui_suite.h"
#include "efl_ui_test_item_container_common.h"
static Eo *win;
static void
item_container_setup()
{
Eo * list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
position_manager = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
win = win_add();
item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
efl_ui_item_container_position_manager_set(efl_added, list));
}
static void
item_container_teardown()
{
item_container = NULL;
position_manager = NULL;
win = NULL;
}
void efl_ui_test_grid_container(TCase *tc)
{
tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
efl_ui_test_item_container_common_add(tc);
efl_ui_test_position_manager_common_add(tc);
}

View File

@ -0,0 +1,54 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include "efl_ui_suite.h"
#include "efl_ui_test_item_container_common.h"
static Eo* win;
static void
item_container_setup()
{
win = win_add();
}
static void
item_container_teardown()
{
win = NULL;
}
EFL_START_TEST(finalizer_check)
{
Eo *grid = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
Eo *list = efl_new(EFL_UI_GRID_POSITION_MANAGER_CLASS);
Eo *random_obj = efl_add(EFL_UI_BUTTON_CLASS, win);
Eo *o;
EXPECT_ERROR_START;
ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win), NULL);
EXPECT_ERROR_END;
EXPECT_ERROR_START;
ck_assert_ptr_eq(efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, random_obj)), NULL);
EXPECT_ERROR_END;
o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, grid));
ck_assert_ptr_ne(o, NULL);
ck_assert_ptr_eq(efl_parent_get(grid), o);
efl_del(o);
o = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win, efl_ui_item_container_position_manager_set(efl_added, list));
ck_assert_ptr_ne(o, NULL);
ck_assert_ptr_eq(efl_parent_get(list), o);
efl_del(o);
}
EFL_END_TEST
void efl_ui_test_item_container(TCase *tc)
{
tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
tcase_add_test(tc, finalizer_check);
}

View File

@ -0,0 +1,233 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include "efl_ui_suite.h"
#include "efl_ui_test_item_container_common.h"
Eo *item_container;
void
fill_items(const Efl_Class *klass)
{
for (int i = 0; i < 3; ++i)
{
char buf[PATH_MAX];
Eo *it = efl_add(klass, item_container);
snprintf(buf, sizeof(buf), "%d - Test %d", i, i%13);
efl_text_set(it, buf);
efl_pack_end(item_container, it);
}
}
static void
_set_pointer_quit(void *data, const Efl_Event *ev)
{
Efl_Ui_Item **b = data;
ck_assert_ptr_eq(*b, NULL);
*b = ev->info;
}
static void
_iterator_to_array(Eina_Array **arr, Eina_Iterator *iter)
{
Efl_Ui_Widget *widget;
*arr = eina_array_new(10);
EINA_ITERATOR_FOREACH(iter, widget)
{
eina_array_push(*arr, widget);
}
eina_iterator_free(iter);
}
EFL_START_TEST(test_multi_select)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
Eina_Array *arr_selected;
efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_TRUE);
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
_iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
ck_assert_int_eq(eina_array_count(arr_selected), 2);
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 0));
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 1), efl_pack_content_get(item_container, 2));
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
}
EFL_END_TEST
EFL_START_TEST(test_multi_select_removal)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
Eina_Array *arr_selected;
efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_MULTI);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_FALSE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_FALSE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 2));
selected = NULL;
unselected = NULL;
ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL);
_iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
ck_assert_int_eq(eina_array_count(arr_selected), 0);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
}
EFL_END_TEST
EFL_START_TEST(test_single_select)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
Eina_Array *arr_selected;
efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
selected = NULL;
unselected = NULL;
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
_iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
ck_assert_int_eq(eina_array_count(arr_selected), 1);
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2));
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
}
EFL_END_TEST
EFL_START_TEST(test_single_select_always)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
Eina_Array *arr_selected;
efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_SINGLE_ALWAYS);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
ck_assert_ptr_eq(unselected, efl_pack_content_get(item_container, 0));
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(item_container, 2));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), efl_pack_content_get(item_container, 2));
_iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
ck_assert_int_eq(eina_array_count(arr_selected), 1);
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(item_container, 2));
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
}
EFL_END_TEST
EFL_START_TEST(test_none_select)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
Eina_Array *arr_selected;
efl_ui_select_mode_set(item_container, EFL_UI_SELECT_MODE_NONE);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
fill_items(EFL_UI_LIST_DEFAULT_ITEM_CLASS);
efl_ui_item_selected_set(efl_pack_content_get(item_container, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_ui_item_selected_set(efl_pack_content_get(item_container, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 0)), EINA_FALSE);
ck_assert_int_eq(efl_ui_item_selected_get(efl_pack_content_get(item_container, 2)), EINA_FALSE);
ck_assert_ptr_eq(efl_ui_item_container_last_selected_item_get(item_container), NULL);
_iterator_to_array(&arr_selected, efl_ui_item_container_selected_items_get(item_container));
ck_assert_int_eq(eina_array_count(arr_selected), 0);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(item_container, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
}
EFL_END_TEST
void efl_ui_test_item_container_common_add(TCase *tc)
{
tcase_add_test(tc, test_multi_select);
tcase_add_test(tc, test_multi_select_removal);
tcase_add_test(tc, test_single_select);
tcase_add_test(tc, test_none_select);
tcase_add_test(tc, test_single_select_always);
}

View File

@ -0,0 +1,11 @@
#ifndef EFL_UI_TEST_ITEM_CONTAINER_COMMON_H
#define EFL_UI_TEST_ITEM_CONTAINER_COMMON_H 1
extern Eo *item_container;
extern Eo *position_manager;
void fill_items(const Efl_Class *klass);
void efl_ui_test_item_container_common_add(TCase *tc);
void efl_ui_test_position_manager_common_add(TCase *tc);
#endif

View File

@ -0,0 +1,36 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include "efl_ui_suite.h"
#include "efl_ui_test_item_container_common.h"
static Eo *win;
static void
item_container_setup()
{
Eo * list = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
position_manager = efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS);
win = win_add();
item_container = efl_add(EFL_UI_ITEM_CONTAINER_CLASS, win,
efl_ui_item_container_position_manager_set(efl_added, list));
}
static void
item_container_teardown()
{
item_container = NULL;
position_manager = NULL;
win = NULL;
}
void efl_ui_test_list_container(TCase *tc)
{
tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
efl_ui_test_item_container_common_add(tc);
efl_ui_test_position_manager_common_add(tc);
}

View File

@ -0,0 +1,192 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Efl_Ui.h>
#include "efl_ui_suite.h"
#include "efl_ui_test_item_container_common.h"
Eo *position_manager;
static Eo* win;
static Eina_Array *arr_obj;
static Eina_Inarray *arr_size;
static Eina_Accessor inner_size_acc;
static Eina_Accessor *size_acc;
static void
item_container_setup()
{
win = win_add();
}
static void
item_container_teardown()
{
win = NULL;
}
static Eina_Bool
_get_at(Eina_Accessor *it EINA_UNUSED, unsigned int idx, void **data)
{
Eina_Size2D *result_ptr = (void*)data;
Eina_Size2D *inner_result;
if (!eina_accessor_data_get(size_acc, idx, (void*)&inner_result))
return EINA_FALSE;
*result_ptr = *inner_result;
return EINA_TRUE;
}
static void
_free_cb(Eina_Accessor *it EINA_UNUSED)
{
eina_accessor_free(size_acc);
}
static Eina_Bool
_lock_cb(Eina_Accessor *it EINA_UNUSED)
{
return eina_accessor_lock(size_acc);
}
static Eina_Accessor*
_clone_cb(Eina_Accessor *it EINA_UNUSED)
{
return eina_accessor_clone(size_acc);
}
static void
_initial_setup(void)
{
arr_obj = eina_array_new(10);
arr_size = eina_inarray_new(sizeof(Eina_Size2D), 10);
size_acc = eina_inarray_accessor_new(arr_size);
inner_size_acc.version = EINA_ACCESSOR_VERSION;
EINA_MAGIC_SET(&inner_size_acc, EINA_MAGIC_ACCESSOR);
inner_size_acc.get_at = _get_at;
inner_size_acc.free = _free_cb;
inner_size_acc.lock = _lock_cb;
inner_size_acc.clone = _clone_cb;
efl_ui_item_position_manager_data_access_set(position_manager,
eina_array_accessor_new(arr_obj),
&inner_size_acc, 0);
}
static int
_add_item(Eo *obj, Eina_Size2D size)
{
int idx = eina_array_count(arr_obj);
EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_array_count(arr_obj) == eina_inarray_count(arr_size), -1);
eina_array_push(arr_obj, (void*)0x1); //wtf
eina_array_data_set(arr_obj, idx, obj);
eina_inarray_push(arr_size, &size);
efl_ui_item_position_manager_item_added(position_manager, idx, obj);
return idx;
}
static void
_update_item(int index, Eo *obj, Eina_Size2D size)
{
Eina_Size2D *s;
eina_array_data_set(arr_obj, index, obj);
s = eina_inarray_nth(arr_size, index);
*s = size;
}
static void
_ticker(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
efl_loop_quit(efl_main_loop_get(), EINA_VALUE_EMPTY);
}
static void
_iterate_a_few(void)
{
efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TIMER_TICK, _ticker, NULL),
efl_loop_timer_interval_set(efl_added, 0.1));
efl_loop_begin(efl_main_loop_get());
}
EFL_START_TEST(no_crash1)
{
_initial_setup();
//try to resize the viewport while we have no item
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
_iterate_a_few();
_add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
_iterate_a_few();
}
EFL_END_TEST
EFL_START_TEST(no_crash2)
{
_initial_setup();
//try to resize the viewport while we have no item
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
_iterate_a_few();
_add_item(NULL, EINA_SIZE2D(20, 20));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 0, 0));
_iterate_a_few();
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(20, 20, 200, 200));
_iterate_a_few();
_update_item(0, efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
_iterate_a_few();
}
EFL_END_TEST
static void
_content_size_cb(void *data, const Efl_Event *ev)
{
Eina_Size2D *size = data;
*size = *((Eina_Size2D*)ev->info);
}
EFL_START_TEST(viewport_newsize_event_result)
{
Eina_Size2D size = EINA_SIZE2D(-2, -2), min_size = EINA_SIZE2D(-2, -2);
efl_event_callback_add(position_manager,
EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_SIZE_CHANGED, _content_size_cb, &size);
efl_event_callback_add(position_manager,
EFL_UI_ITEM_POSITION_MANAGER_EVENT_CONTENT_MIN_SIZE_CHANGED, _content_size_cb, &min_size);
_initial_setup();
_add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 20));
_add_item(efl_add(EFL_UI_GRID_DEFAULT_ITEM_CLASS, win), EINA_SIZE2D(20, 30));
efl_ui_item_position_manager_viewport_set(position_manager, EINA_RECT(0, 0, 200, 200));
_iterate_a_few();
ck_assert_int_ne(size.w, -2);
ck_assert_int_ne(size.h, -2);
ck_assert_int_ne(min_size.w, -2);
ck_assert_int_ne(min_size.h, -2);
}
EFL_END_TEST
void efl_ui_test_position_manager_common_add(TCase *tc)
{
tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown);
tcase_add_test(tc, no_crash1);
tcase_add_test(tc, no_crash2);
tcase_add_test(tc, viewport_newsize_event_result);
}

View File

@ -146,6 +146,11 @@ efl_ui_suite_src = [
'efl_ui_test_spin.c',
'efl_ui_test_spin_button.c',
'efl_ui_test_slider.c',
'efl_ui_test_item_container_common.c',
'efl_ui_test_item_container.c',
'efl_ui_test_list_container.c',
'efl_ui_test_grid_container.c',
'efl_ui_test_position_manager_common.c',
]
efl_ui_suite = executable('efl_ui_suite',

View File

@ -9,9 +9,10 @@
/* spec-meta-start
{"test-interface":"Efl.Pack_Linear",
"test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container"],
"test-widgets": ["Efl.Ui.Box", "Efl.Ui.Grid", "Efl.Ui.Spotlight.Container", "Test.Efl.Ui.Item_Container_List"],
"custom-mapping" : {
"Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS"
"Efl.Ui.Grid" : "EFL_UI_GRID_DEFAULT_ITEM_CLASS",
"Test.Efl.Ui.Item_Container_List" : "EFL_UI_LIST_DEFAULT_ITEM_CLASS"
}
}
spec-meta-end */

View File

@ -8,6 +8,16 @@
#include "suite_helpers.h"
#include "eo_internal.h"
//helper functions for custom widget intialization
EOLIAN static Efl_Object*
_test_efl_ui_item_container_list_efl_object_constructor(Eo *obj, void *pd EINA_UNUSED)
{
efl_constructor(efl_super(obj, TEST_EFL_UI_ITEM_CONTAINER_LIST_CLASS));
efl_ui_item_container_position_manager_set(obj, efl_new(EFL_UI_LIST_POSITION_MANAGER_CLASS));
return obj;
}
Evas_Object *win = NULL;
Evas_Object *widget = NULL;
const Efl_Class *test_content_klass = NULL;
@ -63,3 +73,5 @@ main(int argc, char **argv)
return (failed_count == 0) ? 0 : 255;
}
#include "test_efl_ui_item_container_list.eo.c"

View File

@ -5,6 +5,7 @@
#define EFL_NOLEGACY_API_SUPPORT
#include <Efl_Ui.h>
#include "test_efl_ui_item_container_list.eo.h"
#include "../efl_check.h"
extern Evas_Object *win;

View File

@ -1,3 +1,6 @@
priv_eo_files = [
'test_efl_ui_item_container_list.eo',
]
efl_ui_suite_behavior_test_files = files([
'efl_test_pack.c',
@ -21,6 +24,20 @@ efl_ui_suite_behavior_src = files([
test_generator = find_program('generator.py')
priv_eo_file_target = []
foreach eo_file : priv_eo_files
priv_eo_file_target += custom_target('eolian_gen_' + eo_file,
input : eo_file,
output : [eo_file + '.h'],
depfile : eo_file + '.d',
command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories,
'-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'),
'-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'),
'-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'),
'-gchd', '@INPUT@'])
endforeach
generated_test_parts = custom_target('generate_test_suite',
input: efl_ui_suite_behavior_test_files,
output: 'efl_ui_spec_suite_gen.x',
@ -28,7 +45,7 @@ generated_test_parts = custom_target('generate_test_suite',
)
efl_ui_behavior_suite = executable('efl_ui_spec_suite',
efl_ui_suite_behavior_src + [generated_test_parts],
efl_ui_suite_behavior_src + [generated_test_parts] + [priv_eo_file_target],
dependencies: [check, eina, elementary, elementary_deps],
include_directories : [config_dir] + [elementary_config_dir] + [include_directories('../')],
c_args : [

View File

@ -0,0 +1,6 @@
class @beta Test.Efl.Ui.Item_Container_List extends Efl.Ui.Item_Container {
data: null;
implements {
Efl.Object.constructor;
}
}