efl_ui_single_selectable: add properties and events

this commit adds 2 new features, a new event and a new property

The fallback property is usefull if you want to have a selection that always falls back
to some preconfigured things.
As a usecase, think about a list of languges where you can select the
languages you want to have. When everything is deselected, the list will
simply fallback to the preconfigured element (for example the language
configured before)

The event is annoncing that there was a change to the selection. The
event is allowed to be defered, it will be emitted once for a range of
selection elements that happened during one loop iteration. This is
usefull if you are interested in a general selection where you want to
know that we changed from a specific set of selected element to another
set of elements, without monitoring every selectable in the widget.

ref T8057

Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Reviewed-by: Cedric BAIL <cedric.bail@free.fr>
Differential Revision: https://phab.enlightenment.org/D9662
This commit is contained in:
Marcel Hollerbach 2019-08-20 18:22:00 +02:00
parent 1c1c3685fe
commit 43edd17f41
5 changed files with 157 additions and 74 deletions

View File

@ -132,10 +132,12 @@ typedef struct {
Efl_Ui_Pan *pan;
Eina_List *selected;
Eina_List *items;
Efl_Ui_Selection *fallback;
Efl_Ui_Select_Mode mode;
Efl_Ui_Layout_Orientation dir;
Eina_Size2D content_min_size;
Efl_Ui_Position_Manager_Entity *pos_man;
Eina_Future *selection_changed_job;
struct {
Eina_Bool w;
Eina_Bool h;
@ -418,6 +420,8 @@ _efl_ui_collection_efl_object_invalidate(Eo *obj, Efl_Ui_Collection_Data *pd EIN
{
efl_ui_collection_position_manager_set(obj, NULL);
efl_ui_single_selectable_fallback_selection_set(obj, NULL);
deselect_all(pd);
while(pd->items)
@ -492,12 +496,51 @@ _efl_ui_collection_efl_ui_multi_selectable_select_mode_get(const Eo *obj EINA_UN
return pd->mode;
}
static Eina_Value
_schedule_selection_job_cb(Eo *o, void *data EINA_UNUSED, const Eina_Value value EINA_UNUSED)
{
MY_DATA_GET(o, pd);
pd->selection_changed_job = NULL;
efl_event_callback_call(o, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, NULL);
return EINA_VALUE_EMPTY;
}
static void
_schedule_selection_changed(Eo *obj, Efl_Ui_Collection_Data *pd)
{
Eina_Future *f;
if (pd->selection_changed_job) return;
f = efl_loop_job(efl_main_loop_get());
pd->selection_changed_job = efl_future_then(obj, f, _schedule_selection_job_cb);
}
static void
_apply_fallback(Eo *obj EINA_UNUSED, Efl_Ui_Collection_Data *pd)
{
if (pd->fallback && !pd->selected)
{
efl_ui_selectable_selected_set(pd->fallback, EINA_TRUE);
}
}
static void
_selection_changed(void *data, const Efl_Event *ev)
{
Eina_Bool selection = *((Eina_Bool*) ev->info);
Eo *obj = data;
MY_DATA_GET(obj, pd);
Efl_Ui_Selection *fallback;
//if this is the highest call in the tree of selection changes, safe the fallback and apply it later
//this way we ensure that we are not accidently adding fallback even if we just want to have a empty selection list
fallback = pd->fallback;
pd->fallback = NULL;
if (selection)
{
@ -520,13 +563,15 @@ _selection_changed(void *data, const Efl_Event *ev)
return;
}
pd->selected = eina_list_append(pd->selected, ev->object);
efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_SELECTED, ev->object);
}
else
{
pd->selected = eina_list_remove(pd->selected, ev->object);
efl_event_callback_call(obj, EFL_UI_EVENT_ITEM_UNSELECTED, ev->object);
}
pd->fallback = fallback;
_apply_fallback(obj, pd);
_schedule_selection_changed(obj, pd);
}
static void
@ -1010,6 +1055,19 @@ _efl_ui_collection_efl_ui_multi_selectable_unselect_range(Eo *obj, Efl_Ui_Collec
_range_selection_find(obj, pd, a, b, EINA_FALSE);
}
EOLIAN static void
_efl_ui_collection_efl_ui_single_selectable_fallback_selection_set(Eo *obj EINA_UNUSED, Efl_Ui_Collection_Data *pd, Efl_Ui_Selectable *fallback)
{
pd->fallback = fallback;
_apply_fallback(obj, pd);
}
EOLIAN static Efl_Ui_Selectable*
_efl_ui_collection_efl_ui_single_selectable_fallback_selection_get(const Eo *obj EINA_UNUSED, Efl_Ui_Collection_Data *pd)
{
return pd->fallback;
}
#include "efl_ui_collection.eo.c"
#define ITEM_IS_OUTSIDE_VISIBLE(id) id < collection_pd->start_id || id > collection_pd->end_id
@ -1090,4 +1148,5 @@ _efl_ui_collection_focus_manager_efl_ui_focus_manager_manager_focus_set(Eo *obj,
efl_ui_focus_manager_focus_set(efl_super(obj, EFL_UI_COLLECTION_FOCUS_MANAGER_CLASS), focus);
}
#include "efl_ui_collection_focus_manager.eo.c"

View File

@ -3,7 +3,6 @@ class @beta Efl.Ui.Collection extends Efl.Ui.Layout_Base implements
Efl.Ui.Scrollbar,
Efl.Pack_Linear, Efl.Pack_Layout,
Efl.Ui.Layout_Orientable,
Efl.Ui.Container_Selectable,
Efl.Ui.Multi_Selectable,
Efl.Ui.Focus.Manager_Sub,
Efl.Ui.Widget_Focus_Manager
@ -71,6 +70,7 @@ class @beta Efl.Ui.Collection extends Efl.Ui.Layout_Base implements
Efl.Ui.Multi_Selectable.unselect_all;
Efl.Ui.Multi_Selectable.select_range;
Efl.Ui.Multi_Selectable.unselect_range;
Efl.Ui.Single_Selectable.fallback_selection {get; set;}
}
events {
item,pressed : Efl.Ui.Item; [[A $press event occurred over an item.]]

View File

@ -13,5 +13,20 @@ interface @beta Efl.Ui.Single_Selectable {
selectable : Efl.Ui.Selectable; [[The latest selected item.]]
}
}
@property fallback_selection {
[[A object that will be selected in case nothing is selected
A object set to this property will be selected instead of no item beeing selected. Which means, there will be always at least one element selected.
If this property is NULL, the state of "no item is selected" can be reached.
Setting this property as a result of selection events results in undefined behavior.
]]
values {
fallback : Efl.Ui.Selectable;
}
}
}
events {
selection_changed : void; [[Called when there is a change in the selection state, this event will collect all the item selection change events that are happening within one loop iteration. This means, you will only get this event once, even if a lot of items have changed. If you are interested in detailed changes, subscribe to the selection,changed event of Efl.Ui.Selectable.]]
}
}

