From d9c421e8d2c13460d48b51a2ece2be24b2007133 Mon Sep 17 00:00:00 2001 From: Cedric Bail Date: Wed, 25 Oct 2017 18:03:00 -0700 Subject: [PATCH] ecore: refactor and migrate efl.model.composite.selection to the new efl.model API. --- src/lib/ecore/efl_model_composite_selection.c | 534 +++++++++++++++------ src/lib/ecore/efl_model_composite_selection.eo | 20 +- .../efl_model_composite_selection_children.eo | 4 +- 3 files changed, 384 insertions(+), 174 deletions(-) diff --git a/src/lib/ecore/efl_model_composite_selection.c b/src/lib/ecore/efl_model_composite_selection.c index 6450dc6..914a45f 100644 --- a/src/lib/ecore/efl_model_composite_selection.c +++ b/src/lib/ecore/efl_model_composite_selection.c @@ -10,227 +10,447 @@ #include "efl_model_composite_selection.eo.h" #include "efl_model_accessor_view_private.h" -typedef struct _Efl_Model_Composite_Selection_Data +typedef struct _Efl_Model_Composite_Selection_Data Efl_Model_Composite_Selection_Data; +typedef struct _Efl_Model_Composite_Selection_Children_Data Efl_Model_Composite_Selection_Children_Data; + +struct _Efl_Model_Composite_Selection_Data { - Efl_Model_Composite_Selection* obj; - struct { - Efl_Model* selected_child; - } exclusive_data; - Eina_Bool is_exclusive; - -} Efl_Model_Composite_Selection_Data; + unsigned long last; + + Eina_Bool exclusive : 1; + Eina_Bool none : 1; +}; -typedef struct _Efl_Model_Composite_Selection_Children_Data +struct _Efl_Model_Composite_Selection_Children_Data { - Efl_Model_Composite_Selection_Data* pd; -} Efl_Model_Composite_Selection_Children_Data; +}; static Eo* _efl_model_composite_selection_efl_object_constructor(Eo *obj, - Efl_Model_Composite_Selection_Data *pd EINA_UNUSED) + Efl_Model_Composite_Selection_Data *pd EINA_UNUSED) { - efl_constructor(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CLASS)); - efl_model_composite_boolean_property_add(obj, "selected", EINA_FALSE); - pd->obj = obj; + obj = efl_constructor(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CLASS)); + + efl_model_composite_boolean_add(obj, "selected", EINA_FALSE); + + pd->last = -1; + return obj; } -/***************************/ -static void _select_property_failed(void* data, Efl_Event const* event) +static Eina_Value +_commit_change(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED) { - Efl_Promise* promise = data; - Efl_Future_Event_Failure* fail = event->info; - efl_promise_failed_set(promise, fail->error); - efl_unref(promise); + Efl_Model_Composite_Selection_Data *pd; + Efl_Model *child = data; + Eina_Value *vc = NULL; + Eina_Value *selected = NULL; + Eina_Bool selflag = EINA_FALSE; + + if (v.type == EINA_VALUE_TYPE_ERROR) + goto on_error; + + vc = efl_model_property_get(child, "Child.index"); + selected = efl_model_property_get(child, "selected"); + + pd = efl_data_scope_get(efl_parent_get(child), EFL_MODEL_COMPOSITE_SELECTION_CLASS); + if (!pd) goto on_error; + + eina_value_bool_get(selected, &selflag); + if (selflag) + { + // select case + pd->none = EINA_FALSE; + eina_value_ulong_get(vc, &pd->last); + efl_event_callback_call(child, EFL_MODEL_COMPOSITE_SELECTION_EVENT_SELECTED, child); + } + else + { + // unselect case + unsigned long last; + + eina_value_ulong_get(vc, &last); + if (pd->last == last) + { + pd->last = 0; + pd->none = EINA_TRUE; + } + efl_event_callback_call(child, EFL_MODEL_COMPOSITE_SELECTION_EVENT_UNSELECTED, child); + } + + on_error: + eina_value_free(vc); + eina_value_free(selected); + return v; } -static void _select_property_then(void* data, Efl_Event const* event EINA_UNUSED) +static Eina_Value +_clear_child(void *data, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - Efl_Promise* promise = data; - Eina_Value* v = eina_value_new(EINA_VALUE_TYPE_UCHAR); - eina_value_set(v, EINA_TRUE); - efl_promise_value_set(promise, v, (Eina_Free_Cb)&eina_value_free); - efl_unref(promise); + Efl_Model *child = data; + + efl_del(child); + + return v; } -static void _select_slice_then(void* data EINA_UNUSED, Efl_Event const* event) +static Efl_Model * +_select_child_get(const Eina_Value *array, unsigned int idx) { - Efl_Future_Event_Success* success = event->info; - Eina_Accessor* accessor = eina_accessor_clone(success->value); - Eina_Value value; - Efl_Model* child; + Efl_Model *ret = NULL; - if (!eina_accessor_data_get(accessor, 0, (void **)&child)) return; - - eina_value_setup(&value, EINA_VALUE_TYPE_UCHAR); - eina_value_set(&value, EINA_TRUE); - - efl_future_then(efl_model_property_set(child, "selected", &value), - _select_property_then, _select_property_failed, NULL, efl_ref(success->next)); + if (eina_value_type_get(array) != EINA_VALUE_TYPE_ARRAY) + return NULL; + + if (idx >= eina_value_array_count(array)) + return NULL; + + eina_value_array_get(array, idx, &ret); + + return ret; } -static void _select_error(void* data EINA_UNUSED, Efl_Event const* event) +static Eina_Future * +_check_child_change(Efl_Model *child, Eina_Bool value) { - Efl_Future_Event_Failure* fail = event->info; - efl_promise_failed_set(fail->next, fail->error); + Eina_Future *r = NULL; + Eina_Value *prev; + Eina_Bool prevflag = EINA_FALSE; + + prev = efl_model_property_get(child, "selected"); + eina_value_bool_get(prev, &prevflag); + eina_value_free(prev); + + if (prevflag & value) + { + r = eina_future_resolved(efl_loop_future_scheduler_get(child), + eina_value_bool_init(value)); + } + else + { + r = efl_model_property_set(child, "selected", eina_value_bool_new(!!value)); + r = eina_future_then(r, _commit_change, child); + r = eina_future_then(r, _clear_child, child); + } + + return r; } -/***************************/ +static Eina_Future * +_select_child(Efl_Model *child) +{ + return _check_child_change(child, EINA_TRUE); +} -static Efl_Future* -_efl_model_composite_selection_select(Eo *obj, - Efl_Model_Composite_Selection_Data *pd, int idx) +static Eina_Future * +_unselect_child(Efl_Model *child) { - return efl_future_then(efl_model_children_slice_get(obj, idx, 1), - &_select_slice_then, - &_select_error, - NULL, pd); + return _check_child_change(child, EINA_FALSE); } -static void -_efl_model_composite_selection_exclusive_selection_set(Eo *obj EINA_UNUSED, - Efl_Model_Composite_Selection_Data *pd, Eina_Bool exclusive) +static Eina_Value +_select_slice_then(void *data EINA_UNUSED, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - pd->is_exclusive = exclusive; + Efl_Model *child = NULL; + Eina_Future *r; + + child = _select_child_get(&v, 0); + if (!child) goto on_error; + + r = _select_child(child); + return eina_future_as_value(r); + + on_error: + return v; } -static Eina_Bool -_efl_model_composite_selection_exclusive_selection_get(const Eo *obj EINA_UNUSED, - Efl_Model_Composite_Selection_Data *pd) +static Eina_Value +_unselect_slice_then(void *data EINA_UNUSED, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - return pd->is_exclusive; + Efl_Model *child = NULL; + Eina_Future *r; + + child = _select_child_get(&v, 0); + if (!child) goto on_error; + + r = _unselect_child(child); + return eina_future_as_value(r); + + on_error: + return v; } -static void -_exclusive_future_link_then_cb(void* data, Efl_Event const* event) +static Eina_Future * +_efl_model_composite_selection_efl_model_property_set(Eo *obj, + Efl_Model_Composite_Selection_Data *pd, + const char *property, Eina_Value *value) { - Efl_Future_Event_Success *success = event->info; - efl_promise_value_set(data, success->value, NULL); // We would need to move the value - // Needs to set exclusive_child - efl_unref(data); + Eina_Value vf = EINA_VALUE_EMPTY; + + if (!strcmp("exclusive", property)) + { + Eina_Bool exclusive = pd->exclusive; + + vf = eina_value_bool_init(exclusive); + eina_value_convert(value, &vf); + eina_value_bool_get(&vf, &exclusive); + + pd->exclusive = !!exclusive; + + return eina_future_resolved(efl_loop_future_scheduler_get(obj), vf); + } + + if (!strcmp("selected", property)) + { + Eina_Value vl = EINA_VALUE_EMPTY; + unsigned long l = 0; + Eina_Bool success = EINA_TRUE; + + vl = eina_value_ulong_init(0); + success &= eina_value_convert(value, &vl); + success &= eina_value_ulong_get(&vl, &l); + if (!success) + return eina_future_rejected(efl_loop_future_scheduler_get(obj), EFL_MODEL_ERROR_INCORRECT_VALUE); + + return efl_future_Eina_FutureXXX_then(obj, + eina_future_then(efl_model_children_slice_get(obj, l, 1), + _select_slice_then, obj)); + } + + return efl_model_property_set(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CLASS), property, value); } -static void -_exclusive_future_link_failed(void* data, Efl_Event const* event) +static Eina_Value * +_efl_model_composite_selection_efl_model_property_get(const Eo *obj, Efl_Model_Composite_Selection_Data *pd, const char *property) { - Efl_Future_Event_Failure *failed = event->info; - efl_promise_failed_set(data, failed->error); - efl_unref(data); + if (!strcmp("exclusive", property)) + return eina_value_bool_new(pd->exclusive); + if (!strcmp("selected", property)) + { + if (pd->none) + return eina_value_error_new(EFL_MODEL_ERROR_INCORRECT_VALUE); + else + return eina_value_ulong_new(pd->last); + } + + return efl_model_property_get(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CLASS), property); } -static void -_exclusive_unselected_then_cb(void* data, Efl_Event const* event) +static Eina_Value +_regenerate_error(void *data, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - Efl_Future_Event_Success *success = event->info; - Eina_Value* true_value = eina_value_new(EINA_VALUE_TYPE_UCHAR); - eina_value_set(true_value, EINA_TRUE); - efl_future_then(efl_model_property_set(data, "selected", true_value), - _exclusive_future_link_then_cb, _exclusive_future_link_failed, - NULL, efl_ref(success->next)); - efl_unref(data); + Eina_Error *error = data; + Eina_Value r = v; + + if (v.type == EINA_VALUE_TYPE_ERROR) + goto cleanup; + + r = eina_value_error_init(*error); + + cleanup: + free(error); + + return r; } -static void -_exclusive_unselected_failed(void* data, Efl_Event const* event) +static Eina_Value +_untangle_array(void *data, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - Efl_Future_Event_Failure *failed = event->info; - efl_promise_failed_set(data, failed->error); - efl_unref(data); + Efl_Model *child = data; + Eina_Value va = EINA_VALUE_EMPTY; + Eina_Future *f; + + if (v.type == EINA_VALUE_TYPE_ERROR) + { + // We need to roll back the change, which means in this + // case to unselect this child as this is the only case + // where we could end up here. + Eina_Error *error = calloc(1, sizeof (Eina_Error)); + + f = efl_model_property_set(efl_super(child, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS), + "selected", eina_value_bool_new(EINA_FALSE)); + // Once this is done, we need to repropagate the error + eina_value_error_get(&v, error); + f = eina_future_then(f, _regenerate_error, error); + + return eina_future_as_value(f); + } + + // Only return the commit change, not the result of the unselect + eina_value_array_get(&v, 0, &va); + return va; } -static Efl_Future * -_efl_model_composite_selection_children_efl_model_property_set(Eo *obj EINA_UNUSED, - Efl_Model_Composite_Selection_Children_Data *pd, const char *property, const Eina_Value *value) +static Eina_Future * +_efl_model_composite_selection_children_efl_model_property_set(Eo *obj, + Efl_Model_Composite_Selection_Children_Data *pd EINA_UNUSED, + const char *property, Eina_Value *value) { - if(!strcmp("selected", property)) + Eina_Value *ve = NULL; + Eina_Value *vb = NULL; + Eina_Value lvb = EINA_VALUE_EMPTY; + Eina_Bool success = EINA_TRUE; + Eina_Bool exclusive = EINA_FALSE; + Eina_Bool prevflag = EINA_FALSE, newflag = EINA_FALSE; + Eina_Future *chain; + + if (strcmp("selected", property)) + return efl_model_property_set(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS), + property, value); + + vb = efl_model_property_get(obj, "selected"); + success &= eina_value_bool_get(vb, &prevflag); + eina_value_free(vb); + + lvb = eina_value_bool_init(prevflag); + success &= eina_value_convert(value, &lvb); + success &= eina_value_bool_get(&lvb, &newflag); + eina_value_flush(&lvb); + + if (!success) + return eina_future_rejected(efl_loop_future_scheduler_get(obj), EFL_MODEL_ERROR_INCORRECT_VALUE); + + // Nothing changed + if (newflag == prevflag) + return eina_future_resolved(efl_loop_future_scheduler_get(obj), + eina_value_bool_init(newflag)); + + ve = efl_model_property_get(efl_parent_get(obj), "exclusive"); + eina_value_bool_get(ve, &exclusive); + eina_value_free(ve); + + // First store the new value in the boolean model we inherit from + chain = efl_model_property_set(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS), + "selected", value); + + // Now act ! + if (exclusive) { - unsigned long v = EINA_FALSE; - if(eina_value_type_get(value) != EINA_VALUE_TYPE_ULONG) + // We are here either, because we weren't and are after this call + // or because we were selected and are not anymore. In the later case, + // there is nothing special to do, just normal commit change will do. + if (!newflag) { - Eina_Value to; - eina_value_setup(&to, EINA_VALUE_TYPE_ULONG); - if(eina_value_convert(value, &to)) - eina_value_get(&to, &v); - eina_value_flush(&to); + Efl_Model_Composite_Selection_Data *ppd; + Eina_Value *index; + unsigned int i = 0; + + index = efl_model_property_get(obj, "child.index"); + if (!eina_value_uint_get(index, &i)) + goto commit_change; + + ppd = efl_data_scope_get(efl_parent_get(obj), EFL_MODEL_COMPOSITE_SELECTION_CLASS); + if (ppd->last == i && !newflag) + ppd->none = EINA_TRUE; } else - eina_value_get(value, &v); - - if(v && pd->pd->is_exclusive) { - if(pd->pd->exclusive_data.selected_child) + Eina_Value *vs; + unsigned long selected = 0; + Eina_Bool success; + + // In this case we need to first unselect the previously selected one + // and then commit the change to this one. + + // Fetch the one to unselect + vs = efl_model_property_get(efl_parent_get(obj), "selected"); + // Check if there was any selected + if (eina_value_type_get(vs) == EINA_VALUE_TYPE_ERROR) { - // unset current selected - // set this child as current - // bookkeep this child as current selection - // return with future for this asynchronous task - Eina_Value* false_value = eina_value_new(EINA_VALUE_TYPE_UCHAR); - eina_value_set(false_value, EINA_FALSE); - return - efl_future_then(efl_model_property_set - (pd->pd->exclusive_data.selected_child, - property, false_value), - _exclusive_unselected_then_cb, - _exclusive_unselected_failed, NULL, - efl_ref(obj)); + eina_value_free(vs); + goto commit_change; } - else - { - Efl_Promise *promise = efl_add(EFL_PROMISE_CLASS, efl_main_loop_get()); - Efl_Future *rfuture = efl_promise_future_get(promise); - Eina_Value* true_value = eina_value_new(EINA_VALUE_TYPE_UCHAR); - eina_value_set(true_value, EINA_TRUE); - efl_future_then(efl_model_property_set - (efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS), - "selected", true_value), - _exclusive_future_link_then_cb, _exclusive_future_link_failed, - NULL, efl_ref(promise)); - return rfuture; - } - } - else - { - + success = eina_value_ulong_get(vs, &selected); + eina_value_free(vs); + + if (!success) + return eina_future_rejected(efl_loop_future_scheduler_get(obj), + EFL_MODEL_ERROR_INCORRECT_VALUE); + + // There was, need to unselect the previous one along setting the new value + chain = eina_future_all(chain, + eina_future_then(efl_model_children_slice_get(efl_parent_get(obj), selected, 1), + _unselect_slice_then, NULL)); + + chain = eina_future_then(chain, _untangle_array, obj); } } - return efl_model_property_set(efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS), - property, value); -} + commit_change: + chain = eina_future_then(chain, _commit_change, obj); -static Eo* _construct_children(void* pdata, Eo* child) -{ - Efl_Model_Composite_Selection_Data* pd = pdata; - Eo* new_child = efl_add_ref(EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS, NULL); - Efl_Model_Composite_Selection_Children_Data* data = efl_data_scope_get - (new_child, EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS); - data->pd = pd; - efl_composite_attach(new_child, efl_ref(child)); - return new_child; + return efl_future_Eina_FutureXXX_then(obj, chain); } -static void _composited_children_slice_get_then(void* data, Efl_Event const* event) +typedef struct _Selection_Children_Request Selection_Children_Request; +struct _Selection_Children_Request { - Efl_Future_Event_Success* success = event->info; - Eina_Accessor* accessor = success->value; - efl_promise_value_set(success->next, - efl_model_accessor_view_new(eina_accessor_clone(accessor), &_construct_children, - data), - (Eina_Free_Cb)&eina_accessor_free); -} -static void _composited_children_slice_get_fail(void* data EINA_UNUSED, Efl_Event const* event) + Efl_Model *parent; + + unsigned int start; +}; + +static Eina_Value +_slice_get(void *data, + const Eina_Value v, + const Eina_Future *dead_future EINA_UNUSED) { - Efl_Future_Event_Failure* failure = event->info; - efl_promise_failed_set(failure->next, failure->error); + Selection_Children_Request *req = data; + unsigned int length, it; + Eo *composited = NULL; + Eina_Value r = EINA_VALUE_EMPTY; + + if (v.type == EINA_VALUE_TYPE_ERROR) + goto error; + + eina_value_array_setup(&r, EINA_VALUE_TYPE_OBJECT, 4); + + EINA_VALUE_ARRAY_FOREACH(&v, length, it, composited) + { + Eo *compositing; + + compositing = efl_add(EFL_MODEL_COMPOSITE_SELECTION_CHILDREN_CLASS, req->parent, + efl_model_composite_boolean_children_index_set(efl_added, req->start + it), + efl_ui_view_model_set(efl_added, composited)); + eina_value_array_append(&r, compositing); + } + + error: + efl_unref(req->parent); + free(req); + + return r; } -static Efl_Future * -_efl_model_composite_selection_efl_model_children_slice_get(Eo *obj, Efl_Model_Composite_Selection_Data *pd, unsigned int start, unsigned int count) +static Eina_Future * +_efl_model_composite_selection_efl_model_children_slice_get(Eo *obj, + Efl_Model_Composite_Selection_Data *pd EINA_UNUSED, + unsigned int start, unsigned int count) { - Efl_Future* composited_future = efl_model_children_slice_get - (efl_super(obj, EFL_MODEL_COMPOSITE_SELECTION_CLASS),start, count); - return efl_future_then(composited_future, &_composited_children_slice_get_then, - &_composited_children_slice_get_fail, NULL, pd); + Selection_Children_Request *req; + Eina_Future *f; + + req = calloc(1, sizeof (Selection_Children_Request)); + if (!req) return eina_future_rejected(efl_loop_future_scheduler_get(obj), ENOMEM); + req->parent = efl_ref(obj); + req->start = start; + + // NOTE: We do jump on purpose EFL_MODEL_COMPOSITE_BOOLEAN_CLASS here + f = efl_model_children_slice_get(efl_super(obj, EFL_MODEL_COMPOSITE_BOOLEAN_CLASS), + start, count); + f = eina_future_then(f, _slice_get, req); + + return efl_future_Eina_FutureXXX_then(obj, f); } #include "efl_model_composite_selection.eo.c" diff --git a/src/lib/ecore/efl_model_composite_selection.eo b/src/lib/ecore/efl_model_composite_selection.eo index 5e6a9c8..20ef5e2 100644 --- a/src/lib/ecore/efl_model_composite_selection.eo +++ b/src/lib/ecore/efl_model_composite_selection.eo @@ -1,23 +1,13 @@ class Efl.Model_Composite_Selection (Efl.Model_Composite_Boolean) { [[Efl model composite selection class]] - methods { - select { - [[Select composition]] - params { - idx: int; [[Index]] - } - return: future; [[Future on the selected composition]] - } - @property exclusive_selection { - [[Exclusive Selection property]] - values { - exclusive: bool; [[$true is selection is exclusive, $flase otherwise]] - } - } - } implements { Efl.Object.constructor; Efl.Model.children_slice_get; + Efl.Model.property { get; set; } + } + events { + selected; + unselected; } } diff --git a/src/lib/ecore/efl_model_composite_selection_children.eo b/src/lib/ecore/efl_model_composite_selection_children.eo index 4c6ef7c..f3a9bd8 100644 --- a/src/lib/ecore/efl_model_composite_selection_children.eo +++ b/src/lib/ecore/efl_model_composite_selection_children.eo @@ -1,7 +1,7 @@ -class Efl.Model_Composite_Selection_Children (Efl.Object, Efl.Model) +class Efl.Model_Composite_Selection_Children (Efl.Model_Composite_Boolean_Children) { [[Efl model composite selection children class]] implements { - Efl.Model.property_set; + Efl.Model.property { set; } } } -- 2.7.4