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.
2019-07-02 05:40:06 -07:00
# 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 ;
2019-09-16 09:10:09 -07:00
if ( ! eina_value_double_convert ( & value , & v ) )
ERR ( " Format conversion failed " ) ;
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.
2019-07-02 05:40:06 -07:00
eina_strbuf_append_printf ( str , pd - > format_string , v ) ;
break ;
}
case FORMAT_TYPE_INT :
{
int v = 0 ;
2019-09-16 09:10:09 -07:00
if ( ! eina_value_int_convert ( & value , & v ) )
ERR ( " Format conversion failed " ) ;
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.
2019-07-02 05:40:06 -07:00
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 ;
2019-08-25 22:50:44 -07:00
char * buf = NULL ;
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.
2019-07-02 05:40:06 -07:00
eina_value_get ( & value , & v ) ;
buf = eina_strftime ( pd - > format_string , & v ) ;
2019-08-25 22:50:44 -07:00
if ( buf )
{
eina_strbuf_append ( str , buf ) ;
free ( buf ) ;
}
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.
2019-07-02 05:40:06 -07:00
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 )
{
2019-12-18 08:18:35 -08:00
Efl_Ui_Format_Value * v ;
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.
2019-07-02 05:40:06 -07:00
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 )
{
2019-12-18 08:18:35 -08:00
Efl_Ui_Format_Value vcopy = { v - > value , eina_stringshare_add ( v - > text ) } ;
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.
2019-07-02 05:40:06 -07:00
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 */
2019-08-08 14:18:45 -07:00
Efl_Ui_Format_Value val = { 0 } ;
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.
2019-07-02 05:40:06 -07:00
int ndx ;
2019-09-16 09:10:09 -07:00
if ( ! eina_value_int_convert ( & value , & val . value ) )
ERR ( " Format conversion failed " ) ;
2019-08-08 14:18:45 -07:00
ndx = eina_inarray_search_sorted ( pd - > format_values , & val , ( Eina_Compare_Cb ) _value_compare ) ;
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.
2019-07-02 05:40:06 -07:00
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"