View File

@ -42,23 +42,22 @@ _iterator_to_array(Eina_Array **arr, Eina_Iterator *iter)
EFL_START_TEST(test_multi_select)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
int c = 0;
Eina_Array *arr_selected;
efl_ui_select_mode_set(widget, EFL_UI_SELECT_MODE_MULTI);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 2));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 0)), EINA_TRUE);
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), efl_pack_content_get(widget, 2));
@ -68,68 +67,59 @@ EFL_START_TEST(test_multi_select)
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 1), efl_pack_content_get(widget, 2));
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
ck_assert_int_eq(c, 0);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
}
EFL_END_TEST
EFL_START_TEST(test_multi_select_removal)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
int c = 0;
Eina_Array *arr_selected;
efl_ui_select_mode_set(widget, EFL_UI_SELECT_MODE_MULTI);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 0), EINA_TRUE);
selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
selected = NULL;//No need to ckeck the flag, we asserted in the tcase before
unselected = NULL;
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 0), EINA_FALSE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, efl_pack_content_get(widget, 0));
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_FALSE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, efl_pack_content_get(widget, 2));
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), NULL);
_iterator_to_array(&arr_selected, efl_ui_selected_items_get(widget));
ck_assert_int_eq(eina_array_count(arr_selected), 0);
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
}
EFL_END_TEST
EFL_START_TEST(test_single_select)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
int c = 0;
Eina_Array *arr_selected;
efl_ui_select_mode_set(widget, EFL_UI_SELECT_MODE_SINGLE);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 2));
ck_assert_ptr_eq(unselected, efl_pack_content_get(widget, 0));
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 0)), EINA_FALSE);
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), efl_pack_content_get(widget, 2));
@ -138,48 +128,42 @@ EFL_START_TEST(test_single_select)
ck_assert_ptr_eq(eina_array_data_get(arr_selected, 0), efl_pack_content_get(widget, 2));
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, NULL);
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
ck_assert_int_eq(c, 0);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
}
EFL_END_TEST
EFL_START_TEST(test_single_select_always)
{
Efl_Ui_Item *selected = NULL;
Efl_Ui_Item *unselected = NULL;
int c = 0;
Eina_Array *arr_selected;
efl_ui_select_mode_set(widget, EFL_UI_SELECT_MODE_SINGLE_ALWAYS);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_add(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 0), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 0));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 2));
ck_assert_ptr_eq(unselected, efl_pack_content_get(widget, 0));
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(efl_pack_content_get(widget, 2), EINA_TRUE);
ck_assert_ptr_eq(selected, efl_pack_content_get(widget, 2));
ck_assert_ptr_eq(unselected, NULL);
selected = NULL;
unselected = NULL;
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 0)), EINA_FALSE);
ck_assert_int_eq(efl_ui_selectable_selected_get(efl_pack_content_get(widget, 2)), EINA_TRUE);
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), efl_pack_content_get(widget, 2));
_iterator_to_array(&arr_selected, efl_ui_selected_items_get(widget));
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(widget, 2));
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_SELECTED, _set_pointer_quit, &selected);
efl_event_callback_del(widget, EFL_UI_EVENT_ITEM_UNSELECTED, _set_pointer_quit, &unselected);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_del(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
}
EFL_END_TEST

