summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2019-05-29 13:03:37 +0200
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2019-06-20 16:02:00 +0200
commitc9177a9f8d75fc7bd2634a0adb63106492f3e94c (patch)
treec116e4c39d0f3f7d36233fdfc9b57dedc6b91441
parent42b293ae1f89aa3737fe7a4d81ebc3596fd2c631 (diff)
downloadefl-c9177a9f8d75fc7bd2634a0adb63106492f3e94c.tar.gz
Introduce Efl.Ui.Radio_Group & Efl.Ui.Radio_Box
Radio_Group is a interface that manages that radio groups can be grouped inside a external object, the current API of radio was considered confusing in that regard. It is implemented in the Radio_Group_Internal class which is private to EFL, a instance of it can be found with get due to the class function in efl_ui_radio.eo. This architecture was taken like this, in order to have implementation and interface seperated. With those two seperated we can inherit from regular widgets, implement the interface, and composite attach the internal object to the regular widget. This makes a lot of things easier. Radio_Box is a class which is extending Efl.Ui.Box, which has an internal Radio_Group. This is extremly usefull for cases where you just want to have a list of radio buttons in your UI. The radio group is also exposed using composition to the internal object. Simular things can be done for the table. For now i did not add API to find the group of a radio button. However, this can be quickly added if requested. ref T7867 Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Reviewed-by: Xavi Artigas <xavierartigas@yahoo.es> Differential Revision: https://phab.enlightenment.org/D9058
-rw-r--r--src/bin/elementary/meson.build1
-rw-r--r--src/bin/elementary/test.c2
-rw-r--r--src/bin/elementary/test_ui_radio.c136
-rw-r--r--src/examples/elementary/efl_ui_radio_example_01.c46
-rw-r--r--src/examples/elementary/meson.build3
-rw-r--r--src/lib/elementary/efl_ui_radio.c1
-rw-r--r--src/lib/elementary/efl_ui_radio.eo46
-rw-r--r--src/lib/elementary/efl_ui_radio_box.c127
-rw-r--r--src/lib/elementary/efl_ui_radio_box.eo21
-rw-r--r--src/lib/elementary/efl_ui_radio_eo.h6
-rw-r--r--src/lib/elementary/efl_ui_radio_group.eo50
-rw-r--r--src/lib/elementary/efl_ui_radio_group_impl.c152
-rw-r--r--src/lib/elementary/efl_ui_radio_group_impl.eo11
-rw-r--r--src/lib/elementary/meson.build5
-rw-r--r--src/tests/elementary/efl_ui_suite.c1
-rw-r--r--src/tests/elementary/efl_ui_suite.h1
-rw-r--r--src/tests/elementary/efl_ui_test_radio_group.c266
-rw-r--r--src/tests/elementary/meson.build1
18 files changed, 829 insertions, 47 deletions
diff --git a/src/bin/elementary/meson.build b/src/bin/elementary/meson.build
index 65479f79ee..c17a182b86 100644
--- a/src/bin/elementary/meson.build
+++ b/src/bin/elementary/meson.build
@@ -100,6 +100,7 @@ elementary_test_src = [
'test_ui_panes.c',
'test_ui_panel.c',
'test_ui_active_view.c',
+ 'test_ui_radio.c',
'test_part_bg.c',
'test_part_shadow.c',
'test_photo.c',
diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c
index c797426c2c..bcca771d63 100644
--- a/src/bin/elementary/test.c
+++ b/src/bin/elementary/test.c
@@ -391,6 +391,7 @@ void test_ui_active_view_plain(void *data, Evas_Object *obj, void *event_info);
void test_ui_active_view_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);
static void _list_udpate(void);
@@ -1076,6 +1077,7 @@ add_tests:
ADD_TEST(NULL, "Selectors", "DaySelector", test_dayselector);
ADD_TEST(NULL, "Selectors", "Main menu", test_main_menu);
ADD_TEST(NULL, "Selectors", "Combobox", test_combobox);
+ ADD_TEST_EO(NULL, "Selectors", "Efl.Ui.Radio_Box", test_efl_ui_radio);
//------------------------------//
ADD_TEST(NULL, "Cursors", "Cursor", test_cursor);
diff --git a/src/bin/elementary/test_ui_radio.c b/src/bin/elementary/test_ui_radio.c
new file mode 100644
index 0000000000..259846f96d
--- /dev/null
+++ b/src/bin/elementary/test_ui_radio.c
@@ -0,0 +1,136 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Efl_Ui.h>
+
+const char *countries[] =
+{
+ "Germany",
+ "USA",
+ "France",
+ "Korea",
+ "UK",
+ "Romania",
+ "Italy",
+ NULL,
+};
+#define MAX_INDEX 8
+
+static void
+_check_button_selection_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+ if (efl_ui_check_selected_get(ev->object))
+ printf("Object %p is now selected\n", ev->object);
+ else
+ printf("Object %p is now unselected\n", ev->object);
+}
+
+static Eina_Array*
+create_radios(Efl_Ui_Win *win)
+{
+ Eina_Array *arr = eina_array_new(5);
+
+ for (unsigned int i = 0; countries[i]; ++i)
+ {
+ Efl_Ui_Radio *rbtn = efl_add(EFL_UI_RADIO_CLASS, win);
+ efl_ui_radio_state_value_set(rbtn, i);
+ efl_text_set(rbtn, countries[i]);
+ efl_event_callback_add(rbtn, EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _check_button_selection_changed_cb, NULL);
+ eina_array_push(arr, rbtn);
+ }
+
+ return arr;
+}
+
+static void
+_value_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+ Efl_Ui_Radio_Group *g = ev->object;
+ int index = efl_ui_radio_group_selected_value_get(g);
+ if (index == -1)
+ {
+ printf("Nothing is selected anymore\n");
+ }
+ else
+ {
+ EINA_SAFETY_ON_FALSE_RETURN((index >= 0) && index < MAX_INDEX);
+ printf("Now selected value %s\n", countries[index]);
+ }
+
+}
+
+static void
+_select_btn_clicked(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+ Efl_Ui_Check *c = data;
+
+ efl_ui_check_selected_set(c, EINA_TRUE);
+}
+
+static void
+_set_selected_btn_clicked(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+ Efl_Ui_Radio_Group *group = data;
+
+ efl_ui_radio_group_selected_value_set(group, 0);
+}
+
+static void
+_set_selected_object_btn_clicked(void *data, const Efl_Event *ev)
+{
+ Efl_Ui_Radio_Group *group = data;
+
+ efl_ui_radio_group_selected_value_set(group, 3);
+ efl_ui_radio_group_selected_object_set(group, efl_key_data_get(ev->object, "uk"));
+}
+
+void test_efl_ui_radio(void *data EINA_UNUSED,
+ Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Efl_Ui_Win *win;
+ Efl_Ui_Table *table;
+ Efl_Ui_Box *bx;
+ Eina_Array *arr;
+ Efl_Ui_Button *o;
+ Efl_Ui_Radio *uk;
+
+ 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.Radio_Box"),
+ efl_ui_win_autodel_set(efl_added, EINA_TRUE));
+ table = efl_add(EFL_UI_TABLE_CLASS, win);
+ efl_content_set(win, table);
+
+ bx = efl_add(EFL_UI_RADIO_BOX_CLASS, table);
+ efl_pack_table(table, bx, 0, 0, 1, 3);
+ efl_event_callback_add(bx, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _value_changed_cb, NULL);
+
+ arr = create_radios(win);
+ for (unsigned int i = 0; i < eina_array_count(arr); ++i)
+ {
+ Efl_Ui_Radio *r = eina_array_data_get(arr, i);
+ efl_pack_end(bx, r);
+ if (i == 4)
+ uk = r;
+ }
+
+ o = efl_add(EFL_UI_BUTTON_CLASS, table);
+ efl_pack_table(table, o, 1, 0, 1, 1);
+ efl_text_set(o, "Selected France check");
+ efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _select_btn_clicked, eina_array_data_get(arr, 2));
+
+ o = efl_add(EFL_UI_BUTTON_CLASS, table);
+ efl_pack_table(table, o, 1, 1, 1, 1);
+ efl_text_set(o, "Set value for Germany");
+ efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _set_selected_btn_clicked, bx);
+
+ o = efl_add(EFL_UI_BUTTON_CLASS, table);
+ efl_pack_table(table, o, 1, 2, 1, 1);
+ efl_text_set(o, "Set object for UK");
+ efl_key_data_set(o, "uk", uk);
+ efl_event_callback_add(o, EFL_UI_EVENT_CLICKED, _set_selected_object_btn_clicked, bx);
+
+ eina_array_free(arr);
+}
diff --git a/src/examples/elementary/efl_ui_radio_example_01.c b/src/examples/elementary/efl_ui_radio_example_01.c
new file mode 100644
index 0000000000..b6b72e2ae2
--- /dev/null
+++ b/src/examples/elementary/efl_ui_radio_example_01.c
@@ -0,0 +1,46 @@
+/*
+ * gcc -o efl_ui_radio_example_01 efl_ui_radio_example_01.c `pkg-config --cflags --libs elementary`
+ */
+#define EFL_BETA_API_SUPPORT 1
+
+#include <Efl_Ui.h>
+#include <Elementary.h>
+
+const char *example_strings[] = {
+ "Seoul",
+ "Karlsruhe",
+ "New York",
+ "Hong Kong",
+ "Hamburg",
+ "Berlin",
+ "Paris",
+ NULL
+};
+
+EAPI_MAIN void
+efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
+{
+ Eo *win, *box;
+
+ elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+
+ 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.Radio example"),
+ efl_ui_win_autodel_set(efl_added, EINA_TRUE)
+ );
+
+ box = efl_add(EFL_UI_RADIO_BOX_CLASS, win,
+ efl_content_set(win, efl_added));
+
+ for (int i = 0; example_strings[i]; ++i)
+ {
+ Eo *radio;
+
+ radio = efl_add(EFL_UI_RADIO_CLASS, box);
+ efl_text_set(radio, example_strings[i]);
+ efl_ui_radio_state_value_set(radio, i + 1);
+ efl_pack_end(box, radio);
+ }
+}
+EFL_MAIN()
diff --git a/src/examples/elementary/meson.build b/src/examples/elementary/meson.build
index 35a7eeac03..f29a94f687 100644
--- a/src/examples/elementary/meson.build
+++ b/src/examples/elementary/meson.build
@@ -117,7 +117,8 @@ examples = [
'efl_canvas_layout_text',
'efl_ui_theme_example_01',
'efl_ui_theme_example_02',
- 'efl_ui_slideshow_example'
+ 'efl_ui_slideshow_example',
+ 'efl_ui_radio_example_01',
]
foreach example : examples
diff --git a/src/lib/elementary/efl_ui_radio.c b/src/lib/elementary/efl_ui_radio.c
index a25a9b6007..35e273a06f 100644
--- a/src/lib/elementary/efl_ui_radio.c
+++ b/src/lib/elementary/efl_ui_radio.c
@@ -373,6 +373,7 @@ ELM_LAYOUT_TEXT_ALIASES_IMPLEMENT(MY_CLASS_PFX)
ELM_LAYOUT_TEXT_ALIASES_OPS(MY_CLASS_PFX)
#include "efl_ui_radio.eo.c"
+#include "efl_ui_radio_group.eo.c"
#include "efl_ui_radio_eo.legacy.c"
#include "efl_ui_radio_legacy_eo.h"
diff --git a/src/lib/elementary/efl_ui_radio.eo b/src/lib/elementary/efl_ui_radio.eo
index a833f39e09..e59e548300 100644
--- a/src/lib/elementary/efl_ui_radio.eo
+++ b/src/lib/elementary/efl_ui_radio.eo
@@ -19,52 +19,6 @@ class @beta Efl.Ui.Radio extends Efl.Ui.Check implements Efl.Access.Widget.Actio
value: int; [[The value to use if this radio object is selected.]]
}
}
- @property value_pointer {
- set {
- [[Set a convenience pointer to an integer, which changes when radio group
- value changes.
-
- This sets a pointer to an integer that in addition to the radio
- object state will also be modified directly. To stop setting the
- object pointed to, simply use NULL as the valuep argument. If
- valuep is not NULL then when called, the radio object
- state will also be modified to reflect the value of the integer
- valuep points to, just like calling elm_radio_value_set().
- ]]
- }
- values {
- valuep: ptr(int); [[Pointer to the integer to modify]]
- }
- }
- @property selected_object {
- get {
- [[Get the selected radio object.]]
- return: Efl.Canvas.Object; [[The selected radio object]]
- }
- }
- group_add {
- [[Add this radio to a group of other radio objects
-
- Radio objects work in groups. Each member should have a different
- integer value assigned. In order to have them work as a group, they
- need to know about each other. This adds the given radio object to
- the group of which the group object indicated is a member.
- ]]
-
- params {
- @in group: Efl.Ui.Radio; [[Any radio object whose group the obj is
- to join.]]
- }
- }
- @property group_value {
- [[Change the value of the group.
-
- This will enable the radio button in the group that is assosiated with this value. A value which is not assosiated with any radio button will result in every radio button beeing disabled.
- ]]
- values {
- value : int; [[The value of the radio button in the group which should be enabled.]]
- }
- }
}
implements {
Efl.Object.constructor;
diff --git a/src/lib/elementary/efl_ui_radio_box.c b/src/lib/elementary/efl_ui_radio_box.c
new file mode 100644
index 0000000000..b58e76f3fe
--- /dev/null
+++ b/src/lib/elementary/efl_ui_radio_box.c
@@ -0,0 +1,127 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+#define MY_CLASS EFL_UI_RADIO_BOX_CLASS
+
+typedef struct {
+ Eina_Bool in_pack;
+ Efl_Ui_Radio_Group *group;
+} Efl_Ui_Radio_Box_Data;
+
+static inline Eina_Bool
+register_safe_in_group_begin(Eo *subobj, Efl_Ui_Radio_Box_Data *pd)
+{
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(subobj, EFL_UI_RADIO_CLASS), EINA_FALSE);
+ if (!pd->in_pack)
+ efl_ui_radio_group_register(pd->group, subobj);
+ pd->in_pack = EINA_TRUE;
+
+ return EINA_TRUE;
+}
+
+static inline Eina_Bool
+register_safe_group_end(Eo *subobj, Efl_Ui_Radio_Box_Data *pd, Eina_Bool result)
+{
+ if (!result)
+ efl_ui_radio_group_unregister(pd->group, subobj);
+ pd->in_pack = EINA_FALSE;
+
+ return result;
+}
+
+#define REGISTER_SAFE(f) \
+ Eina_Bool result; \
+ if (!register_safe_in_group_begin(subobj, pd)) \
+ return EINA_FALSE; \
+ result = f ; \
+ return register_safe_group_end(subobj, pd, result);
+
+static void
+unpack_from_logical(Eo *obj, Efl_Ui_Radio_Box_Data *pd)
+{
+ int length = efl_content_count(obj);
+ for (int i = 0; i < length; ++i)
+ {
+ efl_ui_radio_group_unregister(pd->group, efl_pack_content_get(obj, i));
+ }
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_pack_clear(Eo *obj, Efl_Ui_Radio_Box_Data *pd)
+{
+ unpack_from_logical(obj, pd);
+ return efl_pack_clear(efl_super(obj, MY_CLASS));
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_unpack_all(Eo *obj, Efl_Ui_Radio_Box_Data *pd)
+{
+ unpack_from_logical(obj, pd);
+ return efl_pack_unpack_all(efl_super(obj, MY_CLASS));
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_unpack(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj)
+{
+ efl_ui_radio_group_unregister(pd->group, subobj);
+ return efl_pack_unpack(efl_super(obj, MY_CLASS), subobj);
+}
+
+EOLIAN static Efl_Gfx_Entity*
+_efl_ui_radio_box_efl_pack_linear_pack_unpack_at(Eo *obj, Efl_Ui_Radio_Box_Data *pd, int index)
+{
+ efl_ui_radio_group_unregister(pd->group, efl_pack_content_get(obj, index));
+ return efl_pack_unpack_at(efl_super(obj, MY_CLASS), index);
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_pack(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj)
+{
+ REGISTER_SAFE(efl_pack(efl_super(obj, MY_CLASS), subobj))
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_linear_pack_begin(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj)
+{
+ REGISTER_SAFE(efl_pack_begin(efl_super(obj, MY_CLASS), subobj))
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_linear_pack_end(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj)
+{
+ REGISTER_SAFE(efl_pack_end(efl_super(obj, MY_CLASS), subobj))
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_linear_pack_before(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing)
+{
+ REGISTER_SAFE(efl_pack_before(efl_super(obj, MY_CLASS), subobj, existing));
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_linear_pack_after(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj, const Efl_Gfx_Entity *existing)
+{
+ REGISTER_SAFE(efl_pack_after(efl_super(obj, MY_CLASS), subobj, existing));
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_radio_box_efl_pack_linear_pack_at(Eo *obj, Efl_Ui_Radio_Box_Data *pd, Efl_Gfx_Entity *subobj, int index)
+{
+ REGISTER_SAFE(efl_pack_at(efl_super(obj, MY_CLASS), subobj, index));
+}
+
+EOLIAN static Efl_Object*
+_efl_ui_radio_box_efl_object_constructor(Eo *obj, Efl_Ui_Radio_Box_Data *pd)
+{
+ pd->group = efl_new(EFL_UI_RADIO_GROUP_IMPL_CLASS, NULL);
+ efl_composite_attach(obj, pd->group);
+ efl_event_callback_forwarder_add(pd->group, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, obj);
+ return efl_constructor(efl_super(obj, MY_CLASS));
+}
+
+
+#include "efl_ui_radio_box.eo.c"
diff --git a/src/lib/elementary/efl_ui_radio_box.eo b/src/lib/elementary/efl_ui_radio_box.eo
new file mode 100644
index 0000000000..07cfb81bbc
--- /dev/null
+++ b/src/lib/elementary/efl_ui_radio_box.eo
@@ -0,0 +1,21 @@
+class @beta Efl.Ui.Radio_Box extends Efl.Ui.Box implements Efl.Ui.Radio_Group
+{
+ methods {
+ }
+ implements {
+ Efl.Pack.pack_clear;
+ Efl.Pack.unpack_all;
+ Efl.Pack.unpack;
+ Efl.Pack.pack;
+ Efl.Pack_Linear.pack_begin;
+ Efl.Pack_Linear.pack_end;
+ Efl.Pack_Linear.pack_before;
+ Efl.Pack_Linear.pack_after;
+ Efl.Pack_Linear.pack_at;
+ Efl.Pack_Linear.pack_unpack_at;
+ Efl.Object.constructor;
+ }
+ composite {
+ Efl.Ui.Radio_Group;
+ }
+}
diff --git a/src/lib/elementary/efl_ui_radio_eo.h b/src/lib/elementary/efl_ui_radio_eo.h
index c16605e9a3..bd8665d728 100644
--- a/src/lib/elementary/efl_ui_radio_eo.h
+++ b/src/lib/elementary/efl_ui_radio_eo.h
@@ -1 +1,7 @@
+#define _EFL_UI_RADIO_EO_CLASS_TYPE
+typedef Eo Efl_Ui_Radio;
+
+#include "efl_ui_radio_group.eo.h"
+#include "efl_ui_radio_group_impl.eo.h"
#include "efl_ui_radio.eo.h"
+#include "efl_ui_radio_box.eo.h"
diff --git a/src/lib/elementary/efl_ui_radio_group.eo b/src/lib/elementary/efl_ui_radio_group.eo
new file mode 100644
index 0000000000..c19636308c
--- /dev/null
+++ b/src/lib/elementary/efl_ui_radio_group.eo
@@ -0,0 +1,50 @@
+interface @beta Efl.Ui.Radio_Group
+{
+ [[A object for organising a group of radio buttons
+
+ A group of radio buttons is a set of pair-wise different values. (Where values are the assosiated values of each radio button)
+ At any point of time only 1 or 0 buttons can be selected. If a new button is selected, the previous selected button will be unselected.
+ ]]
+ methods {
+ @property selected_object {
+ [[Which object is currently selected in the group
+
+ $NULL if no object is selected.
+ ]]
+ values {
+ selected_object : Efl.Ui.Radio; [[The selected radio button in the group, $NULL for no object]]
+ }
+ }
+ @property selected_value {
+ [[Each group has a selected value, representing which radio group is selected.
+
+ A value of -1 here does represent that no button is selected.
+ Possible values here are only those that are assisiated with a registered radio button.
+ ]]
+ values {
+ selected_value : int; [[The value of the selection state]]
+ }
+ }
+ register {
+ [[Register a new radio button to this group.
+
+ The assosiated value of the radio button must not collide with any other assosiated value of registered buttons.
+ ]]
+ params {
+ radio : Efl.Ui.Radio; [[The radio button to regsiter]]
+ }
+ }
+ unregister {
+ [[Unregsiter a before registered button from this group.
+
+ if the button was selected before, no object will be selected afterwards.
+ ]]
+ params {
+ radio : Efl.Ui.Radio; [[The radio button to unregister]]
+ }
+ }
+ }
+ events {
+ value,changed : int; [[Emitted each time the $selected_value has changed.]]
+ }
+}
diff --git a/src/lib/elementary/efl_ui_radio_group_impl.c b/src/lib/elementary/efl_ui_radio_group_impl.c
new file mode 100644
index 0000000000..4e68299540
--- /dev/null
+++ b/src/lib/elementary/efl_ui_radio_group_impl.c
@@ -0,0 +1,152 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+#define MY_CLASS EFL_UI_RADIO_GROUP_IMPL_CLASS
+
+static Eina_Hash *radio_group_map;
+
+typedef struct {
+ Efl_Ui_Radio *selected;
+ Eina_List *registered_set;
+} Efl_Ui_Radio_Group_Impl_Data;
+
+EOLIAN static void
+_efl_ui_radio_group_impl_efl_ui_radio_group_selected_object_set(Eo *obj EINA_UNUSED, Efl_Ui_Radio_Group_Impl_Data *pd, Efl_Ui_Radio *selected_object)
+{
+ int new_value = -1;
+ Eo *old_selected;
+
+ if (pd->selected == selected_object) return;
+
+ old_selected = pd->selected;
+ pd->selected = selected_object;
+
+ //it is essential to *first* set pd->selected to the new state before calling this
+ //otherwise this here will be called again, and the event will get emitted twice
+ if (old_selected && efl_alive_get(old_selected))
+ efl_ui_check_selected_set(old_selected, EINA_FALSE);
+
+ if (pd->selected)
+ {
+ efl_ui_check_selected_set(pd->selected, EINA_TRUE);
+ new_value = efl_ui_radio_state_value_get(pd->selected);
+ }
+ efl_event_callback_call(obj, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, &new_value);
+}
+
+EOLIAN static Efl_Ui_Radio*
+_efl_ui_radio_group_impl_efl_ui_radio_group_selected_object_get(const Eo *obj EINA_UNUSED, Efl_Ui_Radio_Group_Impl_Data *pd)
+{
+ return pd->selected;
+}
+
+EOLIAN static void
+_efl_ui_radio_group_impl_efl_ui_radio_group_selected_value_set(Eo *obj, Efl_Ui_Radio_Group_Impl_Data *pd, int selected_value)
+{
+ Efl_Ui_Radio *reged;
+ Eina_List *n;
+
+ EINA_LIST_FOREACH(pd->registered_set, n, reged)
+ {
+ if (efl_ui_radio_state_value_get(reged) == selected_value)
+ {
+ efl_ui_radio_group_selected_object_set(obj, reged);
+ return;
+ }
+ }
+ ERR("Value %d not assosiated with any radio button", selected_value);
+}
+
+EOLIAN static int
+_efl_ui_radio_group_impl_efl_ui_radio_group_selected_value_get(const Eo *obj EINA_UNUSED, Efl_Ui_Radio_Group_Impl_Data *pd)
+{
+ return pd->selected ? efl_ui_radio_state_value_get(pd->selected) : -1;
+}
+
+static void
+_selected_cb(void *data, const Efl_Event *ev)
+{
+ if (efl_ui_check_selected_get(ev->object))
+ {
+ efl_ui_radio_group_selected_object_set(data, ev->object);
+ }
+ else
+ {
+ //if something was unselected, we need to make sure that we are unsetting the internal pointer to NULL
+ if (efl_ui_radio_group_selected_object_get(data) == ev->object)
+ {
+ efl_ui_radio_group_selected_object_set(data, NULL);
+ }
+ }
+}
+
+static void
+_invalidate_cb(void *data, const Efl_Event *ev)
+{
+ efl_ui_radio_group_unregister(data, ev->object);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(radio_btn_cb,
+ {EFL_UI_CHECK_EVENT_SELECTED_CHANGED, _selected_cb},
+ {EFL_EVENT_INVALIDATE, _invalidate_cb},
+)
+
+EOLIAN static void
+_efl_ui_radio_group_impl_efl_ui_radio_group_register(Eo *obj, Efl_Ui_Radio_Group_Impl_Data *pd, Efl_Ui_Radio *radio)
+{
+ Efl_Ui_Radio *reged;
+ Eina_List *n;
+
+ if (eina_hash_find(radio_group_map, &radio))
+ {
+ ERR("Radio button %p is already part of another group", radio);
+ return;
+ }
+
+ EINA_LIST_FOREACH(pd->registered_set, n, reged)
+ {
+ EINA_SAFETY_ON_TRUE_RETURN(radio == reged);
+ EINA_SAFETY_ON_TRUE_RETURN(efl_ui_radio_state_value_get(radio) == efl_ui_radio_state_value_get(reged));
+ }
+ EINA_SAFETY_ON_TRUE_RETURN(efl_ui_radio_state_value_get(radio) == -1);
+
+ pd->registered_set = eina_list_append(pd->registered_set, radio);
+ eina_hash_add(radio_group_map, &radio, obj);
+ efl_event_callback_array_add(radio, radio_btn_cb(), obj);
+}
+
+EOLIAN static void
+_efl_ui_radio_group_impl_efl_ui_radio_group_unregister(Eo *obj, Efl_Ui_Radio_Group_Impl_Data *pd, Efl_Ui_Radio *radio)
+{
+ if (pd->selected == radio)
+ efl_ui_radio_group_selected_object_set(obj, NULL);
+
+ efl_event_callback_array_del(radio, radio_btn_cb(), obj);
+ pd->registered_set = eina_list_remove(pd->registered_set, radio);
+ eina_hash_del(radio_group_map, &radio, obj);
+}
+
+EOLIAN static void
+_efl_ui_radio_group_impl_efl_object_destructor(Eo *obj, Efl_Ui_Radio_Group_Impl_Data *pd)
+{
+ Eo *radio;
+
+ EINA_LIST_FREE(pd->registered_set, radio)
+ {
+ efl_event_callback_array_del(radio, radio_btn_cb(), obj);
+ eina_hash_del(radio_group_map, &radio, obj);
+ }
+ efl_destructor(efl_super(obj, MY_CLASS));
+}
+
+void
+_efl_ui_radio_group_impl_class_constructor(Efl_Class *klass EINA_UNUSED)
+{
+ radio_group_map = eina_hash_pointer_new(NULL);
+}
+
+#include "efl_ui_radio_group_impl.eo.c"
diff --git a/src/lib/elementary/efl_ui_radio_group_impl.eo b/src/lib/elementary/efl_ui_radio_group_impl.eo
new file mode 100644
index 0000000000..82204cf386
--- /dev/null
+++ b/src/lib/elementary/efl_ui_radio_group_impl.eo
@@ -0,0 +1,11 @@
+class @beta Efl.Ui.Radio_Group_Impl extends Efl.Object implements Efl.Ui.Radio_Group
+{
+ implements {
+ class.constructor;
+ Efl.Object.destructor;
+ Efl.Ui.Radio_Group.selected_object {get; set;}
+ Efl.Ui.Radio_Group.selected_value {get; set;}
+ Efl.Ui.Radio_Group.register;
+ Efl.Ui.Radio_Group.unregister;
+ }
+}
diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build
index d3c418144d..3cd4f68b6b 100644
--- a/src/lib/elementary/meson.build
+++ b/src/lib/elementary/meson.build
@@ -57,6 +57,9 @@ pub_eo_files = [
'efl_ui_panes.eo',
'efl_ui_progressbar.eo',
'efl_ui_radio.eo',
+ 'efl_ui_radio_group.eo',
+ 'efl_ui_radio_box.eo',
+ 'efl_ui_radio_group_impl.eo',
'efl_ui_slider.eo',
'efl_ui_slider_interval.eo',
'efl_ui_spin.eo',
@@ -813,6 +816,8 @@ elementary_src = [
'elm_prefs_data.c',
'efl_ui_progressbar.c',
'efl_ui_radio.c',
+ 'efl_ui_radio_group_impl.c',
+ 'efl_ui_radio_box.c',
'elm_route.c',
'elm_scroller.c',
'elm_segment_control.c',
diff --git a/src/tests/elementary/efl_ui_suite.c b/src/tests/elementary/efl_ui_suite.c
index 6fab5d7003..66395696bc 100644
--- a/src/tests/elementary/efl_ui_suite.c
+++ b/src/tests/elementary/efl_ui_suite.c
@@ -27,6 +27,7 @@ static const Efl_Test_Case etc[] = {
{ "efl_ui_widget", efl_ui_test_widget },
{ "efl_ui_active_view", efl_ui_test_active_view},
{ "efl_ui_check", efl_ui_test_check },
+ { "efl_ui_radio_group", efl_ui_test_radio_group },
{ NULL, NULL }
};
diff --git a/src/tests/elementary/efl_ui_suite.h b/src/tests/elementary/efl_ui_suite.h
index 472493818a..b5aec1b098 100644
--- a/src/tests/elementary/efl_ui_suite.h
+++ b/src/tests/elementary/efl_ui_suite.h
@@ -36,6 +36,7 @@ void efl_ui_model(TCase *tc);
void efl_ui_test_widget(TCase *tc);
void efl_ui_test_active_view(TCase *tc);
void efl_ui_test_check(TCase *tc);
+void efl_ui_test_radio_group(TCase *tc);
void loop_timer_interval_set(Eo *obj, double in);
diff --git a/src/tests/elementary/efl_ui_test_radio_group.c b/src/tests/elementary/efl_ui_test_radio_group.c
new file mode 100644
index 0000000000..4c515bf140
--- /dev/null
+++ b/src/tests/elementary/efl_ui_test_radio_group.c
@@ -0,0 +1,266 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Efl_Ui.h>
+#include "efl_ui_suite.h"
+#include "suite_helpers.h"
+
+static Eo *win, *radio_group;
+
+int state_values = 0;
+
+typedef struct {
+ int called;
+ int value;
+} Group_Changed_Called;
+
+Group_Changed_Called changed_evt;
+
+#define RESET_EVT \
+ changed_evt.called = 0; \
+ changed_evt.value = 200000000;
+
+#define EXPECT_EVT(c, v) \
+ ck_assert_int_eq(changed_evt.called, c); \
+ ck_assert_int_eq(changed_evt.value, v);
+
+static void
+_group_value_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+ changed_evt.called ++;
+ changed_evt.value = *((int*)ev->info);
+}
+
+static void
+check_setup()
+{
+ win = win_add();
+ radio_group = efl_new(EFL_UI_RADIO_GROUP_IMPL_CLASS, NULL);
+ efl_event_callback_add(radio_group, EFL_UI_RADIO_GROUP_EVENT_VALUE_CHANGED, _group_value_changed_cb, NULL);
+}
+
+static void
+check_teardown()
+{
+ efl_unref(radio_group);
+ if (win)
+ {
+ efl_del(win);
+ win = NULL;
+ }
+}
+
+static Eo*
+radio(void)
+{
+ state_values += 1;
+ return efl_add(EFL_UI_RADIO_CLASS, win, efl_ui_radio_state_value_set(efl_added, state_values));
+}
+
+EFL_START_TEST(active_selected_value_setting)
+{
+ Eo *r1 = radio();
+ Eo *r2 = radio();
+
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r1);
+ efl_ui_radio_group_register(radio_group, r2);
+
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+
+ RESET_EVT
+ efl_ui_radio_group_selected_value_set(radio_group, 1);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r1);
+ ck_assert_int_eq(efl_ui_check_selected_get(r1), EINA_TRUE);
+ ck_assert_int_eq(efl_ui_check_selected_get(r2), EINA_FALSE);
+ EXPECT_EVT(1,1);
+
+ RESET_EVT
+ efl_ui_radio_group_selected_object_set(radio_group, r2);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 2);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r2);
+ ck_assert_int_eq(efl_ui_check_selected_get(r1), EINA_FALSE);
+ ck_assert_int_eq(efl_ui_check_selected_get(r2), EINA_TRUE);
+ EXPECT_EVT(1,2);
+}
+EFL_END_TEST
+
+EFL_START_TEST(active_selection_setting)
+{
+ Eo *r1 = radio();
+ Eo *r2 = radio();
+
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r1);
+ efl_ui_radio_group_register(radio_group, r2);
+
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+
+ RESET_EVT
+ efl_ui_radio_group_selected_object_set(radio_group, r1);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r1);
+ ck_assert_int_eq(efl_ui_check_selected_get(r1), EINA_TRUE);
+ ck_assert_int_eq(efl_ui_check_selected_get(r2), EINA_FALSE);
+ EXPECT_EVT(1,1);
+
+ RESET_EVT
+ efl_ui_radio_group_selected_object_set(radio_group, r2);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 2);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r2);
+ ck_assert_int_eq(efl_ui_check_selected_get(r1), EINA_FALSE);
+ ck_assert_int_eq(efl_ui_check_selected_get(r2), EINA_TRUE);
+ EXPECT_EVT(1,2);
+}
+EFL_END_TEST
+
+EFL_START_TEST(active_selection_object_setting)
+{
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r);
+ efl_ui_radio_group_register(radio_group, radio());
+
+ RESET_EVT
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+ efl_ui_check_selected_set(r, EINA_TRUE);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r);
+ EXPECT_EVT(1,1);
+}
+EFL_END_TEST
+
+EFL_START_TEST(unregister_setted)
+{
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r);
+ efl_ui_radio_group_register(radio_group, radio());
+
+ RESET_EVT
+ efl_ui_radio_group_selected_object_set(radio_group, r);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r);
+ EXPECT_EVT(1,1);
+ RESET_EVT
+ efl_ui_radio_group_unregister(radio_group, r);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+ EXPECT_EVT(1,-1);
+}
+EFL_END_TEST
+
+EFL_START_TEST(delete_setted)
+{
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r);
+ efl_ui_radio_group_register(radio_group, radio());
+ RESET_EVT
+ efl_ui_radio_group_selected_object_set(radio_group, r);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r);
+ EXPECT_EVT(1,1);
+ RESET_EVT
+ efl_del(r);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+ EXPECT_EVT(1,-1);
+}
+EFL_END_TEST
+
+EFL_START_TEST(external_setting)
+{
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, radio());
+ efl_ui_radio_group_register(radio_group, r);
+ efl_ui_radio_group_register(radio_group, radio());
+ RESET_EVT
+ efl_ui_check_selected_set(r, EINA_TRUE);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), 1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), r);
+ EXPECT_EVT(1,1);
+ RESET_EVT
+ efl_ui_check_selected_set(r, EINA_FALSE);
+ ck_assert_int_eq(efl_ui_radio_group_selected_value_get(radio_group), -1);
+ ck_assert_ptr_eq(efl_ui_radio_group_selected_object_get(radio_group), NULL);
+ EXPECT_EVT(1,-1);
+}
+EFL_END_TEST
+
+EFL_START_TEST(pairwise_different_error)
+{
+ Eo *r = radio();
+ Eo *r1 = radio();
+ Eo *r2 = radio();
+ efl_ui_radio_state_value_set(r, -1);
+ efl_ui_radio_state_value_set(r1, 14);
+ efl_ui_radio_state_value_set(r2, 14);
+ EXPECT_ERROR_START;
+ efl_ui_radio_group_register(radio_group, r);
+ EXPECT_ERROR_END;
+ efl_ui_radio_group_register(radio_group, r1);
+ EXPECT_ERROR_START;
+ efl_ui_radio_group_register(radio_group, r2);
+ EXPECT_ERROR_END;
+}
+EFL_END_TEST
+
+EFL_START_TEST(group_double_registeration)
+{
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, r);
+ EXPECT_ERROR_START;
+ efl_ui_radio_group_register(radio_group, r);
+ EXPECT_ERROR_END;
+}
+EFL_END_TEST
+
+EFL_START_TEST(group_registeration_in_two_groups)
+{
+ Eo *radio_group2 = efl_new(EFL_UI_RADIO_GROUP_IMPL_CLASS, NULL);
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, r);
+ EXPECT_ERROR_START;
+ efl_ui_radio_group_register(radio_group2, r);
+ EXPECT_ERROR_END;
+}
+EFL_END_TEST
+
+EFL_START_TEST(group_invalid_value)
+{
+ Eo *radio_group2 = efl_new(EFL_UI_RADIO_GROUP_IMPL_CLASS, NULL);
+ Eo *r = radio();
+ efl_ui_radio_group_register(radio_group, r);
+ EXPECT_ERROR_START;
+ efl_ui_radio_group_selected_value_set(radio_group2, 1000);
+ EXPECT_ERROR_END;
+}
+EFL_END_TEST
+
+void efl_ui_test_radio_group(TCase *tc)
+{
+ tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown);
+ tcase_add_checked_fixture(tc, check_setup, check_teardown);
+ tcase_add_test(tc, active_selected_value_setting);
+ tcase_add_test(tc, active_selection_setting);
+ tcase_add_test(tc, active_selection_object_setting);
+ tcase_add_test(tc, pairwise_different_error);
+ tcase_add_test(tc, group_double_registeration);
+ tcase_add_test(tc, group_registeration_in_two_groups);
+ tcase_add_test(tc, unregister_setted);
+ tcase_add_test(tc, external_setting);
+ tcase_add_test(tc, delete_setted);
+ tcase_add_test(tc, group_invalid_value);
+}
diff --git a/src/tests/elementary/meson.build b/src/tests/elementary/meson.build
index 735ba0a829..43b3ca1cfe 100644
--- a/src/tests/elementary/meson.build
+++ b/src/tests/elementary/meson.build
@@ -140,6 +140,7 @@ efl_ui_suite_src = [
'efl_ui_test_widget.c',
'efl_ui_test_active_view.c',
'efl_ui_test_check.c',
+ 'efl_ui_test_radio_group.c',
]
efl_ui_suite = executable('efl_ui_suite',