diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index d12698d814..f9485bb9b7 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -178,6 +178,7 @@ elm_private_eolian_files = \ lib/elementary/efl_ui_list_view_precise_layouter.eo \ lib/elementary/efl_ui_model_size.eo \ lib/elementary/efl_ui_model_homogeneous.eo \ + lib/elementary/efl_ui_model_exact.eo \ $(NULL) # Legacy classes - not part of public EO API @@ -890,6 +891,7 @@ lib_elementary_libelementary_la_SOURCES = \ lib/elementary/efl_ui_widget_factory.c \ lib/elementary/efl_ui_model_size.c \ lib/elementary/efl_ui_model_homogeneous.c \ + lib/elementary/efl_ui_model_exact.c \ $(NULL) diff --git a/src/lib/elementary/efl_ui_model_exact.c b/src/lib/elementary/efl_ui_model_exact.c new file mode 100644 index 0000000000..76d799108d --- /dev/null +++ b/src/lib/elementary/efl_ui_model_exact.c @@ -0,0 +1,338 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "elm_priv.h" + +// This class store the size information in a compressed array and unpack it +// when necessary. It does maintain a cache of up to 3 uncompressed slot. +// That cache could get dropped when the application is entering the 'pause' +// state. + +#define EFL_UI_MODEL_EXACT_CONTENT 1024 +#define EFL_UI_MODEL_EXACT_CONTENT_LENGTH (EFL_UI_MODEL_EXACT_CONTENT * sizeof (unsigned int)) + +// For now only vertical logic is implemented. Horizontal list and grid are not supported. + +typedef struct _Efl_Ui_Model_Exact_Data Efl_Ui_Model_Exact_Data; +struct _Efl_Ui_Model_Exact_Data +{ + Efl_Ui_Model_Exact_Data *parent; + + struct { + Eina_List *width; + Eina_List *height; + } compressed; + + struct { + unsigned int width; + unsigned int height; + } total_size; + + struct { + unsigned int *width; + unsigned int *height; + unsigned int start_offset; + unsigned short usage; + Eina_Bool defined : 1; + struct { + Eina_Bool width : 1; + Eina_Bool height : 1; + } decompressed; + } slot[3]; +}; + +static Efl_Object * +_efl_ui_model_exact_efl_object_constructor(Eo *obj, Efl_Ui_Model_Exact_Data *pd) +{ + Eo *parent = efl_parent_get(obj); + + if (parent && efl_isa(parent, EFL_UI_MODEL_EXACT_CLASS)) + pd->parent = efl_data_scope_get(efl_parent_get(obj), EFL_UI_MODEL_EXACT_CLASS); + + return efl_constructor(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS)); +} + +static unsigned int +_efl_ui_model_exact_list_find(unsigned int list_index, Eina_List *start, Eina_List **l) +{ + Eina_Binbuf *tbuf; + + EINA_LIST_FOREACH(start, *l, tbuf) + { + if (list_index == 0) break; + list_index--; + } + + return list_index; +} + +static Eina_List * +_efl_ui_model_exact_slot_compress(unsigned int index, Eina_List *compressed, unsigned int *buffer) +{ + unsigned int list_index = index / EFL_UI_MODEL_EXACT_CONTENT; + static Eina_Binbuf *z = NULL; + Eina_Binbuf *cbuf; + Eina_Binbuf *tbuf; + Eina_List *l = NULL; + + _efl_ui_model_exact_list_find(list_index, compressed, &l); + + tbuf = eina_binbuf_manage_new((unsigned char *) buffer, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE); + cbuf = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST); + eina_binbuf_free(tbuf); + + if (!tbuf || !cbuf) return compressed; + + // Make sure the list has all the buffer up to the needed one filled with valid data + if (list_index) + { + // Create the compressed zero buffer once. + if (!z) + { + unsigned char *zmem; + + zmem = calloc(EFL_UI_MODEL_EXACT_CONTENT, sizeof (unsigned int)); + if (!zmem) return compressed; + + tbuf = eina_binbuf_manage_new(zmem, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE); + if (!tbuf) return compressed; + + z = emile_compress(tbuf, EMILE_LZ4, EMILE_COMPRESSOR_FAST); + + eina_binbuf_free(tbuf); + free(zmem); + } + + // Fill the list all the way to the needed index with buffer full of zero + while (list_index) compressed = eina_list_append(compressed, z); + l = eina_list_last(compressed); + } + + // Replace older buffer by newer buffer + tbuf = eina_list_data_get(l); + compressed = eina_list_prepend_relative(compressed, l, cbuf); + compressed = eina_list_remove_list(compressed, l); + if (tbuf != z) eina_binbuf_free(tbuf); + + return compressed; +} + +static unsigned int * +_efl_ui_model_exact_buffer_expand(unsigned int list_index, unsigned int *buffer, Eina_List *list) +{ + unsigned int found; + Eina_Binbuf *tmp; + Eina_List *l = NULL; + + if (!buffer) buffer = malloc(EFL_UI_MODEL_EXACT_CONTENT_LENGTH); + + found = _efl_ui_model_exact_list_find(list_index, list, &l); + + // Check if the data is in the list + if (!found) + { + // Not found -> everything is assumed to be zero + memset(buffer, 0, EFL_UI_MODEL_EXACT_CONTENT_LENGTH); + return buffer; + } + + // Found -> expand in buffer + tmp = eina_binbuf_manage_new((unsigned char*) buffer, EFL_UI_MODEL_EXACT_CONTENT_LENGTH, EINA_TRUE); + emile_expand(eina_list_data_get(l), tmp, EMILE_LZ4); + eina_binbuf_free(tmp); + + return buffer; +} + +static unsigned char +_efl_ui_model_exact_slot_find(Efl_Ui_Model_Exact_Data *pd, unsigned int index, + Eina_Bool width_get, Eina_Bool height_get) +{ + unsigned char lookup; + unsigned char found = EINA_C_ARRAY_LENGTH(pd->parent->slot); + + for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++) + { + // Check if the slot has valid content + if (!pd->parent->slot[lookup].defined) + continue; + if (pd->parent->slot[lookup].start_offset <= index && + index < pd->parent->slot[lookup].start_offset + EFL_UI_MODEL_EXACT_CONTENT) + found = lookup; + // Reduce usage to find unused slot. + if (pd->parent->slot[lookup].usage > 0) + pd->parent->slot[lookup].usage--; + } + + // Do we need to find a new slot? + if (found == EINA_C_ARRAY_LENGTH(pd->parent->slot)) + { + found = 0; + for (lookup = 0; lookup < EINA_C_ARRAY_LENGTH(pd->parent->slot); lookup++) + { + if (!pd->parent->slot[lookup].defined) + { + // Found an empty slot, let's use that. + found = lookup; + break; + } + if (pd->parent->slot[lookup].usage < pd->parent->slot[found].usage) + found = lookup; + } + + // Commit change back to the stored buffer list + if (pd->parent->slot[found].defined && + (pd->parent->slot[found].width || + pd->parent->slot[found].height)) + { + if (pd->parent->slot[found].width && + pd->parent->slot[found].decompressed.width) + pd->parent->compressed.width = _efl_ui_model_exact_slot_compress(index, + pd->parent->compressed.width, + pd->parent->slot[found].width); + if (pd->parent->slot[found].height && + pd->parent->slot[found].decompressed.height) + pd->parent->compressed.height = _efl_ui_model_exact_slot_compress(index, + pd->parent->compressed.height, + pd->parent->slot[found].height); + } + + pd->parent->slot[found].defined = EINA_TRUE; + pd->parent->slot[found].decompressed.width = EINA_FALSE; + pd->parent->slot[found].decompressed.height = EINA_FALSE; + pd->parent->slot[found].start_offset = index / EFL_UI_MODEL_EXACT_CONTENT; + } + + // Increase usage of the returnd slot for now + pd->parent->slot[found].usage++; + + // Unpack the data if requested + if (width_get && !pd->parent->slot[found].decompressed.width) + { + pd->parent->slot[found].width = _efl_ui_model_exact_buffer_expand(pd->parent->slot[found].start_offset, + pd->parent->slot[found].width, + pd->parent->compressed.width); + pd->parent->slot[found].decompressed.width = EINA_TRUE; + } + if (height_get && !pd->parent->slot[found].decompressed.height) + { + pd->parent->slot[found].height = _efl_ui_model_exact_buffer_expand(pd->parent->slot[found].start_offset, + pd->parent->slot[found].height, + pd->parent->compressed.height); + pd->parent->slot[found].decompressed.height = EINA_TRUE; + } + + return found; +} + +static Eina_Future * +_efl_ui_model_exact_efl_model_property_set(Eo *obj, Efl_Ui_Model_Exact_Data *pd, + const char *property, Eina_Value *value) +{ + if (pd->parent) + { + if (!strcmp(property, _efl_model_property_selfw)) + { + unsigned int index; + unsigned char found; + + index = efl_model_composite_index_get(obj); + found = _efl_ui_model_exact_slot_find(pd, index, EINA_TRUE, EINA_FALSE); + if (!eina_value_uint_convert(value, &pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT])) + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE); + // We succeeded so let's update the max total size width (As we only handle vertical list case at the moment) + if (pd->parent->total_size.width < pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]) + pd->parent->total_size.width = pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]; + return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT])); + } + if (!strcmp(property, _efl_model_property_selfh)) + { + unsigned int old_value; + unsigned int index; + unsigned char found; + + index = efl_model_composite_index_get(obj); + found = _efl_ui_model_exact_slot_find(pd, index, EINA_FALSE, EINA_TRUE); + old_value = pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT]; + if (!eina_value_uint_convert(value, &pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT])) + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_INCORRECT_VALUE); + // We succeeded so let's update the total size + pd->parent->total_size.height += pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT] - old_value; + return efl_loop_future_resolved(obj, eina_value_uint_init(pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT])); + } + // The following property are calculated by the model and so READ_ONLY + if (!strcmp(property, _efl_model_property_totalh)) + { + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY); + } + if (!strcmp(property, _efl_model_property_totalw)) + { + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY); + } + } + + if (!strcmp(property, _efl_model_property_itemw)) + { + // The exact model can not guess a general item size if asked + // and should refuse to remember anything like that. + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY); + } + if (!strcmp(property, _efl_model_property_itemh)) + { + // The exact model can not guess a general item size if asked + // and should refuse to remember anything like that. + return efl_loop_future_rejected(obj, EFL_MODEL_ERROR_READ_ONLY); + } + + return efl_model_property_set(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS), property, value); +} + +static Eina_Value * +_efl_ui_model_exact_efl_model_property_get(const Eo *obj, Efl_Ui_Model_Exact_Data *pd, + const char *property) +{ + if (pd->parent) + { + if (!strcmp(property, _efl_model_property_selfw)) + { + unsigned int index; + unsigned char found; + + index = efl_model_composite_index_get(obj); + found = _efl_ui_model_exact_slot_find(pd, index, EINA_TRUE, EINA_FALSE); + return eina_value_uint_new(pd->parent->slot[found].width[index % EFL_UI_MODEL_EXACT_CONTENT]); + } + if (!strcmp(property, _efl_model_property_selfh)) + { + unsigned int index; + unsigned char found; + + index = efl_model_composite_index_get(obj); + found = _efl_ui_model_exact_slot_find(pd, index, EINA_FALSE, EINA_TRUE); + return eina_value_uint_new(pd->parent->slot[found].height[index % EFL_UI_MODEL_EXACT_CONTENT]); + } + } + if (!strcmp(property, _efl_model_property_totalh)) + { + return eina_value_uint_new(pd->total_size.height); + } + if (!strcmp(property, _efl_model_property_totalw)) + { + return eina_value_uint_new(pd->total_size.width); + } + if (!strcmp(property, _efl_model_property_itemw)) + { + // The exact model can not guess a general item size if asked. + return eina_value_error_new(EAGAIN); + } + if (!strcmp(property, _efl_model_property_itemh)) + { + // The exact model can not guess a general item size if asked. + return eina_value_error_new(EAGAIN); + } + return efl_model_property_get(efl_super(obj, EFL_UI_MODEL_EXACT_CLASS), property); +} + +#include "efl_ui_model_exact.eo.c" diff --git a/src/lib/elementary/efl_ui_model_exact.eo b/src/lib/elementary/efl_ui_model_exact.eo new file mode 100644 index 0000000000..7a36202934 --- /dev/null +++ b/src/lib/elementary/efl_ui_model_exact.eo @@ -0,0 +1,17 @@ +class Efl.Ui.Model_Exact extends Efl.Ui.Model_Size +{ + [[Class to be used to store object item size for List/Grid View. + + This model provide "$total.width" and "$total.height" as an accumulated size of all + its children "$self.width" and "$self.height" @Efl.Model.property. This is currently + assuming a vertical list only. + + This model will return an error code EAGAIN for "$item.width" and "$item.height" all + the time and the View that use this Model should continue to get this value even after + computing all the "$self.width" and "$self.height" of this object children.]] + + implements { + Efl.Object.constructor; + Efl.Model.property { set; get; } + } +} \ No newline at end of file diff --git a/src/lib/elementary/elm_priv.h b/src/lib/elementary/elm_priv.h index aa97ee3c51..a5f187df17 100644 --- a/src/lib/elementary/elm_priv.h +++ b/src/lib/elementary/elm_priv.h @@ -71,6 +71,7 @@ # include "efl_datetime_manager.eo.h" # include "efl_ui_model_size.eo.h" # include "efl_ui_model_homogeneous.eo.h" +# include "efl_ui_model_exact.eo.h" extern const char *_efl_model_property_itemw; extern const char *_efl_model_property_itemh; diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 1b2b539731..c4453b7c9e 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -339,6 +339,7 @@ priv_eo_files = [ 'efl_ui_list_view_relayout.eo', 'efl_ui_model_size.eo', 'efl_ui_model_homogeneous.eo', + 'efl_ui_model_exact.eo', ] priv_eo_file_target = [] @@ -908,7 +909,8 @@ elementary_src = [ 'efl_ui_caching_factory.c', 'efl_ui_widget_factory.c', 'efl_ui_model_size.c', - 'efl_ui_model_homogeneous.c' + 'efl_ui_model_homogeneous.c', + 'efl_ui_model_exact.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]