View File

@ -33,11 +33,19 @@ _setup(void)
EFL_START_TEST(last_selectable_check)
{
int c = 0;
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, (Efl_Event_Cb) event_callback_single_call_int_data, &c);
efl_event_callback_add(widget, EFL_UI_SINGLE_SELECTABLE_EVENT_SELECTION_CHANGED, event_callback_that_quits_the_main_loop_when_called, NULL);
Eo *c1 = efl_pack_content_get(widget, 0);
Eo *c2 = efl_pack_content_get(widget, 2);
efl_ui_selectable_selected_set(c1, EINA_TRUE);
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), c1);
if (c == 0) efl_loop_begin(efl_main_loop_get());
ck_assert_int_eq(c, 1);
c = 0;
efl_ui_selectable_selected_set(c2, EINA_TRUE);
ck_assert_ptr_eq(efl_ui_single_selectable_last_selected_get(widget), c2);
@ -48,9 +56,26 @@ EFL_START_TEST(last_selectable_check)
}
EFL_END_TEST
EFL_START_TEST(fallback_selection)
{
Eo *c1 = efl_pack_content_get(widget, 0);
Eo *c2 = efl_pack_content_get(widget, 2);
efl_ui_single_selectable_fallback_selection_set(widget, c2);
ck_assert_int_eq(efl_ui_selectable_selected_get(c2), EINA_TRUE);
efl_ui_selectable_selected_set(c1, EINA_TRUE);
ck_assert_int_eq(efl_ui_selectable_selected_get(c2), EINA_FALSE);
ck_assert_int_eq(efl_ui_selectable_selected_get(c1), EINA_TRUE);
efl_ui_selectable_selected_set(c1, EINA_FALSE);
ck_assert_int_eq(efl_ui_selectable_selected_get(c2), EINA_TRUE);
ck_assert_int_eq(efl_ui_selectable_selected_get(c1), EINA_FALSE);
}
EFL_END_TEST
void
efl_ui_single_selectable_behavior_test(TCase *tc)
{
tcase_add_checked_fixture(tc, _setup, NULL);
tcase_add_test(tc, last_selectable_check);
tcase_add_test(tc, fallback_selection);
}