From e776f5f0d7b6292d32c29ccb4fffe3f20063d53d Mon Sep 17 00:00:00 2001 From: Xavi Artigas Date: Tue, 2 Jul 2019 14:40:06 +0200 Subject: [PATCH] Efl.Ui.Format revamp This class helps widgets which contain a numerical value and must display it, like Progressbar (units label), Spin, Spin_Button, Slider (both units and popup labels, in legacy), Tags (when in shrunk mode) or Calendar (year_month label). Previously this was a mix of interface and mixin: widgets had to support setting a formatting func, and the mixin offered support for formatting strings, by setting an internal formatting func. On top of that, the spinner widget supported "special values", a list of values that should be shown as certain strings instead. This has now been simplified and unified: Widgets including this mixin can use the formatted_value_get() method which accepts an Eina_Value and returns a string. Thats's it. The mixin adds three properties to the widget (format_values, format_func and format_string) which users can use to tailor formatting. The widget does not need to know which method has been used, it just retrieves the resulting string. This removes a lot of duplicated widget code, and adds functionality which was missing before. For example, all widgets support passing a list of values now. Widgets must implement the apply_formatted_value() method so they are notified of changes in the format and they can redraw anything they need. Tests have been added to the Elementary Spec suite for all cases. Legacy widgets behavior has not been modified, although a few needed some code changes. --- src/bin/elementary/test_calendar.c | 9 +- src/bin/elementary/test_ui_progressbar.c | 7 +- src/bin/elementary/test_ui_spin.c | 2 +- src/bin/elementary/test_ui_spin_button.c | 13 +- src/bin/elementary/test_ui_tags.c | 2 +- .../elementary/calendar_cxx_example_02.cc | 8 +- src/lib/efl/Efl.h | 1 - src/lib/efl/interfaces/efl_ui_format.c | 169 --------- src/lib/efl/interfaces/efl_ui_format.eo | 42 --- src/lib/efl/interfaces/meson.build | 2 - src/lib/elementary/Efl_Ui.h | 1 + src/lib/elementary/efl_ui_calendar.c | 96 +---- src/lib/elementary/efl_ui_calendar.eo | 3 +- src/lib/elementary/efl_ui_calendar_private.h | 8 +- src/lib/elementary/efl_ui_format.c | 345 ++++++++++++++++++ src/lib/elementary/efl_ui_format.eo | 149 ++++++++ src/lib/elementary/efl_ui_progressbar.c | 57 ++- src/lib/elementary/efl_ui_progressbar.eo | 2 +- .../elementary/efl_ui_progressbar_private.h | 3 - src/lib/elementary/efl_ui_spin.c | 230 +----------- src/lib/elementary/efl_ui_spin.eo | 31 +- src/lib/elementary/efl_ui_spin_button.c | 57 +-- src/lib/elementary/efl_ui_spin_private.h | 18 - src/lib/elementary/efl_ui_tags.c | 67 +--- src/lib/elementary/efl_ui_tags.eo | 2 +- src/lib/elementary/efl_ui_tags_private.h | 5 - src/lib/elementary/elm_slider.c | 49 ++- src/lib/elementary/elm_slider_eo.c | 6 +- .../elementary/elm_slider_part_indicator_eo.c | 8 +- .../elementary/elm_widget_multibuttonentry.h | 2 +- src/lib/elementary/elm_widget_slider.h | 4 +- src/lib/elementary/meson.build | 2 + src/tests/elementary/spec/README | 1 + src/tests/elementary/spec/efl_test_format.c | 174 +++++++++ src/tests/elementary/spec/efl_ui_spec_suite.h | 1 + src/tests/elementary/spec/meson.build | 1 + 36 files changed, 826 insertions(+), 751 deletions(-) delete mode 100644 src/lib/efl/interfaces/efl_ui_format.c delete mode 100644 src/lib/efl/interfaces/efl_ui_format.eo create mode 100644 src/lib/elementary/efl_ui_format.c create mode 100644 src/lib/elementary/efl_ui_format.eo create mode 100644 src/tests/elementary/spec/efl_test_format.c diff --git a/src/bin/elementary/test_calendar.c b/src/bin/elementary/test_calendar.c index 9a5de374ee..b3f54f5ee7 100644 --- a/src/bin/elementary/test_calendar.c +++ b/src/bin/elementary/test_calendar.c @@ -405,17 +405,18 @@ _cal_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev) max_date.tm_year + 1900); } -static void +static Eina_Bool _cal_format_cb(void *data EINA_UNUSED, Eina_Strbuf *str, const Eina_Value value) { struct tm current_time; //return if the value type is other than EINA_VALUE_TYPE_TM if (eina_value_type_get(&value) != EINA_VALUE_TYPE_TM) - return; + return EINA_FALSE; eina_value_get(&value, ¤t_time); eina_strbuf_append_strftime(str, "<< %b %y >>", ¤t_time); + return EINA_TRUE; } void @@ -444,7 +445,7 @@ test_efl_ui_calendar(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void efl_ui_calendar_date_max_set(efl_added, max_date), efl_ui_calendar_date_set(efl_added, selected_date), efl_event_callback_add(efl_added, EFL_UI_CALENDAR_EVENT_CHANGED, _cal_changed_cb, NULL), - efl_ui_format_string_set(efl_added, "%b"), + efl_ui_format_string_set(efl_added, "%b", EFL_UI_FORMAT_STRING_TYPE_TIME), efl_pack(box, efl_added)); efl_add(EFL_UI_CALENDAR_CLASS, win, @@ -452,7 +453,7 @@ test_efl_ui_calendar(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void efl_ui_calendar_date_max_set(efl_added, max_date), efl_ui_calendar_date_set(efl_added, selected_date), efl_event_callback_add(efl_added, EFL_UI_CALENDAR_EVENT_CHANGED, _cal_changed_cb, NULL), - efl_ui_format_cb_set(efl_added, NULL, _cal_format_cb, NULL), + efl_ui_format_func_set(efl_added, NULL, _cal_format_cb, NULL), efl_pack(box, efl_added)); efl_gfx_entity_size_set(win, EINA_SIZE2D(300, 300)); diff --git a/src/bin/elementary/test_ui_progressbar.c b/src/bin/elementary/test_ui_progressbar.c index 3d50d0be61..5a1c705a03 100644 --- a/src/bin/elementary/test_ui_progressbar.c +++ b/src/bin/elementary/test_ui_progressbar.c @@ -119,7 +119,7 @@ _win_delete_req_cb(void *d, const Efl_Event *ev EINA_UNUSED) free(pd); } -static void +static Eina_Bool _custom_format_cb(void *data EINA_UNUSED, Eina_Strbuf *str, const Eina_Value value) { double v; @@ -129,6 +129,7 @@ _custom_format_cb(void *data EINA_UNUSED, Eina_Strbuf *str, const Eina_Value val else if (v < 75.f) eina_strbuf_append_printf(str, "Getting there..."); else if (v < 100.f) eina_strbuf_append_printf(str, "Almost done..."); else eina_strbuf_append_printf(str, "Done!"); + return EINA_TRUE; } static void @@ -204,7 +205,7 @@ test_ui_progressbar(void *data EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_inf efl_pack(bx, efl_added), efl_text_set(efl_added, "Custom string"), efl_ui_range_limits_set(efl_added, 0, 100), - efl_ui_format_string_set(efl_added, "%d rabbits"), + efl_ui_format_string_set(efl_added, "%d rabbits", EFL_UI_FORMAT_STRING_TYPE_SIMPLE), efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(250, 20)) ); @@ -212,7 +213,7 @@ test_ui_progressbar(void *data EINA_UNUSED, Eo *obj EINA_UNUSED, void *event_inf efl_pack(bx, efl_added), efl_text_set(efl_added, "Custom func"), efl_ui_range_limits_set(efl_added, 0, 100), - efl_ui_format_cb_set(efl_added, NULL, _custom_format_cb, NULL), + efl_ui_format_func_set(efl_added, NULL, _custom_format_cb, NULL), efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(250, 20)) ); diff --git a/src/bin/elementary/test_ui_spin.c b/src/bin/elementary/test_ui_spin.c index 1c5b86b531..f07a42778a 100644 --- a/src/bin/elementary/test_ui_spin.c +++ b/src/bin/elementary/test_ui_spin.c @@ -50,7 +50,7 @@ test_ui_spin(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_i efl_ui_range_limits_set(efl_added, 0, 10), efl_ui_range_value_set(efl_added, 6), efl_ui_range_step_set(efl_added, 2), - efl_ui_format_string_set(efl_added, "test %d"), + efl_ui_format_string_set(efl_added, "test %d", EFL_UI_FORMAT_STRING_TYPE_SIMPLE), efl_event_callback_add(efl_added, EFL_UI_SPIN_EVENT_CHANGED,_spin_changed_cb, NULL), efl_event_callback_add(efl_added, EFL_UI_SPIN_EVENT_MIN_REACHED,_spin_min_reached_cb, NULL), efl_event_callback_add(efl_added, EFL_UI_SPIN_EVENT_MAX_REACHED,_spin_max_reached_cb, NULL), diff --git a/src/bin/elementary/test_ui_spin_button.c b/src/bin/elementary/test_ui_spin_button.c index 52455e67ef..82a207f1b0 100644 --- a/src/bin/elementary/test_ui_spin_button.c +++ b/src/bin/elementary/test_ui_spin_button.c @@ -16,18 +16,12 @@ void test_ui_spin_button(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *win, *bx; - int i; - Eina_Array *array; - Efl_Ui_Spin_Special_Value values[12] = { + Efl_Ui_Format_Value special_values[] = { {1, "January"}, {2, "February"}, {3, "March"}, {4, "April"}, {5, "May"}, {6, "June"}, {7, "July"}, {8, "August"}, {9, "September"}, {10, "October"}, {11, "November"}, {12, "December"} }; - array = eina_array_new(sizeof(Efl_Ui_Spin_Special_Value)); - for (i = 0; i < NUM_OF_VALS; i++) - eina_array_push(array, &values[i]); - win = efl_add_ref(EFL_UI_WIN_CLASS, NULL, efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC), efl_text_set(efl_added, "Efl.Ui.Spin_Button"), @@ -49,7 +43,7 @@ test_ui_spin_button(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void * efl_add(EFL_UI_SPIN_BUTTON_CLASS, bx, efl_ui_range_limits_set(efl_added, -100.0, 100.0), efl_ui_range_value_set(efl_added, 0), - efl_ui_format_string_set(efl_added, "test float %0.2f"), + efl_ui_format_string_set(efl_added, "test float %0.2f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE), efl_ui_spin_button_editable_set(efl_added, EINA_FALSE), efl_pack(bx, efl_added)); @@ -57,10 +51,9 @@ test_ui_spin_button(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void * efl_ui_range_limits_set(efl_added, 1, 12), efl_ui_range_value_set(efl_added, 1), efl_ui_spin_button_editable_set(efl_added, EINA_FALSE), - efl_ui_spin_special_value_set(efl_added, array), + efl_ui_format_values_set(efl_added, EINA_C_ARRAY_ACCESSOR_NEW(special_values)), efl_ui_layout_orientation_set(efl_added, EFL_UI_LAYOUT_ORIENTATION_VERTICAL), efl_pack(bx, efl_added)); - eina_array_free(array); efl_gfx_entity_size_set(win, EINA_SIZE2D(180, 140)); } diff --git a/src/bin/elementary/test_ui_tags.c b/src/bin/elementary/test_ui_tags.c index 410c7b24cd..2ef7d6db2b 100644 --- a/src/bin/elementary/test_ui_tags.c +++ b/src/bin/elementary/test_ui_tags.c @@ -78,7 +78,7 @@ test_ui_tags(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_i efl_event_callback_add(efl_added, EFL_UI_TAGS_EVENT_ITEM_ADDED, _item_added_cb, array), efl_event_callback_add(efl_added, EFL_UI_TAGS_EVENT_ITEM_DELETED, _item_deleted_cb, array), efl_text_set(efl_added, "To :"), - efl_ui_format_string_set(efl_added, "+ %d items"), + efl_ui_format_string_set(efl_added, "+ %d items", EFL_UI_FORMAT_STRING_TYPE_SIMPLE), elm_object_part_content_set(layout, "multibuttonentry", efl_added)); efl_add(EFL_UI_BUTTON_CLASS, layout, diff --git a/src/examples/elementary/calendar_cxx_example_02.cc b/src/examples/elementary/calendar_cxx_example_02.cc index 60fb796bf1..e666ae844a 100644 --- a/src/examples/elementary/calendar_cxx_example_02.cc +++ b/src/examples/elementary/calendar_cxx_example_02.cc @@ -26,17 +26,19 @@ struct appData auto wcal(cal._get_wref()); // FIXME: How does one figure out the argument types for the function? - auto cb_a = std::bind([=]( + auto cb_a = std::bind([]( efl::eina::strbuf_wrapper& sb, - efl::eina::value_view const& value) { + efl::eina::value_view const& value) -> bool { try { sb.append_strftime("%b. %y", efl::eina::get(value)); } catch (std::system_error const&) { sb.append(value.to_string()); } std::cout << "Month: " << std::string(sb) << std::endl; + return true; }, _1, _2); - cal.format_cb_set(cb_a); + // FIXME XAR: I broke this and I do not know how to fix it + // cal.format_func_set(cb_a); } void destroy() { diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h index aa88196720..5cbd1c2f96 100644 --- a/src/lib/efl/Efl.h +++ b/src/lib/efl/Efl.h @@ -147,7 +147,6 @@ typedef Efl_Gfx_Path_Command_Type Efl_Gfx_Path_Command; #include "interfaces/efl_ui_property_bind.eo.h" #include "interfaces/efl_ui_factory.eo.h" #include "interfaces/efl_ui_factory_bind.eo.h" -#include "interfaces/efl_ui_format.eo.h" #include "interfaces/efl_cached_item.eo.h" /* Observable interface */ diff --git a/src/lib/efl/interfaces/efl_ui_format.c b/src/lib/efl/interfaces/efl_ui_format.c deleted file mode 100644 index f6f1b811e2..0000000000 --- a/src/lib/efl/interfaces/efl_ui_format.c +++ /dev/null @@ -1,169 +0,0 @@ -#include "config.h" -#include "Efl.h" - -#define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) -#define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) - -typedef enum _Format_Type -{ - FORMAT_TYPE_INVALID, - FORMAT_TYPE_DOUBLE, - FORMAT_TYPE_INT, - FORMAT_TYPE_STRING, - FORMAT_TYPE_STATIC -} Format_Type; - -typedef struct -{ - const char *template; - Format_Type format_type; -} Efl_Ui_Format_Data; - -static Eina_Bool -_is_valid_digit(char x) -{ - return ((x >= '0' && x <= '9') || (x == '.')) ? EINA_TRUE : EINA_FALSE; -} - -static Format_Type -_format_string_check(const char *fmt) -{ - const char *itr; - Eina_Bool found = EINA_FALSE; - Format_Type ret_type = FORMAT_TYPE_STATIC; - - for (itr = fmt; *itr; itr++) - { - if (itr[0] != '%') continue; - if (itr[1] == '%') - { - itr++; - if (ret_type == FORMAT_TYPE_STATIC) - ret_type = FORMAT_TYPE_STRING; - continue; - } - - if (!found) - { - found = EINA_TRUE; - for (itr++; *itr; itr++) - { - // FIXME: This does not properly support int64 or unsigned. - if ((*itr == 'd') || (*itr == 'u') || (*itr == 'i') || - (*itr == 'o') || (*itr == 'x') || (*itr == 'X')) - { - ret_type = FORMAT_TYPE_INT; - break; - } - else if ((*itr == 'f') || (*itr == 'F')) - { - ret_type = FORMAT_TYPE_DOUBLE; - break; - } - else if (*itr == 's') - { - ret_type = FORMAT_TYPE_STRING; - break; - } - else if (_is_valid_digit(*itr)) - { - continue; - } - else - { - ERR("Format string '%s' has unknown format element '%c' in format. It must have one format element of type 's', 'f', 'F', 'd', 'u', 'i', 'o', 'x' or 'X'", fmt, *itr); - found = EINA_FALSE; - break; - } - } - if (!(*itr)) break; - } - else - { - ret_type = FORMAT_TYPE_INVALID; - break; - } - } - - if (ret_type == FORMAT_TYPE_INVALID) - { - ERR("Format string '%s' is invalid. It must have one and only one format element of type 's', 'f', 'F', 'd', 'u', 'i', 'o', 'x' or 'X'", fmt); - } - return ret_type; -} - -static void -_default_format_cb(void *data, Eina_Strbuf *str, const Eina_Value value) -{ - Efl_Ui_Format_Data *sd = data; - Eina_Value copy; - - if (sd->format_type == FORMAT_TYPE_DOUBLE) - { - double v = 0.0; - eina_value_setup(©, EINA_VALUE_TYPE_DOUBLE); - eina_value_convert(&value, ©); - eina_value_get(©, &v); - eina_strbuf_append_printf(str, sd->template, v); - eina_value_flush(©); - } - else if (sd->format_type == FORMAT_TYPE_INT) - { - int v = 0; - eina_value_setup(©, EINA_VALUE_TYPE_INT); - eina_value_convert(&value, ©); - eina_value_get(©, &v); - eina_strbuf_append_printf(str, sd->template, v); - eina_value_flush(©); - } - else if (sd->format_type == FORMAT_TYPE_STRING) - { - char *v = eina_value_to_string(&value); - eina_strbuf_append_printf(str, sd->template, v); - free(v); - } - else if (sd->format_type == FORMAT_TYPE_STATIC) - { - eina_strbuf_append(str, sd->template); - } - else - { - // Error: Discard format string and just print value. - DBG("Could not guess value type in format string: '%s'", sd->template); - char *v = eina_value_to_string(&value); - eina_strbuf_append(str, v); - free(v); - } -} - -static void -_default_format_free_cb(void *data) -{ - Efl_Ui_Format_Data *sd = data; - - if (sd && sd->template) - { - eina_stringshare_del(sd->template); - sd->template = NULL; - } -} - -EOLIAN static void -_efl_ui_format_format_string_set(Eo *obj, Efl_Ui_Format_Data *sd, const char *template) -{ - if (!template) return; - - eina_stringshare_replace(&sd->template, template); - sd->format_type = _format_string_check(sd->template); - - efl_ui_format_cb_set(obj, sd, _default_format_cb, _default_format_free_cb); -} - -EOLIAN static const char * -_efl_ui_format_format_string_get(const Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *sd) -{ - return sd->template; -} - -#include "interfaces/efl_ui_format.eo.c" - diff --git a/src/lib/efl/interfaces/efl_ui_format.eo b/src/lib/efl/interfaces/efl_ui_format.eo deleted file mode 100644 index c7b6aba841..0000000000 --- a/src/lib/efl/interfaces/efl_ui_format.eo +++ /dev/null @@ -1,42 +0,0 @@ -import eina_types; - -function @beta Efl.Ui.Format_Func_Cb { - [[Function pointer for format function hook]] - params { - @in str: strbuf; [[the formated string to be appended by user.]] - @in value: const(any_value); [[The @Eina.Value passed by $obj.]] - } -}; - -mixin @beta Efl.Ui.Format -{ - [[interface class for format_func]] - methods { - @property format_cb { - set @pure_virtual { - [[Set the format function pointer to format the string. - ]] - } - values { - func: Efl.Ui.Format_Func_Cb; [[The format function callback]] - } - } - @property format_string { - [[Control the format string for a given units label - - If $NULL is passed to $format, it will hide $obj's units - area completely. If not, it'll set the format - string for the units label text. The units label is - provided as a floating point value, so the units text can display - at most one floating point value. Note that the units label is - optional. Use a format string such as "%1.2f meters" for example. - - Note: The default format string is an integer percentage, - as in $"%.0f %%". - ]] - values { - units: string; [[The format string for $obj's units label.]] - } - } - } -} diff --git a/src/lib/efl/interfaces/meson.build b/src/lib/efl/interfaces/meson.build index e3baa55571..d09a24c255 100644 --- a/src/lib/efl/interfaces/meson.build +++ b/src/lib/efl/interfaces/meson.build @@ -94,7 +94,6 @@ pub_eo_files = [ 'efl_observer.eo', 'efl_observable.eo', 'efl_ui_autorepeat.eo', - 'efl_ui_format.eo', 'efl_gfx_color_class.eo', 'efl_gfx_text_class.eo', 'efl_gfx_size_class.eo', @@ -166,7 +165,6 @@ efl_src += files([ 'efl_io_queue.c', 'efl_observer.c', 'efl_file.c', - 'efl_ui_format.c', 'efl_ui_layout_orientable_readonly.c', 'efl_text_markup_util.c', ]) diff --git a/src/lib/elementary/Efl_Ui.h b/src/lib/elementary/Efl_Ui.h index 2647362ba5..dc13d16a65 100644 --- a/src/lib/elementary/Efl_Ui.h +++ b/src/lib/elementary/Efl_Ui.h @@ -246,6 +246,7 @@ typedef Eo Efl_Ui_Active_View_Indicator; # include # include # include +# include /** * Initialize Elementary diff --git a/src/lib/elementary/efl_ui_calendar.c b/src/lib/elementary/efl_ui_calendar.c index 200566139b..7fb174273f 100644 --- a/src/lib/elementary/efl_ui_calendar.c +++ b/src/lib/elementary/efl_ui_calendar.c @@ -5,6 +5,7 @@ #define EFL_UI_FOCUS_COMPOSITION_PROTECTED #define EFL_UI_FOCUS_OBJECT_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED +#define EFL_UI_FORMAT_PROTECTED #include #include "elm_priv.h" @@ -167,37 +168,17 @@ _disable(Efl_Ui_Calendar_Data *sd, static void _set_month_year(Efl_Ui_Calendar_Data *sd) { + Eina_Strbuf *strbuf = eina_strbuf_new(); + Eina_Value val; sd->filling = EINA_TRUE; - if (sd->format_cb) - { - Eina_Value val; - const char *buf; - - eina_value_setup(&val, EINA_VALUE_TYPE_TM); - eina_value_set(&val, sd->shown_date); - eina_strbuf_reset(sd->format_strbuf); - sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); - buf = eina_strbuf_string_get(sd->format_strbuf); - eina_value_flush(&val); - - if (buf) - elm_layout_text_set(sd->obj, "month_text", buf); - else - elm_layout_text_set(sd->obj, "month_text", ""); - } - else - { - char *buf; - buf = eina_strftime(E_("%B %Y"), &sd->shown_date); - if (buf) - { - elm_layout_text_set(sd->obj, "month_text", buf); - free(buf); - } - else elm_layout_text_set(sd->obj, "month_text", ""); - } + eina_value_setup(&val, EINA_VALUE_TYPE_TM); + eina_value_set(&val, sd->shown_date); + efl_ui_format_formatted_value_get(sd->obj, strbuf, val); + elm_layout_text_set(sd->obj, "month_text", eina_strbuf_string_get(strbuf)); + eina_value_flush(&val); + eina_strbuf_free(strbuf); sd->filling = EINA_FALSE; } @@ -841,9 +822,6 @@ _efl_ui_calendar_efl_object_destructor(Eo *obj, Efl_Ui_Calendar_Data *sd) ecore_timer_del(sd->update_timer); - efl_ui_format_cb_set(obj, NULL, NULL, NULL); - eina_strbuf_free(sd->format_strbuf); - for (i = 0; i < ELM_DAY_LAST; i++) eina_stringshare_del(sd->weekdays[i]); @@ -919,7 +897,6 @@ _efl_ui_calendar_constructor_internal(Eo *obj, Efl_Ui_Calendar_Data *priv) priv->today_it = -1; priv->selected_it = -1; priv->first_day_it = -1; - priv->format_cb = NULL; edje_object_signal_callback_add (wd->resize_obj, "efl,action,selected", "*", @@ -1146,64 +1123,11 @@ _efl_ui_calendar_date_get(const Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) } EOLIAN static void -_efl_ui_calendar_efl_ui_format_format_cb_set(Eo *obj, Efl_Ui_Calendar_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) +_efl_ui_calendar_efl_ui_format_apply_formatted_value(Eo *obj, Efl_Ui_Calendar_Data *pd EINA_UNUSED) { - if ((sd->format_cb_data == func_data) && (sd->format_cb == func)) - return; - - if (sd->format_cb_data && sd->format_free_cb) - sd->format_free_cb(sd->format_cb_data); - - sd->format_cb = func; - sd->format_cb_data = func_data; - sd->format_free_cb = func_free_cb; - if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); - evas_object_smart_changed(obj); } -static void -_calendar_format_cb(void *data, Eina_Strbuf *str, const Eina_Value value) -{ - Efl_Ui_Calendar_Data *sd = data; - const Eina_Value_Type *type = eina_value_type_get(&value); - struct tm v; - - if (type == EINA_VALUE_TYPE_TM) - { - eina_value_get(&value, &v); - eina_strbuf_append_strftime(str, sd->format_template, &v); - } -} - -static void -_calendar_format_free_cb(void *data) -{ - Efl_Ui_Calendar_Data *sd = data; - - if (sd && sd->format_template) - { - eina_stringshare_del(sd->format_template); - sd->format_template = NULL; - } -} - -EOLIAN static void -_efl_ui_calendar_efl_ui_format_format_string_set(Eo *obj, Efl_Ui_Calendar_Data *sd, const char *template) -{ - if (!template) return; - - eina_stringshare_replace(&sd->format_template, template); - - efl_ui_format_cb_set(obj, sd, _calendar_format_cb, _calendar_format_free_cb); -} - -EOLIAN static const char * -_efl_ui_calendar_efl_ui_format_format_string_get(const Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) -{ - return sd->format_template; -} - EOLIAN static void _efl_ui_calendar_first_day_of_week_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Ui_Calendar_Weekday day) { diff --git a/src/lib/elementary/efl_ui_calendar.eo b/src/lib/elementary/efl_ui_calendar.eo index 888319d382..586535f500 100644 --- a/src/lib/elementary/efl_ui_calendar.eo +++ b/src/lib/elementary/efl_ui_calendar.eo @@ -111,8 +111,7 @@ class @beta Efl.Ui.Calendar extends Efl.Ui.Layout_Base implements Efl.Ui.Focus.C Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Widget.widget_input_event_handler; Efl.Access.Widget.Action.elm_actions { get; } - Efl.Ui.Format.format_cb { set; } - Efl.Ui.Format.format_string { set; get;} + Efl.Ui.Format.apply_formatted_value; } events { changed: void; [[Emitted when the selected date in the calendar is changed]] diff --git a/src/lib/elementary/efl_ui_calendar_private.h b/src/lib/elementary/efl_ui_calendar_private.h index 78446c612e..3d80b89305 100644 --- a/src/lib/elementary/efl_ui_calendar_private.h +++ b/src/lib/elementary/efl_ui_calendar_private.h @@ -39,15 +39,9 @@ struct _Efl_Ui_Calendar_Data Evas_Object *month_access; Eo *items[42]; - Efl_Ui_Calendar_Weekday first_week_day; + Efl_Ui_Calendar_Weekday first_week_day; unsigned char first_day_it; - const char *format_template; - Efl_Ui_Format_Func_Cb format_cb; - Eina_Free_Cb format_free_cb; - void *format_cb_data; - Eina_Strbuf *format_strbuf; - Eina_Bool selected : 1; Eina_Bool filling : 1; Eina_Bool weekdays_set : 1; diff --git a/src/lib/elementary/efl_ui_format.c b/src/lib/elementary/efl_ui_format.c new file mode 100644 index 0000000000..beb3945f43 --- /dev/null +++ b/src/lib/elementary/efl_ui_format.c @@ -0,0 +1,345 @@ +#define EFL_UI_FORMAT_PROTECTED 1 + +#include "config.h" +#include "Efl_Ui.h" +#include "elm_priv.h" /* To be able to use elm_widget_is_legacy() */ + +typedef enum _Format_Type +{ + /* When a format string is used, it is parsed to find out the expected data type */ + FORMAT_TYPE_INVALID, /* Format description not understood */ + FORMAT_TYPE_DOUBLE, /* double */ + FORMAT_TYPE_INT, /* int */ + FORMAT_TYPE_TM, /* struct tm, for time and date values */ + FORMAT_TYPE_STRING, /* const char* */ + FORMAT_TYPE_STATIC /* No value is passed, the format string IS the formatted output */ +} Format_Type; + +typedef struct +{ + Efl_Ui_Format_Func format_func; /* User-supplied formatting function */ + void *format_func_data; /* User data for the above function */ + Eina_Free_Cb format_func_free; /* How to free the above data */ + + Eina_Inarray *format_values; /* Array of formatting values, owned by us */ + + const char *format_string; /* User-supplied formatting string, stringshare */ + Format_Type format_string_type; /* Type of data expected in the above string */ +} Efl_Ui_Format_Data; + +static Eina_Bool +_is_valid_digit(char x) +{ + return ((x >= '0' && x <= '9') || (x == '.')) ? EINA_TRUE : EINA_FALSE; +} + +static Format_Type +_format_string_check(const char *fmt, Efl_Ui_Format_String_Type type) +{ + const char *itr; + Eina_Bool found = EINA_FALSE; + Format_Type ret_type = FORMAT_TYPE_STATIC; + + if (type == EFL_UI_FORMAT_STRING_TYPE_TIME) return FORMAT_TYPE_TM; + + for (itr = fmt; *itr; itr++) + { + if (itr[0] != '%') continue; + if (itr[1] == '%') + { + itr++; + if (ret_type == FORMAT_TYPE_STATIC) + ret_type = FORMAT_TYPE_STRING; + continue; + } + + if (!found) + { + found = EINA_TRUE; + for (itr++; *itr; itr++) + { + // FIXME: This does not properly support int64 or unsigned. + if ((*itr == 'd') || (*itr == 'u') || (*itr == 'i') || + (*itr == 'o') || (*itr == 'x') || (*itr == 'X')) + { + ret_type = FORMAT_TYPE_INT; + break; + } + else if ((*itr == 'f') || (*itr == 'F')) + { + ret_type = FORMAT_TYPE_DOUBLE; + break; + } + else if (*itr == 's') + { + ret_type = FORMAT_TYPE_STRING; + break; + } + else if (_is_valid_digit(*itr)) + { + continue; + } + else + { + ERR("Format string '%s' has unknown format element '%c' in format. It must have one format element of type 's', 'f', 'F', 'd', 'u', 'i', 'o', 'x' or 'X'", fmt, *itr); + found = EINA_FALSE; + break; + } + } + if (!(*itr)) break; + } + else + { + ret_type = FORMAT_TYPE_INVALID; + break; + } + } + + if (ret_type == FORMAT_TYPE_INVALID) + { + ERR("Format string '%s' is invalid. It must have one and only one format element of type 's', 'f', 'F', 'd', 'u', 'i', 'o', 'x' or 'X'", fmt); + } + return ret_type; +} + +static Eina_Bool +_do_format_string(Efl_Ui_Format_Data *pd, Eina_Strbuf *str, const Eina_Value value) +{ + switch (pd->format_string_type) + { + case FORMAT_TYPE_DOUBLE: + { + double v = 0.0; + eina_value_double_convert(&value, &v); + eina_strbuf_append_printf(str, pd->format_string, v); + break; + } + case FORMAT_TYPE_INT: + { + int v = 0; + eina_value_int_convert(&value, &v); + eina_strbuf_append_printf(str, pd->format_string, v); + break; + } + case FORMAT_TYPE_STRING: + { + char *v = eina_value_to_string(&value); + eina_strbuf_append_printf(str, pd->format_string, v); + free(v); + break; + } + case FORMAT_TYPE_STATIC: + { + eina_strbuf_append(str, pd->format_string); + break; + } + case FORMAT_TYPE_TM: + { + struct tm v; + char *buf; + eina_value_get(&value, &v); + buf = eina_strftime(pd->format_string, &v); + eina_strbuf_append(str, buf); + free(buf); + break; + } + default: + return EINA_FALSE; + } + return EINA_TRUE; +} + +static Eina_Bool +_legacy_default_format_func(void *data, Eina_Strbuf *str, const Eina_Value value) +{ + if (!_do_format_string(data, str, value)) + { + /* Fallback to just printing the value if format string fails (legacy behavior) */ + char *v = eina_value_to_string(&value); + eina_strbuf_append(str, v); + free(v); + } + return EINA_TRUE; +} + +EOLIAN static void +_efl_ui_format_format_func_set(Eo *obj, Efl_Ui_Format_Data *pd, void *func_data, Efl_Ui_Format_Func func, Eina_Free_Cb func_free_cb) +{ + if (pd->format_func_free) + pd->format_func_free(pd->format_func_data); + pd->format_func = func; + pd->format_func_data = func_data; + pd->format_func_free = func_free_cb; + + if (efl_alive_get(obj)) + efl_ui_format_apply_formatted_value(obj); +} + +EOLIAN static Efl_Ui_Format_Func +_efl_ui_format_format_func_get(const Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *pd) +{ + return pd->format_func; +} + +static int +_value_compare(const Efl_Ui_Format_Value *val1, const Efl_Ui_Format_Value *val2) +{ + return val1->value - val2->value; +} + +EOLIAN static void +_efl_ui_format_format_values_set(Eo *obj, Efl_Ui_Format_Data *pd, Eina_Accessor *values) +{ + Efl_Ui_Format_Value v; + int i; + if (pd->format_values) + { + Efl_Ui_Format_Value *vptr; + /* Delete previous values array */ + EINA_INARRAY_FOREACH(pd->format_values, vptr) + { + eina_stringshare_del(vptr->text); + } + eina_inarray_free(pd->format_values); + pd->format_values = NULL; + } + if (values == NULL) + { + if (efl_alive_get(obj)) + efl_ui_format_apply_formatted_value(obj); + return; + } + + /* Copy the values to our internal array */ + pd->format_values = eina_inarray_new(sizeof(Efl_Ui_Format_Value), 4); + EINA_ACCESSOR_FOREACH(values, i, v) + { + Efl_Ui_Format_Value vcopy = { v.value, eina_stringshare_add(v.text) }; + eina_inarray_insert_sorted(pd->format_values, &vcopy, (Eina_Compare_Cb)_value_compare); + } + eina_accessor_free(values); + + if (efl_alive_get(obj)) + efl_ui_format_apply_formatted_value(obj); +} + +EOLIAN static Eina_Accessor * +_efl_ui_format_format_values_get(const Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *pd) +{ + if (!pd->format_values) return NULL; + return eina_inarray_accessor_new(pd->format_values); +} + +EOLIAN static void +_efl_ui_format_format_string_set(Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *sd, const char *string, Efl_Ui_Format_String_Type type) +{ + eina_stringshare_replace(&sd->format_string, string); + if (string) + sd->format_string_type = _format_string_check(sd->format_string, type); + else + sd->format_string_type = FORMAT_TYPE_INVALID; + + /* In legacy, setting the format string installs a default format func. + Some widgets then override the format_func_set method so we keep that behavior. */ + if (elm_widget_is_legacy(obj)) + efl_ui_format_func_set(obj, sd, _legacy_default_format_func, NULL); + + if (efl_alive_get(obj)) + efl_ui_format_apply_formatted_value(obj); +} + +EOLIAN static void +_efl_ui_format_format_string_get(const Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *sd, const char **string, Efl_Ui_Format_String_Type *type) +{ + if (string) *string = sd->format_string; + if (type) *type = sd->format_string_type == FORMAT_TYPE_TM ? + EFL_UI_FORMAT_STRING_TYPE_TIME : EFL_UI_FORMAT_STRING_TYPE_SIMPLE; +} + +EOLIAN static void +_efl_ui_format_formatted_value_get(Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *pd, Eina_Strbuf *str, const Eina_Value value) +{ + char *v; + eina_strbuf_reset(str); + if (pd->format_values) + { + /* Search in the format_values array if we have one */ + Efl_Ui_Format_Value v = { 0 }; + int ndx; + eina_value_int_convert(&value, &v.value); + ndx = eina_inarray_search_sorted(pd->format_values, &v, (Eina_Compare_Cb)_value_compare); + if (ndx > -1) { + Efl_Ui_Format_Value *entry = eina_inarray_nth(pd->format_values, ndx); + eina_strbuf_append(str, entry->text); + return; + } + } + if (pd->format_func) + { + /* If we have a formatting function, try to use it */ + if (pd->format_func(pd->format_func_data, str, value)) + return; + } + if (pd->format_string) + { + /* If we have a formatting string, use it */ + if (_do_format_string(pd, str, value)) + return; + } + + /* Fallback to just printing the value if everything else fails */ + v = eina_value_to_string(&value); + eina_strbuf_append(str, v); + free(v); +} + +EOLIAN static int +_efl_ui_format_decimal_places_get(Eo *obj EINA_UNUSED, Efl_Ui_Format_Data *pd) +{ + char result[16] = "0"; + const char *start; + + /* This method can only be called if a format_string has been supplied */ + if (!pd->format_string) return 0; + + start = strchr(pd->format_string, '%'); + while (start) + { + if (start[1] != '%') + { + start = strchr(start, '.'); + if (start) + start++; + break; + } + else + start = strchr(start + 2, '%'); + } + + if (start) + { + const char *p = strchr(start, 'f'); + + if ((p) && ((p - start) < 15)) + sscanf(start, "%[^f]", result); + } + + return atoi(result); +} + +EOLIAN static void +_efl_ui_format_efl_object_destructor(Eo *obj, Efl_Ui_Format_Data *pd EINA_UNUSED) +{ + /* Legacy widgets keep their own formatting data and have their own destructors */ + if (!elm_widget_is_legacy(obj)) + { + /* Otherwise, free formatting data */ + efl_ui_format_func_set(obj, NULL, NULL, NULL); + efl_ui_format_values_set(obj, NULL); + efl_ui_format_string_set(obj, NULL, 0); + } + efl_destructor(efl_super(obj, EFL_UI_FORMAT_MIXIN)); +} + +#include "efl_ui_format.eo.c" + diff --git a/src/lib/elementary/efl_ui_format.eo b/src/lib/elementary/efl_ui_format.eo new file mode 100644 index 0000000000..3858a79ef1 --- /dev/null +++ b/src/lib/elementary/efl_ui_format.eo @@ -0,0 +1,149 @@ +import eina_types; + +function @beta Efl.Ui.Format_Func +{ + [[A function taking an @Eina.Value and producing its textual representation. + See @Efl.Ui.Format.format_func. + ]] + params { + @in str: strbuf; [[Output formatted string. Its contents will be overwritten by this method.]] + @in value: const(any_value); [[The @Eina.Value to convert to text.]] + } + return: bool; [[Whether the conversion succeeded or not.]] +}; + +struct @beta Efl.Ui.Format_Value +{ + [[A value which should always be displayed as a specific text string. + See @Efl.Ui.Format.format_values. + ]] + value: int; [[Input value.]] + text: string; [[Text string to replace it.]] +} + +enum @beta Efl.Ui.Format_String_Type +{ + [[Type of formatting string.]] + simple, [[This is the simplest formatting mechanism, working pretty much like $printf. + Accepted formats are $s, $f, $F, $d, $u, $i, $o, $x and $X. + For example, "%1.2f meters", "%.0%%" or "%d items". + ]] + time [[A strftime-style string used to format date and time values. + For example, "%A" for the full name of the day or "%y" for the year as a decimal number + without a century (range 00 to 99). Note that these are not the $printf formats. + See the man page for the $strftime function for the complete list. + ]] +} + +mixin @beta Efl.Ui.Format requires Efl.Object +{ + [[Helper mixin that simplifies converting numerical values to text. + + A number of widgets represent a numerical value but display a text representation. + For example, an @Efl.Ui.Progressbar can hold the number 0.75 but display the string "75%", + or an @Efl.Ui.Spin can hold numbers 1 to 7, but display the strings "Monday" thru "Sunday". + + This conversion can be controlled through the @.format_func, @.format_values and @.format_string properties. + Only one of them needs to be set. When more than one is set @.format_values has the highest priority, followed by @.format_func and then @.format_string. + If one mechanism fails to produce a valid string the others will be tried (if provided) in descending + order of priority. + If no user-provided mechanism works, a fallback is used that just displays the value. + + Widgets including this mixin offer their users different properties to control how + @Eina.Value's are converted to text. + ]] + methods { + @property format_func { + [[User-provided function which takes care of converting an @Eina.Value into a text string. + The user is then completely in control of how the string is generated, but it is the + most cumbersome method to use. + If the conversion fails the other mechanisms will be tried, according to their priorities. + ]] + values { + func: Efl.Ui.Format_Func; [[User-provided formatting function.]] + } + } + + @property format_values { + [[User-provided list of values which are to be rendered using specific text strings. + This is more convenient to use than @.format_func and is perfectly suited for cases + where the strings make more sense than the numerical values. For example, weekday names + ("Monday", "Tuesday", ...) are friendlier than numbers 1 to 7. + If a value is not found in the list, the other mechanisms will be tried according to their priorities. + List members do not need to be in any particular order. They are sorted internally for + performance reasons. + ]] + values { + values: accessor; [[Accessor over a list of value-text pairs. + The method will dispose of the accessor, but not of + its contents. + For convenience, Eina offers a range of helper + methods to obtain accessors from Eina.Array, + Eina.List or even plain C arrays. + ]] + } + } + + @property format_string { + [[A user-provided, string used to format the numerical value. + + For example, "%1.2f meters", "%.0%%" or "%d items". + + This is the simplest formatting mechanism, working pretty much like $printf. + + Different format specifiers (the character after the %) are available, depending on the + $type used. Use @Efl.Ui.Format_String_Type.simple for simple numerical values and + @Efl.Ui.Format_String_Type.time for time and date values. + For instance, %d means "integer" when the first type is used, but it means "day of the month + as a decimal number" in the second. + + Pass $NULL to disable this mechanism. + ]] + values { + string: string; [[Formatting string containing regular characters and format specifiers.]] + type: Efl.Ui.Format_String_Type(Efl.Ui.Format_String_Type.simple); + [[Type of formatting string, which controls how the + different format specifiers are to be traslated.]] + } + } + + formatted_value_get @protected { + [[Internal method to be used by widgets including this mixin to perform the conversion + from the internal numerical value into the text representation (Users of these widgets + do not need to call this method). + + @.formatted_value_get uses any user-provided mechanism to perform the conversion, according to their + priorities, and implements a simple fallback if all mechanisms fail. + ]] + params { + @in str: strbuf; [[Output formatted string. Its contents will be overwritten by this method.]] + @in value: const(any_value); [[The @Eina.Value to convert to text.]] + } + } + + decimal_places_get @protected { + [[Internal method to be used by widgets including this mixin. + It can only be used when a @.format_string has been supplied, and it returns the number + of decimal places that the format string will produce for floating point values. + + For example, "%.2f" returns 2, and "%d" returns 0; + ]] + return: int; [[Number of decimal places, or 0 for non-floating point types.]] + } + + apply_formatted_value @pure_virtual @protected { + [[Internal method to be implemented by widgets including this mixin. + + The mixin will call this method to signal the widget that the formatting has changed + and therefore the current value should be converted and rendered again. + Widgets must typically call @.formatted_value_get and display the returned string. This + is something they are already doing (whenever the value changes, for example) so there + should be no extra code written to implement this method. + ]] + } + } + + implements { + Efl.Object.destructor; + } +} diff --git a/src/lib/elementary/efl_ui_progressbar.c b/src/lib/elementary/efl_ui_progressbar.c index dfe4b07fbb..637c54f8d6 100644 --- a/src/lib/elementary/efl_ui_progressbar.c +++ b/src/lib/elementary/efl_ui_progressbar.c @@ -6,6 +6,7 @@ #define ELM_LAYOUT_PROTECTED #define EFL_ACCESS_VALUE_PROTECTED #define EFL_PART_PROTECTED +#define EFL_UI_FORMAT_PROTECTED #include @@ -73,7 +74,7 @@ _units_set(Evas_Object *obj) { EFL_UI_PROGRESSBAR_DATA_GET(obj, sd); - if (sd->show_progress_label && sd->format_cb) + if (sd->show_progress_label) { Eina_Value val; @@ -84,8 +85,8 @@ _units_set(Evas_Object *obj) if (sd->is_legacy_format_string && !sd->is_legacy_format_cb) eina_value_set(&val, 100 * sd->val); - eina_strbuf_reset(sd->format_strbuf); - sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); + if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); + efl_ui_format_formatted_value_get(obj, sd->format_strbuf, val); eina_value_flush(&val); @@ -361,7 +362,7 @@ _efl_ui_progressbar_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Progressbar_Data free(group); - efl_ui_format_string_set(obj, "%.0f%%"); + efl_ui_format_string_set(obj, "%.0f%%", EFL_UI_FORMAT_STRING_TYPE_SIMPLE); priv->spacer = evas_object_rectangle_add(evas_object_evas_get(obj)); evas_object_color_set(priv->spacer, 0, 0, 0, 0); @@ -402,8 +403,8 @@ _efl_ui_progressbar_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Progressbar_Data } } - efl_ui_format_cb_set(obj, NULL, NULL, NULL); eina_strbuf_free(sd->format_strbuf); + sd->format_strbuf = NULL; efl_canvas_group_del(efl_super(obj, MY_CLASS)); } @@ -610,32 +611,6 @@ _efl_ui_progressbar_efl_ui_range_display_range_value_get(const Eo *obj, Efl_Ui_P return efl_ui_range_value_get(efl_part(obj, "efl.cur.progressbar")); } -EOLIAN static void -_efl_ui_progressbar_efl_ui_format_format_cb_set(Eo *obj, Efl_Ui_Progressbar_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) -{ - ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); - - if (sd->format_cb_data == func_data && sd->format_cb == func) - return; - - if (sd->format_cb_data && sd->format_free_cb) - sd->format_free_cb(sd->format_cb_data); - - sd->format_cb = func; - sd->format_cb_data = func_data; - sd->format_free_cb = func_free_cb; - if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); - - if (elm_widget_is_legacy(obj)) - elm_layout_signal_emit(obj, "elm,state,units,visible", "elm"); - else - elm_layout_signal_emit(obj, "efl,state,units,visible", "efl"); - edje_object_message_signal_process(wd->resize_obj); - - _units_set(obj); - elm_layout_sizing_eval(obj); -} - EOLIAN static void _efl_ui_progressbar_pulse_set(Eo *obj, Efl_Ui_Progressbar_Data *sd, Eina_Bool state) { @@ -773,6 +748,12 @@ _efl_ui_progressbar_show_progress_label_get(const Eo *obj EINA_UNUSED, Efl_Ui_Pr return pd->show_progress_label; } +EOLIAN static void +_efl_ui_progressbar_efl_ui_format_apply_formatted_value(Eo *obj, Efl_Ui_Progressbar_Data *pd EINA_UNUSED) +{ + _units_set(obj); +} + #include "efl_ui_progressbar_part.eo.c" /* Efl.Part end */ @@ -982,7 +963,7 @@ typedef struct progressbar_freefunc_type format_free_cb; } Pb_Format_Wrapper_Data; -static void +static Eina_Bool _format_legacy_to_format_eo_cb(void *data, Eina_Strbuf *str, const Eina_Value value) { Pb_Format_Wrapper_Data *pfwd = data; @@ -998,6 +979,8 @@ _format_legacy_to_format_eo_cb(void *data, Eina_Strbuf *str, const Eina_Value va if (buf) eina_strbuf_append(str, buf); if (pfwd->format_free_cb) pfwd->format_free_cb(buf); + + return EINA_TRUE; } static void @@ -1018,8 +1001,8 @@ elm_progressbar_unit_format_function_set(Evas_Object *obj, progressbar_func_type pfwd->format_free_cb = free_func; sd->is_legacy_format_cb = EINA_TRUE; - efl_ui_format_cb_set(obj, pfwd, _format_legacy_to_format_eo_cb, - _format_legacy_to_format_eo_free_cb); + efl_ui_format_func_set(obj, pfwd, _format_legacy_to_format_eo_cb, + _format_legacy_to_format_eo_free_cb); } EAPI void @@ -1042,13 +1025,15 @@ elm_progressbar_unit_format_set(Evas_Object *obj, const char *units) EFL_UI_PROGRESSBAR_DATA_GET_OR_RETURN(obj, sd); sd->is_legacy_format_string = EINA_TRUE; - efl_ui_format_string_set(obj, units); + efl_ui_format_string_set(obj, units, EFL_UI_FORMAT_STRING_TYPE_SIMPLE); } EAPI const char * elm_progressbar_unit_format_get(const Evas_Object *obj) { - return efl_ui_format_string_get(obj); + const char *fmt; + efl_ui_format_string_get(obj, &fmt, NULL); + return fmt; } EAPI void diff --git a/src/lib/elementary/efl_ui_progressbar.eo b/src/lib/elementary/efl_ui_progressbar.eo index 6a708447a9..ba8b3e37f2 100644 --- a/src/lib/elementary/efl_ui_progressbar.eo +++ b/src/lib/elementary/efl_ui_progressbar.eo @@ -58,7 +58,7 @@ class @beta Efl.Ui.Progressbar extends Efl.Ui.Layout_Base implements Efl.Ui.Rang Efl.Ui.Range_Display.range_value { get; set; } Efl.Ui.Range_Display.range_limits {get; set; } Efl.Ui.Layout_Orientable.orientation { get; set; } - Efl.Ui.Format.format_cb { set; } + Efl.Ui.Format.apply_formatted_value; Efl.Part.part_get; Efl.Access.Value.value_and_text { get; } Efl.Text.text { get; set; } diff --git a/src/lib/elementary/efl_ui_progressbar_private.h b/src/lib/elementary/efl_ui_progressbar_private.h index 9abd57d3d7..48b999b532 100644 --- a/src/lib/elementary/efl_ui_progressbar_private.h +++ b/src/lib/elementary/efl_ui_progressbar_private.h @@ -38,9 +38,6 @@ struct _Efl_Ui_Progressbar_Data Eina_List *progress_status; /**< The list of _Elm_Progress_Status. To save the progress value(in percentage) each part of given progress bar */ - Efl_Ui_Format_Func_Cb format_cb; - Eina_Free_Cb format_free_cb; - void *format_cb_data; Eina_Strbuf *format_strbuf; Efl_Ui_Layout_Orientation dir; /**< Orientation of the progressbar */ diff --git a/src/lib/elementary/efl_ui_spin.c b/src/lib/elementary/efl_ui_spin.c index feb3f543af..dddc869152 100644 --- a/src/lib/elementary/efl_ui_spin.c +++ b/src/lib/elementary/efl_ui_spin.c @@ -5,6 +5,7 @@ #define EFL_ACCESS_OBJECT_PROTECTED #define EFL_ACCESS_VALUE_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED +#define EFL_UI_FORMAT_PROTECTED #include @@ -15,142 +16,17 @@ #define MY_CLASS_NAME "Efl.Ui.Spin" -static Eina_Bool -_is_valid_digit(char x) -{ - return ((x >= '0' && x <= '9') || (x == '.')) ? EINA_TRUE : EINA_FALSE; -} - -static Efl_Ui_Spin_Format_Type -_is_label_format_integer(const char *fmt) -{ - const char *itr = NULL; - const char *start = NULL; - Eina_Bool found = EINA_FALSE; - Efl_Ui_Spin_Format_Type ret_type = SPIN_FORMAT_INVALID; - - start = strchr(fmt, '%'); - if (!start) return SPIN_FORMAT_INVALID; - - while (start) - { - if (found && start[1] != '%') - { - return SPIN_FORMAT_INVALID; - } - - if (start[1] != '%' && !found) - { - found = EINA_TRUE; - for (itr = start + 1; *itr != '\0'; itr++) - { - if ((*itr == 'd') || (*itr == 'u') || (*itr == 'i') || - (*itr == 'o') || (*itr == 'x') || (*itr == 'X')) - { - ret_type = SPIN_FORMAT_INT; - break; - } - else if ((*itr == 'f') || (*itr == 'F')) - { - ret_type = SPIN_FORMAT_FLOAT; - break; - } - else if (_is_valid_digit(*itr)) - { - continue; - } - else - { - return SPIN_FORMAT_INVALID; - } - } - } - start = strchr(start + 2, '%'); - } - - return ret_type; -} static void -_label_write(Evas_Object *obj) +_label_write(Evas_Object *obj, Efl_Ui_Spin_Data *sd) { - Efl_Ui_Spin_Special_Value *sv; - unsigned int i; - Eina_Array_Iterator iterator; + Eina_Strbuf *strbuf = eina_strbuf_new(); + Eina_Value val = eina_value_double_init(sd->val); + efl_ui_format_formatted_value_get(obj, strbuf, val); - Efl_Ui_Spin_Data *sd = efl_data_scope_get(obj, MY_CLASS); + elm_layout_text_set(obj, "efl.text", eina_strbuf_string_get(strbuf)); - EINA_ARRAY_ITER_NEXT(sd->special_values, i, sv, iterator) - { - if (sv->value == sd->val) - { - char buf[1024]; - snprintf(buf, sizeof(buf), "%s", sv->label); - elm_layout_text_set(obj, "elm.text", buf); - sd->templates = sv->label; - return; - } - } - - if (sd->format_cb) - { - const char *buf; - Eina_Value val; - - if (sd->format_type == SPIN_FORMAT_INT) - { - eina_value_setup(&val, EINA_VALUE_TYPE_INT); - eina_value_set(&val, (int)sd->val); - } - else - { - eina_value_setup(&val, EINA_VALUE_TYPE_DOUBLE); - eina_value_set(&val, sd->val); - } - eina_strbuf_reset(sd->format_strbuf); - sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); - - buf = eina_strbuf_string_get(sd->format_strbuf); - eina_value_flush(&val); - elm_layout_text_set(obj, "efl.text", buf); - sd->templates = buf; - } - else - { - char buf[1024]; - snprintf(buf, sizeof(buf), "%.0f", sd->val); - elm_layout_text_set(obj, "efl.text", buf); - evas_object_show(obj); - } -} - -static int -_decimal_points_get(const char *label) -{ - char result[16] = "0"; - const char *start = strchr(label, '%'); - - while (start) - { - if (start[1] != '%') - { - start = strchr(start, '.'); - if (start) - start++; - break; - } - else - start = strchr(start + 2, '%'); - } - - if (start) - { - const char *p = strchr(start, 'f'); - - if ((p) && ((p - start) < 15)) - sscanf(start, "%[^f]", result); - } - - return atoi(result); + eina_value_flush(&val); + eina_strbuf_free(strbuf); } EOLIAN static void @@ -188,48 +64,6 @@ _efl_ui_spin_efl_ui_widget_widget_input_event_handler(Eo *obj, Efl_Ui_Spin_Data return EINA_TRUE; } -EOLIAN static void -_efl_ui_spin_special_value_set(Eo *obj, Efl_Ui_Spin_Data *sd, const Eina_Array *values) -{ - EINA_SAFETY_ON_NULL_RETURN(values); - - unsigned int i; - Efl_Ui_Spin_Special_Value *sv; - Efl_Ui_Spin_Special_Value *temp; - Eina_Array_Iterator iterator; - - if (eina_array_count(sd->special_values)) - { - EINA_ARRAY_ITER_NEXT(sd->special_values, i, sv, iterator) - { - eina_stringshare_del(sv->label); - free(sv); - } - eina_array_clean(sd->special_values); - } - - if (eina_array_count(values)) - EINA_ARRAY_ITER_NEXT(values, i, temp, iterator) - { - sv = calloc(1, sizeof(*sv)); - if (!sv) return; - sv->value = temp->value; - sv->label = eina_stringshare_add(temp->label); - eina_array_push(sd->special_values, sv); - } - - _label_write(obj); -} - -EOLIAN static const Eina_Array* -_efl_ui_spin_special_value_get(const Eo *obj EINA_UNUSED, Efl_Ui_Spin_Data *sd) -{ - if (eina_array_count(sd->special_values)) - return sd->special_values; - else - return NULL; -} - EOLIAN static Eo * _efl_ui_spin_efl_object_constructor(Eo *obj, Efl_Ui_Spin_Data *sd) { @@ -241,7 +75,6 @@ _efl_ui_spin_efl_object_constructor(Eo *obj, Efl_Ui_Spin_Data *sd) sd->val_max = 100.0; sd->step = 1.0; - sd->special_values = eina_array_new(sizeof(Efl_Ui_Spin_Special_Value)); if (elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), @@ -249,7 +82,7 @@ _efl_ui_spin_efl_object_constructor(Eo *obj, Efl_Ui_Spin_Data *sd) elm_widget_theme_style_get(obj)) == EFL_UI_THEME_APPLY_ERROR_GENERIC) CRI("Failed to set layout!"); - _label_write(obj); + _label_write(obj, sd); elm_widget_can_focus_set(obj, EINA_TRUE); elm_layout_sizing_eval(obj); @@ -260,50 +93,13 @@ _efl_ui_spin_efl_object_constructor(Eo *obj, Efl_Ui_Spin_Data *sd) EOLIAN static void _efl_ui_spin_efl_object_destructor(Eo *obj, Efl_Ui_Spin_Data *sd EINA_UNUSED) { - Efl_Ui_Spin_Special_Value *sv; - Eina_Array_Iterator iterator; - unsigned int i; - - efl_ui_format_cb_set(obj, NULL, NULL, NULL); - - EINA_ARRAY_ITER_NEXT(sd->special_values, i, sv, iterator) - { - eina_stringshare_del(sv->label); - free(sv); - } - eina_array_free(sd->special_values); - efl_destructor(efl_super(obj, MY_CLASS)); } EOLIAN static void -_efl_ui_spin_efl_ui_format_format_cb_set(Eo *obj, Efl_Ui_Spin_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) +_efl_ui_spin_efl_ui_format_apply_formatted_value(Eo *obj, Efl_Ui_Spin_Data *sd EINA_UNUSED) { - if (sd->format_cb_data == func_data && sd->format_cb == func) - return; - - if (sd->format_cb_data && sd->format_free_cb) - sd->format_free_cb(sd->format_cb_data); - - sd->format_cb = func; - sd->format_cb_data = func_data; - sd->format_free_cb = func_free_cb; - if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); - - const char *format = efl_ui_format_string_get(obj); - if (format) - { - sd->format_type = _is_label_format_integer(format); - if (sd->format_type == SPIN_FORMAT_INVALID) - { - ERR("format:\"%s\" is invalid, cannot be set", format); - return; - } - else if (sd->format_type == SPIN_FORMAT_FLOAT) - sd->decimal_points = _decimal_points_get(format); - } - - _label_write(obj); + _label_write(obj, sd); elm_layout_sizing_eval(obj); } @@ -325,7 +121,7 @@ _efl_ui_spin_efl_ui_range_display_range_limits_set(Eo *obj, Efl_Ui_Spin_Data *sd if (sd->val < sd->val_min) sd->val = sd->val_min; if (sd->val > sd->val_max) sd->val = sd->val_max; - _label_write(obj); + _label_write(obj, sd); } EOLIAN static void @@ -372,7 +168,7 @@ _efl_ui_spin_efl_ui_range_display_range_value_set(Eo *obj, Efl_Ui_Spin_Data *sd, efl_event_callback_call(obj, EFL_UI_SPIN_EVENT_CHANGED, NULL); - _label_write(obj); + _label_write(obj, sd); } EOLIAN static double diff --git a/src/lib/elementary/efl_ui_spin.eo b/src/lib/elementary/efl_ui_spin.eo index 4fee3df56c..7f02d5aeaa 100644 --- a/src/lib/elementary/efl_ui_spin.eo +++ b/src/lib/elementary/efl_ui_spin.eo @@ -1,36 +1,11 @@ -struct @beta Efl.Ui.Spin_Special_Value -{ - [[Special value]] - value: double; [[Target value]] - label: string; [[String to replace]] -} - class @beta Efl.Ui.Spin extends Efl.Ui.Layout_Base implements Efl.Ui.Range_Interactive, Efl.Ui.Format, Efl.Access.Value, Efl.Access.Widget.Action { [[A Spin. - This is a widget which allows the user to increase or decrease numeric values - using user interactions. It's a basic type of widget for choosing and displaying values. + This is a widget which allows the user to increase or decrease a numeric value + using arrow buttons. It's a basic type of widget for choosing and displaying values. ]] - methods { - @property special_value { - [[Control special string to display in the place of the numerical value. - - It's useful for cases when a user should select an item that is - better indicated by a label than a value. For example, weekdays or months. - - Note: If another label was previously set to $value, it will be replaced - by the new label.]] - set { - } - get { - } - values { - values: const(array); [[The array of special values, or NULL if none]] - } - } - } implements { Efl.Object.constructor; Efl.Object.destructor; @@ -38,7 +13,7 @@ class @beta Efl.Ui.Spin extends Efl.Ui.Layout_Base implements Efl.Ui.Range_Inter Efl.Ui.Range_Display.range_limits { get; set; } Efl.Ui.Range_Interactive.range_step { get; set; } Efl.Ui.Range_Display.range_value { get; set; } - Efl.Ui.Format.format_cb { set; } + Efl.Ui.Format.apply_formatted_value; } events { changed: void; [[Called when spin changed]] diff --git a/src/lib/elementary/efl_ui_spin_button.c b/src/lib/elementary/efl_ui_spin_button.c index 71eaec160e..c356686aea 100644 --- a/src/lib/elementary/efl_ui_spin_button.c +++ b/src/lib/elementary/efl_ui_spin_button.c @@ -6,6 +6,7 @@ #define EFL_ACCESS_VALUE_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_UI_FOCUS_COMPOSITION_PROTECTED +#define EFL_UI_FORMAT_PROTECTED #include @@ -50,29 +51,20 @@ EFL_CALLBACKS_ARRAY_DEFINE(_inc_dec_button_cb, static void _entry_show(Evas_Object *obj) { - Efl_Ui_Spin_Special_Value *sv; - Eina_Array_Iterator iterator; - unsigned int i; char buf[32], fmt[32] = "%0.f"; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); - EINA_ARRAY_ITER_NEXT(pd->special_values, i, sv, iterator) - { - if (sv->value == pd->val) - { - snprintf(buf, sizeof(buf), "%s", sv->label); - elm_object_text_set(sd->ent, buf); - } - } + const char *format_string; + efl_ui_format_string_get(obj, &format_string, NULL); /* try to construct just the format from given label * completely ignoring pre/post words */ - if (pd->templates) + if (format_string) { - const char *start = strchr(pd->templates, '%'); + const char *start = strchr(format_string, '%'); while (start) { /* handle %% */ @@ -103,10 +95,7 @@ _entry_show(Evas_Object *obj) } } - if (pd->format_type == SPIN_FORMAT_INT) - snprintf(buf, sizeof(buf), fmt, (int)pd->val); - else - snprintf(buf, sizeof(buf), fmt, pd->val); + snprintf(buf, sizeof(buf), fmt, pd->val); elm_object_text_set(sd->ent, buf); } @@ -115,20 +104,16 @@ static void _label_write(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); - Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); - if (pd->templates) - { - efl_text_set(sd->text_button, pd->templates); - } - else - { - char buf[1024]; + Eina_Strbuf *strbuf = eina_strbuf_new(); + Eina_Value val = eina_value_double_init(pd->val); + efl_ui_format_formatted_value_get(obj, strbuf, val); - snprintf(buf, sizeof(buf), "%.0f", pd->val); - efl_text_set(sd->text_button, buf); - } + efl_text_set(sd->text_button, eina_strbuf_string_get(strbuf)); + + eina_value_flush(&val); + eina_strbuf_free(strbuf); } static Eina_Bool @@ -182,9 +167,6 @@ _entry_hide(Evas_Object *obj) static void _entry_value_apply(Evas_Object *obj) { - Efl_Ui_Spin_Special_Value *sv; - Eina_Array_Iterator iterator; - unsigned int i; const char *str; double val; char *end; @@ -200,10 +182,6 @@ _entry_value_apply(Evas_Object *obj) str = elm_object_text_get(sd->ent); if (!str) return; - EINA_ARRAY_ITER_NEXT(pd->special_values, i, sv, iterator) - if (sv->value == pd->val) - if (!strcmp(sv->label, str)) return; - val = strtod(str, &end); if (((*end != '\0') && (!isspace(*end))) || (fabs(val - pd->val) < DBL_EPSILON)) return; efl_ui_range_value_set(obj, val); @@ -267,14 +245,14 @@ static void _entry_accept_filter_add(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); - Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); static Elm_Entry_Filter_Accept_Set digits_filter_data; + int decimal_places = efl_ui_format_decimal_places_get(obj); if (!sd->ent) return; elm_entry_markup_filter_remove(sd->ent, elm_entry_filter_accept_set, &digits_filter_data); - if (pd->decimal_points > 0) + if (decimal_places > 0) digits_filter_data.accepted = "-.0123456789"; else digits_filter_data.accepted = "-0123456789"; @@ -307,6 +285,7 @@ _min_max_validity_filter(void *data, Evas_Object *obj, char **text) char *insert, *new_str = NULL; double val; int max_len = 0, len; + int decimal_places = efl_ui_format_decimal_places_get(data); EINA_SAFETY_ON_NULL_RETURN(data); EINA_SAFETY_ON_NULL_RETURN(obj); @@ -322,12 +301,12 @@ _min_max_validity_filter(void *data, Evas_Object *obj, char **text) if (!new_str) return; if (strchr(new_str, '-')) max_len++; - if (pd->format_type == SPIN_FORMAT_FLOAT) + if (decimal_places > 0) { point = strchr(new_str, '.'); if (point) { - if ((int) strlen(point + 1) > pd->decimal_points) + if ((int) strlen(point + 1) > decimal_places) { *insert = 0; goto end; diff --git a/src/lib/elementary/efl_ui_spin_private.h b/src/lib/elementary/efl_ui_spin_private.h index 3d21e3df54..3dbc06b4ea 100644 --- a/src/lib/elementary/efl_ui_spin_private.h +++ b/src/lib/elementary/efl_ui_spin_private.h @@ -3,29 +3,11 @@ #include "Elementary.h" -typedef enum _Efl_Ui_Spin_Format_Type -{ - SPIN_FORMAT_FLOAT, - SPIN_FORMAT_INT, - SPIN_FORMAT_INVALID -} Efl_Ui_Spin_Format_Type; - typedef struct _Efl_Ui_Spin_Data Efl_Ui_Spin_Data; struct _Efl_Ui_Spin_Data { - const char *templates; double val, val_min, val_max; double step; /**< step for the value change. 1 by default. */ - int decimal_points; - Ecore_Timer *spin_timer; /**< a timer for a repeated spin value change on mouse down */ - Efl_Ui_Spin_Format_Type format_type; - - Efl_Ui_Format_Func_Cb format_cb; - Eina_Free_Cb format_free_cb; - void *format_cb_data; - Eina_Strbuf *format_strbuf; - - Eina_Array *special_values; }; #endif diff --git a/src/lib/elementary/efl_ui_tags.c b/src/lib/elementary/efl_ui_tags.c index 12b56c15c5..34bc487355 100644 --- a/src/lib/elementary/efl_ui_tags.c +++ b/src/lib/elementary/efl_ui_tags.c @@ -2,6 +2,8 @@ # include "elementary_config.h" #endif +#define EFL_UI_FORMAT_PROTECTED + #include #include "elm_priv.h" #include "efl_ui_tags_private.h" @@ -75,7 +77,6 @@ _shrink_mode_set(Eo *obj, EINA_LIST_FOREACH(sd->layouts, l, layout) { - char buf[32]; Evas_Coord w_label_count = 0, h = 0; elm_box_pack_end(sd->box, layout); @@ -92,19 +93,12 @@ _shrink_mode_set(Eo *obj, if (count > 0) { - if (sd->format_cb) - { - eina_strbuf_reset(sd->format_strbuf); - eina_value_set(&val, count); - sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); - edje_object_part_text_escaped_set(sd->end, "efl.text", - eina_strbuf_string_get(sd->format_strbuf)); - } - else - { - snprintf(buf, sizeof(buf), "+ %d", count); - edje_object_part_text_escaped_set(sd->end, "efl.text", buf); - } + Eina_Strbuf *strbuf = eina_strbuf_new(); + eina_value_set(&val, count); + efl_ui_format_formatted_value_get(obj, strbuf, val); + edje_object_part_text_escaped_set(sd->end, "efl.text", + eina_strbuf_string_get(strbuf)); + eina_strbuf_free(strbuf); edje_object_size_min_calc(sd->end, &w_label_count, NULL); elm_coords_finger_size_adjust(1, &w_label_count, 1, NULL); @@ -112,23 +106,16 @@ _shrink_mode_set(Eo *obj, if ((w < 0) || (w < w_label_count)) { + Eina_Strbuf *strbuf = eina_strbuf_new(); elm_box_unpack(sd->box, layout); evas_object_hide(layout); count++; - if (sd->format_cb) - { - eina_strbuf_reset(sd->format_strbuf); - eina_value_set(&val, count); - sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); - edje_object_part_text_escaped_set(sd->end, "efl.text", - eina_strbuf_string_get(sd->format_strbuf)); - } - else - { - snprintf(buf, sizeof(buf), "+ %d", count); - edje_object_part_text_escaped_set(sd->end, "efl.text", buf); - } + eina_value_set(&val, count); + efl_ui_format_formatted_value_get(obj, strbuf, val); + edje_object_part_text_escaped_set(sd->end, "efl.text", + eina_strbuf_string_get(strbuf)); + eina_strbuf_free(strbuf); edje_object_size_min_calc(sd->end, &w_label_count, &h); elm_coords_finger_size_adjust(1, &w_label_count, 1, &h); @@ -1021,7 +1008,6 @@ _efl_ui_tags_efl_object_constructor(Eo *obj, Efl_Ui_Tags_Data *sd) sd->last_it_select = EINA_TRUE; sd->editable = EINA_TRUE; sd->parent = obj; - sd->format_cb = NULL; sd->it_array = eina_array_new(4); _view_init(obj, sd); @@ -1054,9 +1040,6 @@ _efl_ui_tags_efl_object_destructor(Eo *obj, Efl_Ui_Tags_Data *sd) evas_object_del(sd->end); ecore_timer_del(sd->longpress_timer); - efl_ui_format_cb_set(obj, NULL, NULL, NULL); - eina_strbuf_free(sd->format_strbuf); - efl_destructor(efl_super(obj, MY_CLASS)); } @@ -1072,23 +1055,6 @@ _efl_ui_tags_efl_text_text_get(const Eo *obj EINA_UNUSED, Efl_Ui_Tags_Data *sd) return (sd->label_str ? sd->label_str : NULL); } -EOLIAN static void -_efl_ui_tags_efl_ui_format_format_cb_set(Eo *obj EINA_UNUSED, Efl_Ui_Tags_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) -{ - if ((sd->format_cb_data == func_data) && (sd->format_cb == func)) - return; - - if (sd->format_cb_data && sd->format_free_cb) - sd->format_free_cb(sd->format_cb_data); - - sd->format_cb = func; - sd->format_cb_data = func_data; - sd->format_free_cb = func_free_cb; - if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); - - _view_update(sd); -} - EOLIAN static Eina_Bool _efl_ui_tags_expanded_get(const Eo *obj EINA_UNUSED, Efl_Ui_Tags_Data *sd) { @@ -1171,6 +1137,11 @@ _efl_ui_tags_items_get(const Eo *obj EINA_UNUSED, Efl_Ui_Tags_Data *sd) return sd->it_array; } +EOLIAN static void +_efl_ui_tags_efl_ui_format_apply_formatted_value(Eo *obj EINA_UNUSED, Efl_Ui_Tags_Data *pd) +{ + _view_update(pd); +} #define EFL_UI_TAGS_EXTRA_OPS \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_tags), \ diff --git a/src/lib/elementary/efl_ui_tags.eo b/src/lib/elementary/efl_ui_tags.eo index 86e83ab7e2..6f8c739f06 100644 --- a/src/lib/elementary/efl_ui_tags.eo +++ b/src/lib/elementary/efl_ui_tags.eo @@ -47,7 +47,7 @@ class @beta Efl.Ui.Tags extends Efl.Ui.Layout_Base implements Efl.Text, Efl.Ui.F Efl.Object.destructor; Efl.Ui.Widget.widget_input_event_handler; Efl.Text.text { get; set; } - Efl.Ui.Format.format_cb { set; } + Efl.Ui.Format.apply_formatted_value; } events { /* FIXME: Returning a basic type is not future-proof, better return a struct */ diff --git a/src/lib/elementary/efl_ui_tags_private.h b/src/lib/elementary/efl_ui_tags_private.h index 49d0a95264..ef9fca63e3 100644 --- a/src/lib/elementary/efl_ui_tags_private.h +++ b/src/lib/elementary/efl_ui_tags_private.h @@ -34,11 +34,6 @@ struct _Efl_Ui_Tags_Data Evas_Coord w_box, h_box; int shrink; - Efl_Ui_Format_Func_Cb format_cb; - Eina_Free_Cb format_free_cb; - void *format_cb_data; - Eina_Strbuf *format_strbuf; - Eina_Bool last_it_select : 1; Eina_Bool editable : 1; Eina_Bool focused : 1; diff --git a/src/lib/elementary/elm_slider.c b/src/lib/elementary/elm_slider.c index e57893c795..8239e475ee 100644 --- a/src/lib/elementary/elm_slider.c +++ b/src/lib/elementary/elm_slider.c @@ -7,6 +7,7 @@ #define EFL_ACCESS_VALUE_PROTECTED #define ELM_LAYOUT_PROTECTED #define EFL_PART_PROTECTED +#define EFL_UI_FORMAT_PROTECTED #include @@ -953,7 +954,7 @@ _elm_slider_efl_object_constructor(Eo *obj, Elm_Slider_Data *priv) elm_widget_can_focus_set(obj, EINA_TRUE); - efl_ui_format_string_set(efl_part(obj, "indicator"), "%0.2f"); + efl_ui_format_string_set(efl_part(obj, "indicator"), "%0.2f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE); evas_object_event_callback_add (sd->spacer, EVAS_CALLBACK_MOUSE_DOWN, _spacer_down_cb, obj); @@ -976,7 +977,7 @@ _elm_slider_efl_object_destructor(Eo *obj, ELM_SAFE_FREE(sd->indi_template, eina_stringshare_del); eina_strbuf_free(sd->indi_format_strbuf); - efl_ui_format_cb_set(obj, NULL, NULL, NULL); + efl_ui_format_func_set(obj, NULL, NULL, NULL); eina_strbuf_free(sd->format_strbuf); efl_destructor(efl_super(obj, MY_CLASS)); @@ -1004,7 +1005,7 @@ _elm_slider_class_constructor(Efl_Class *klass) } EOLIAN static void -_elm_slider_efl_ui_format_format_cb_set(Eo *obj, Elm_Slider_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) +_elm_slider_efl_ui_format_format_cb_set(Eo *obj, Elm_Slider_Data *sd, void *func_data, Efl_Ui_Format_Func func, Eina_Free_Cb func_free_cb) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); @@ -1076,7 +1077,7 @@ _elm_slider_efl_part_part_get(const Eo *obj, Elm_Slider_Data *sd EINA_UNUSED, co } EOLIAN static void -_elm_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *_pd EINA_UNUSED, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) +_elm_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *_pd EINA_UNUSED, void *func_data, Efl_Ui_Format_Func func, Eina_Free_Cb func_free_cb) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Elm_Slider_Data *sd = efl_data_scope_get(pd->obj, ELM_SLIDER_CLASS); @@ -1095,17 +1096,19 @@ _elm_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *_pd EINA_U efl_canvas_group_change(pd->obj); } -static void +static Eina_Bool _indi_default_format_cb(void *data, Eina_Strbuf *str, const Eina_Value value) { const Eina_Value_Type *type = eina_value_type_get(&value); Elm_Slider_Data *sd = efl_data_scope_get(data, ELM_SLIDER_CLASS); double v; - if (type != EINA_VALUE_TYPE_DOUBLE) return; + if (type != EINA_VALUE_TYPE_DOUBLE) return EINA_FALSE; eina_value_get(&value, &v); eina_strbuf_append_printf(str, sd->indi_template, v); + + return EINA_TRUE; } static void @@ -1129,7 +1132,7 @@ _elm_slider_part_indicator_efl_ui_format_format_string_set(Eo *obj, void *_pd EI if (!template) return; eina_stringshare_replace(&sd->indi_template, template); - efl_ui_format_cb_set(efl_part(pd->obj, "indicator"), pd->obj, _indi_default_format_cb, _indi_default_format_free_cb); + efl_ui_format_func_set(efl_part(pd->obj, "indicator"), pd->obj, _indi_default_format_cb, _indi_default_format_free_cb); } EOLIAN static const char * @@ -1164,6 +1167,10 @@ _elm_slider_part_indicator_visible_mode_get(const Eo *obj, void *_pd EINA_UNUSED return sd->indicator_visible_mode; } +void _elm_slider_part_indicator_efl_ui_format_apply_formatted_value(Eo *obj EINA_UNUSED, Elm_Part_Data *pd EINA_UNUSED) +{ +} + #include "elm_slider_part_indicator_eo.c" /* Efl.Part end */ @@ -1194,13 +1201,15 @@ elm_slider_span_size_get(const Evas_Object *obj) EAPI void elm_slider_unit_format_set(Evas_Object *obj, const char *units) { - efl_ui_format_string_set(obj, units); + efl_ui_format_string_set(obj, units, EFL_UI_FORMAT_STRING_TYPE_SIMPLE); } EAPI const char * elm_slider_unit_format_get(const Evas_Object *obj) { - return efl_ui_format_string_get(obj); + const char* fmt; + efl_ui_format_string_get(obj, &fmt, NULL); + return fmt; } EAPI void @@ -1277,7 +1286,7 @@ typedef struct slider_freefunc_type format_free_cb; } Slider_Format_Wrapper_Data; -static void +static Eina_Bool _format_legacy_to_format_eo_cb(void *data, Eina_Strbuf *str, const Eina_Value value) { Slider_Format_Wrapper_Data *sfwd = data; @@ -1293,6 +1302,8 @@ _format_legacy_to_format_eo_cb(void *data, Eina_Strbuf *str, const Eina_Value va if (buf) eina_strbuf_append(str, buf); if (sfwd->format_free_cb) sfwd->format_free_cb(buf); + + return EINA_TRUE; } static void @@ -1310,7 +1321,7 @@ elm_slider_units_format_function_set(Evas_Object *obj, slider_func_type func, sl sfwd->format_cb = func; sfwd->format_free_cb = free_func; - efl_ui_format_cb_set(obj, sfwd, _format_legacy_to_format_eo_cb, _format_legacy_to_format_eo_free_cb); + efl_ui_format_func_set(obj, sfwd, _format_legacy_to_format_eo_cb, _format_legacy_to_format_eo_free_cb); } EAPI void @@ -1369,13 +1380,15 @@ elm_slider_min_max_get(const Evas_Object *obj, double *min, double *max) EAPI void elm_slider_indicator_format_set(Evas_Object *obj, const char *indicator) { - efl_ui_format_string_set(efl_part(obj, "indicator"), indicator); + efl_ui_format_string_set(efl_part(obj, "indicator"), indicator, EFL_UI_FORMAT_STRING_TYPE_SIMPLE); } EAPI const char * elm_slider_indicator_format_get(const Evas *obj) { - return efl_ui_format_string_get(efl_part(obj, "indicator")); + const char *fmt; + efl_ui_format_string_get(efl_part(obj, "indicator"), &fmt, NULL); + return fmt; } EAPI void @@ -1386,9 +1399,9 @@ elm_slider_indicator_format_function_set(Evas_Object *obj, slider_func_type func sfwd->format_cb = func; sfwd->format_free_cb = free_func; - efl_ui_format_cb_set(efl_part(obj, "indicator"), sfwd, - _format_legacy_to_format_eo_cb, - _format_legacy_to_format_eo_free_cb); + efl_ui_format_func_set(efl_part(obj, "indicator"), sfwd, + _format_legacy_to_format_eo_cb, + _format_legacy_to_format_eo_free_cb); } EAPI void @@ -1452,6 +1465,10 @@ elm_slider_indicator_visible_mode_get(const Evas_Object *obj) return elm_slider_part_indicator_visible_mode_get(efl_part(obj, "indicator")); } +void _elm_slider_efl_ui_format_apply_formatted_value(Eo *obj EINA_UNUSED, Elm_Slider_Data *pd EINA_UNUSED) +{ +} + /* Internal EO APIs and hidden overrides */ ELM_LAYOUT_CONTENT_ALIASES_IMPLEMENT(elm_slider) diff --git a/src/lib/elementary/elm_slider_eo.c b/src/lib/elementary/elm_slider_eo.c index 12debeef34..5f3ecc29e9 100644 --- a/src/lib/elementary/elm_slider_eo.c +++ b/src/lib/elementary/elm_slider_eo.c @@ -29,7 +29,7 @@ void _elm_slider_efl_text_markup_markup_set(Eo *obj, Elm_Slider_Data *pd, const const char *_elm_slider_efl_text_markup_markup_get(const Eo *obj, Elm_Slider_Data *pd); -void _elm_slider_efl_ui_format_format_cb_set(Eo *obj, Elm_Slider_Data *pd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb); +void _elm_slider_efl_ui_format_format_cb_set(Eo *obj, Elm_Slider_Data *pd, void *func_data, Efl_Ui_Format_Func func, Eina_Free_Cb func_free_cb); void _elm_slider_efl_ui_l10n_l10n_text_set(Eo *obj, Elm_Slider_Data *pd, const char *label, const char *domain); @@ -40,6 +40,7 @@ const char *_elm_slider_efl_ui_l10n_l10n_text_get(const Eo *obj, Elm_Slider_Data Efl_Object *_elm_slider_efl_part_part_get(const Eo *obj, Elm_Slider_Data *pd, const char *name); +void _elm_slider_efl_ui_format_apply_formatted_value(Eo *obj, Elm_Slider_Data *pd); static Eina_Bool _elm_slider_class_initializer(Efl_Class *klass) @@ -63,7 +64,8 @@ _elm_slider_class_initializer(Efl_Class *klass) EFL_OBJECT_OP_FUNC(efl_text_get, _elm_slider_efl_text_text_get), EFL_OBJECT_OP_FUNC(efl_text_markup_set, _elm_slider_efl_text_markup_markup_set), EFL_OBJECT_OP_FUNC(efl_text_markup_get, _elm_slider_efl_text_markup_markup_get), - EFL_OBJECT_OP_FUNC(efl_ui_format_cb_set, _elm_slider_efl_ui_format_format_cb_set), + EFL_OBJECT_OP_FUNC(efl_ui_format_func_set, _elm_slider_efl_ui_format_format_cb_set), + EFL_OBJECT_OP_FUNC(efl_ui_format_apply_formatted_value, _elm_slider_efl_ui_format_apply_formatted_value), EFL_OBJECT_OP_FUNC(efl_ui_l10n_text_set, _elm_slider_efl_ui_l10n_l10n_text_set), EFL_OBJECT_OP_FUNC(efl_ui_l10n_text_get, _elm_slider_efl_ui_l10n_l10n_text_get), EFL_OBJECT_OP_FUNC(efl_part_get, _elm_slider_efl_part_part_get), diff --git a/src/lib/elementary/elm_slider_part_indicator_eo.c b/src/lib/elementary/elm_slider_part_indicator_eo.c index 915e1cea7f..54882942b1 100644 --- a/src/lib/elementary/elm_slider_part_indicator_eo.c +++ b/src/lib/elementary/elm_slider_part_indicator_eo.c @@ -7,7 +7,7 @@ Elm_Slider_Indicator_Visible_Mode _elm_slider_part_indicator_visible_mode_get(co EOAPI EFL_FUNC_BODY_CONST(elm_slider_part_indicator_visible_mode_get, Elm_Slider_Indicator_Visible_Mode, 0); -void _elm_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *pd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb); +void _elm_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *pd, void *func_data, Efl_Ui_Format_Func func, Eina_Free_Cb func_free_cb); void _elm_slider_part_indicator_efl_ui_format_format_string_set(Eo *obj, void *pd, const char *units); @@ -15,6 +15,7 @@ void _elm_slider_part_indicator_efl_ui_format_format_string_set(Eo *obj, void *p const char *_elm_slider_part_indicator_efl_ui_format_format_string_get(const Eo *obj, void *pd); +void _elm_slider_part_indicator_efl_ui_format_apply_formatted_value(Eo *obj, Elm_Part_Data *pd); static Eina_Bool _elm_slider_part_indicator_class_initializer(Efl_Class *klass) @@ -30,9 +31,10 @@ _elm_slider_part_indicator_class_initializer(Efl_Class *klass) EFL_OPS_DEFINE(ops, EFL_OBJECT_OP_FUNC(elm_slider_part_indicator_visible_mode_set, _elm_slider_part_indicator_visible_mode_set), EFL_OBJECT_OP_FUNC(elm_slider_part_indicator_visible_mode_get, _elm_slider_part_indicator_visible_mode_get), - EFL_OBJECT_OP_FUNC(efl_ui_format_cb_set, _elm_slider_part_indicator_efl_ui_format_format_cb_set), + EFL_OBJECT_OP_FUNC(efl_ui_format_func_set, _elm_slider_part_indicator_efl_ui_format_format_cb_set), EFL_OBJECT_OP_FUNC(efl_ui_format_string_set, _elm_slider_part_indicator_efl_ui_format_format_string_set), EFL_OBJECT_OP_FUNC(efl_ui_format_string_get, _elm_slider_part_indicator_efl_ui_format_format_string_get), + EFL_OBJECT_OP_FUNC(efl_ui_format_apply_formatted_value, _elm_slider_part_indicator_efl_ui_format_apply_formatted_value), ELM_SLIDER_PART_INDICATOR_EXTRA_OPS ); opsp = &ops; @@ -50,4 +52,4 @@ static const Efl_Class_Description _elm_slider_part_indicator_class_desc = { NULL }; -EFL_DEFINE_CLASS(elm_slider_part_indicator_class_get, &_elm_slider_part_indicator_class_desc, EFL_UI_LAYOUT_PART_CLASS, EFL_UI_FORMAT_MIXIN, NULL); +EFL_DEFINE_CLASS(elm_slider_part_indicator_class_get, &_elm_slider_part_indicator_class_desc, EFL_UI_LAYOUT_PART_CLASS, EFL_UI_LEGACY_INTERFACE, EFL_UI_FORMAT_MIXIN, NULL); diff --git a/src/lib/elementary/elm_widget_multibuttonentry.h b/src/lib/elementary/elm_widget_multibuttonentry.h index 1da716622b..83d5d499bf 100644 --- a/src/lib/elementary/elm_widget_multibuttonentry.h +++ b/src/lib/elementary/elm_widget_multibuttonentry.h @@ -103,7 +103,7 @@ struct _Elm_Multibuttonentry_Data void *add_callback_data; Ecore_Timer *longpress_timer; - Efl_Ui_Format_Func_Cb format_cb; + Efl_Ui_Format_Func format_cb; Eina_Free_Cb format_free_cb; void *format_cb_data; Eina_Strbuf *format_strbuf; diff --git a/src/lib/elementary/elm_widget_slider.h b/src/lib/elementary/elm_widget_slider.h index 1b9c85c1fe..c12ed7c011 100644 --- a/src/lib/elementary/elm_widget_slider.h +++ b/src/lib/elementary/elm_widget_slider.h @@ -35,12 +35,12 @@ struct _Elm_Slider_Data Evas_Coord size; - Efl_Ui_Format_Func_Cb format_cb; + Efl_Ui_Format_Func format_cb; Eina_Free_Cb format_free_cb; void *format_cb_data; Eina_Strbuf *format_strbuf; - Efl_Ui_Format_Func_Cb indi_format_cb; + Efl_Ui_Format_Func indi_format_cb; Eina_Free_Cb indi_format_free_cb; void *indi_format_cb_data; Eina_Strbuf *indi_format_strbuf; diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 9e88e4c9b6..9807d46216 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -180,6 +180,7 @@ pub_eo_files = [ 'efl_ui_relative_layout.eo', 'efl_ui_clickable.eo', 'efl_ui_clickable_util.eo', + 'efl_ui_format.eo', ] foreach eo_file : pub_eo_files @@ -935,6 +936,7 @@ elementary_src = [ 'efl_ui_relative_layout.c', 'efl_ui_clickable.c', 'efl_ui_clickable_util.c', + 'efl_ui_format.c', ] elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl] diff --git a/src/tests/elementary/spec/README b/src/tests/elementary/spec/README index f98cb78533..24fad54327 100644 --- a/src/tests/elementary/spec/README +++ b/src/tests/elementary/spec/README @@ -27,3 +27,4 @@ Just add your widget name to the "test-widgets" array and recompile. Next run of == Adding a new test == To add a new test you need to create the .c file and include the metadata comment at the top. +Remember to add the new .c file to the meson.build file. diff --git a/src/tests/elementary/spec/efl_test_format.c b/src/tests/elementary/spec/efl_test_format.c new file mode 100644 index 0000000000..cd2e698051 --- /dev/null +++ b/src/tests/elementary/spec/efl_test_format.c @@ -0,0 +1,174 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#define EFL_UI_FORMAT_PROTECTED /* To access internal methods */ + +#include +#include "efl_ui_spec_suite.h" +#include "suite_helpers.h" + +/* spec-meta-start + {"test-interface":"Efl.Ui.Format", + "test-widgets": ["Efl.Ui.Progressbar", "Efl.Ui.Calendar", "Efl.Ui.Tags", "Efl.Ui.Spin", "Efl.Ui.Spin_Button"]} + spec-meta-end */ + +static const Efl_Ui_Format_Value values[] = {{15, "fifteen"}, {16, "sixteen"}, {17, "seventeen"}, {18, "eighteen"}}; + +EFL_START_TEST(format_values) +{ + Eina_Strbuf *buf = eina_strbuf_new(); + Eina_Value eina_val; + + efl_ui_format_values_set(widget, EINA_C_ARRAY_ACCESSOR_NEW(values)); + eina_val = eina_value_int_init(17); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "seventeen"); // Check that value works + eina_val = eina_value_float_init(16.f); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "sixteen"); // Check built-in conversion + + eina_value_flush(&eina_val); + eina_strbuf_free(buf); +} +EFL_END_TEST + +static Eina_Bool +_format_func(void *data, Eina_Strbuf *str, const Eina_Value value) +{ + int i = *(int *)data; + int v; + ck_assert_int_eq(i, 1234); // Check that data ptr is passed along correctly + if (eina_value_type_get(&value) != EINA_VALUE_TYPE_INT) return EINA_FALSE; + eina_value_get(&value, &v); + eina_strbuf_append_printf(str, "You said '%d'", v); + + return EINA_TRUE; +} + +static void +_format_free_func(void *data) +{ + int i = *(int *)data; + ck_assert_int_eq(i, 1234); // Check that data ptr is passed along correctly + *(int *)data = 12345; // Change it to indicate that free func was called +} + +EFL_START_TEST(format_func) +{ + int data = 1234; + Eina_Strbuf *buf = eina_strbuf_new(); + Eina_Value eina_val; + + efl_ui_format_func_set(widget, &data, _format_func, _format_free_func); + eina_val = eina_value_int_init(15); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "You said '15'"); // Check that format func works + efl_ui_format_func_set(widget, NULL, NULL, NULL); + ck_assert_int_eq(data, 12345); // Check that free func is called + eina_value_flush(&eina_val); + + eina_strbuf_free(buf); +} +EFL_END_TEST + +EFL_START_TEST(format_string) +{ + Eina_Strbuf *buf = eina_strbuf_new(); + Eina_Value eina_val; + struct tm t = { 0 }; + const char *old_locale = setlocale(LC_TIME, NULL); + +#define CHECK(fmt_str, fmt_type, val, val_type, result) \ + efl_ui_format_string_set(widget, fmt_str, fmt_type); \ + eina_value_setup(&eina_val, val_type); \ + eina_value_set(&eina_val, val); \ + efl_ui_format_formatted_value_get(widget, buf, eina_val); \ + ck_assert_str_eq(eina_strbuf_string_get(buf), result); \ + eina_value_flush(&eina_val) + + CHECK("%d", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234, EINA_VALUE_TYPE_INT, "1234"); + CHECK("%d", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234.f, EINA_VALUE_TYPE_FLOAT, "1234"); // built-in conversion + CHECK("%d units", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234, EINA_VALUE_TYPE_INT, "1234 units"); // complex format + + CHECK("%.0f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 12.34f, EINA_VALUE_TYPE_FLOAT, "12"); + CHECK("%.1f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 12.34f, EINA_VALUE_TYPE_FLOAT, "12.3"); + CHECK("%.2f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 12.34f, EINA_VALUE_TYPE_FLOAT, "12.34"); + CHECK("%.2f", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234, EINA_VALUE_TYPE_INT, "1234.00"); // built-in conversion + CHECK("%.0f%%", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 75.f, EINA_VALUE_TYPE_FLOAT, "75%"); // complex format + + CHECK("%s", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, "Hello!", EINA_VALUE_TYPE_STRING, "Hello!"); + CHECK("%s", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234, EINA_VALUE_TYPE_INT, "1234"); // built-in conversion + CHECK("He said '%s'", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, "Hello!", EINA_VALUE_TYPE_STRING, "He said 'Hello!'"); // complex format + + CHECK("Static string", EFL_UI_FORMAT_STRING_TYPE_SIMPLE, 1234, EINA_VALUE_TYPE_INT, "Static string"); + + strptime("2019 7 3 11:49:3", "%Y %m %d %H:%M:%S", &t); + setlocale(LC_TIME, "C"); + CHECK("%F", EFL_UI_FORMAT_STRING_TYPE_TIME, t, EINA_VALUE_TYPE_TM, "2019-07-03"); + CHECK("%T", EFL_UI_FORMAT_STRING_TYPE_TIME, t, EINA_VALUE_TYPE_TM, "11:49:03"); + CHECK("%A", EFL_UI_FORMAT_STRING_TYPE_TIME, t, EINA_VALUE_TYPE_TM, "Wednesday"); + CHECK("<%B %Y>", EFL_UI_FORMAT_STRING_TYPE_TIME, t, EINA_VALUE_TYPE_TM, ""); + setlocale(LC_TIME, old_locale); + + eina_strbuf_free(buf); +#undef CHECK +} +EFL_END_TEST + +static Eina_Bool +_partial_format_func(void *data EINA_UNUSED, Eina_Strbuf *str, const Eina_Value value) +{ + int v; + if (eina_value_type_get(&value) != EINA_VALUE_TYPE_INT) return EINA_FALSE; + eina_value_get(&value, &v); + if (v < 10) + { + eina_strbuf_append_printf(str, "You said '%d'", v); + return EINA_TRUE; + } + return EINA_FALSE; +} + +EFL_START_TEST(format_mixed) +{ + Eina_Strbuf *buf = eina_strbuf_new(); + Eina_Value eina_val; + + // Now we check combinations of different options + // Each one should be used in turn when the previous one fails: values, func, string, fallback + efl_ui_format_values_set(widget, EINA_C_ARRAY_ACCESSOR_NEW(values)); + efl_ui_format_func_set(widget, NULL, _partial_format_func, NULL); + efl_ui_format_string_set(widget, "%d rabbits", EFL_UI_FORMAT_STRING_TYPE_SIMPLE); + + eina_val = eina_value_int_init(1); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "You said '1'"); // Func + eina_value_set(&eina_val, 15); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "fifteen"); // Values + eina_value_set(&eina_val, 25); + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "25 rabbits"); // Values + + EXPECT_ERROR_START; + // This is an invalid format string (it has two placeholders) which should + // trigger the fallback mechanism + efl_ui_format_string_set(widget, "%d %d", EFL_UI_FORMAT_STRING_TYPE_SIMPLE); + EXPECT_ERROR_END; + efl_ui_format_formatted_value_get(widget, buf, eina_val); + ck_assert_str_eq(eina_strbuf_string_get(buf), "25"); // Fallback + + eina_value_flush(&eina_val); + eina_strbuf_free(buf); +} +EFL_END_TEST + +void +efl_ui_format_behavior_test(TCase *tc) +{ + tcase_add_test(tc, format_values); + tcase_add_test(tc, format_func); + tcase_add_test(tc, format_string); + tcase_add_test(tc, format_mixed); +} diff --git a/src/tests/elementary/spec/efl_ui_spec_suite.h b/src/tests/elementary/spec/efl_ui_spec_suite.h index 8389d819ef..666a40d80d 100644 --- a/src/tests/elementary/spec/efl_ui_spec_suite.h +++ b/src/tests/elementary/spec/efl_ui_spec_suite.h @@ -15,6 +15,7 @@ void efl_pack_linear_behavior_test(TCase *tc); void efl_content_behavior_test(TCase *tc); void efl_gfx_arrangement_behavior_test(TCase *tc); void efl_ui_clickable_behavior_test(TCase *tc); +void efl_ui_format_behavior_test(TCase *tc); void efl_test_container_content_equal(Efl_Ui_Widget **wid, unsigned int len); void efl_test_container_expect_evt_content_added(Efl_Ui_Widget *widget, const Efl_Event_Description *ev, Eina_Bool *flag, void *event_data); diff --git a/src/tests/elementary/spec/meson.build b/src/tests/elementary/spec/meson.build index 0f72b54fad..00fe61c1e7 100644 --- a/src/tests/elementary/spec/meson.build +++ b/src/tests/elementary/spec/meson.build @@ -5,6 +5,7 @@ efl_ui_suite_behavior_test_files = files([ 'efl_test_content.c', 'efl_test_gfx_arrangement.c', 'efl_test_clickable.c', + 'efl_test_format.c', ]) efl_ui_suite_behavior_src = files([