729 lines
22 KiB
C
729 lines
22 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "elementary_config.h"
|
|
#endif
|
|
|
|
#include <Elementary.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "elm_priv.h"
|
|
#include "efl_ui_list_view_relayout.eo.h"
|
|
#include "efl_ui_list_view_seg_array.h"
|
|
|
|
#define MY_CLASS EFL_UI_LIST_VIEW_PRECISE_LAYOUTER_CLASS
|
|
|
|
typedef struct _Efl_Ui_List_View_Precise_Layouter_Data
|
|
{
|
|
Efl_Model* model;
|
|
Efl_Ui_List_View_Model *modeler;
|
|
Ecore_Job *calc_job;
|
|
Efl_Ui_List_View_Seg_Array *seg_array;
|
|
|
|
Eina_Size2D min;
|
|
|
|
unsigned int calc_progress;
|
|
|
|
int count_total;
|
|
|
|
Eina_Bool initialized : 1;
|
|
Eina_Bool recalc : 1;
|
|
Eina_Bool resize : 1;
|
|
} Efl_Ui_List_View_Precise_Layouter_Data;
|
|
|
|
typedef struct _Efl_Ui_List_View_Precise_Layouter_Node_Data
|
|
{
|
|
Eina_Size2D min;
|
|
Eina_Size2D size;
|
|
|
|
Eina_Bool realized;
|
|
} Efl_Ui_List_View_Precise_Layouter_Node_Data;
|
|
|
|
typedef struct _Efl_Ui_List_View_Precise_Layouter_Callback_Data
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd;
|
|
Efl_Ui_List_View_Layout_Item *item;
|
|
} Efl_Ui_List_View_Precise_Layouter_Callback_Data;
|
|
|
|
#include "efl_ui_list_view_precise_layouter.eo.h"
|
|
|
|
static void _efl_ui_list_view_relayout_layout_do(Efl_Ui_List_View_Precise_Layouter_Data *);
|
|
static Eina_Bool _initilize(Eo *, Efl_Ui_List_View_Precise_Layouter_Data*, Efl_Ui_List_View_Model*, Efl_Ui_List_View_Seg_Array*);
|
|
static void _finalize(Eo *, Efl_Ui_List_View_Precise_Layouter_Data*);
|
|
static void _node_realize(Efl_Ui_List_View_Precise_Layouter_Data*, Efl_Ui_List_View_Seg_Array_Node*);
|
|
static void _node_unrealize(Efl_Ui_List_View_Precise_Layouter_Data*, Efl_Ui_List_View_Seg_Array_Node*);
|
|
|
|
static void
|
|
_item_size_calc(Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Layout_Item* item)
|
|
{
|
|
int boxx, boxy, boxw, boxh, boxl, boxr, boxt, boxb, pad[4];
|
|
double align[2];
|
|
Eina_Bool fill[2];
|
|
Eina_Size2D max;
|
|
|
|
efl_gfx_hint_margin_get(item->layout, &pad[0], &pad[1], &pad[2], &pad[3]);
|
|
evas_object_geometry_get(pd->modeler, &boxx, &boxy, &boxw, &boxh);
|
|
efl_gfx_hint_margin_get(pd->modeler, &boxl, &boxr, &boxt, &boxb);
|
|
efl_gfx_hint_align_get(item->layout, &align[0], &align[1]);
|
|
efl_gfx_hint_fill_get(item->layout, &fill[0], &fill[1]);
|
|
max = efl_gfx_hint_size_max_get(item->layout);
|
|
|
|
// box outer margin
|
|
boxw -= boxl + boxr;
|
|
boxh -= boxt + boxb;
|
|
boxx += boxl;
|
|
boxy += boxt;
|
|
|
|
if (EINA_DBL_EQ(align[0], -1))
|
|
{
|
|
align[0] = 0.5;
|
|
fill[0] = EINA_TRUE;
|
|
}
|
|
else if (align[0] < 0)
|
|
{
|
|
align[0] = 0;
|
|
}
|
|
if (EINA_DBL_EQ(align[1], -1))
|
|
{
|
|
align[1] = 0.5;
|
|
fill[1] = EINA_TRUE;
|
|
}
|
|
else if (align[1] < 0)
|
|
{
|
|
align[1] = 0;
|
|
}
|
|
|
|
if (align[0] > 1) align[0] = 1;
|
|
if (align[1] > 1) align[1] = 1;
|
|
|
|
if (max.w <= 0) max.w = INT_MAX;
|
|
if (max.h <= 0) max.h = INT_MAX;
|
|
if (max.w < item->min.w) max.w = item->min.w;
|
|
if (max.h < item->min.h) max.h = item->min.h;
|
|
|
|
// horizontally
|
|
if (max.w < INT_MAX)
|
|
{
|
|
item->size.w = MIN(MAX(item->min.w - pad[0] - pad[1], max.w), boxw);
|
|
item->pos.x = boxx + pad[0];
|
|
}
|
|
else if (fill[0])
|
|
{
|
|
// fill x
|
|
item->size.w = boxw - pad[0] - pad[1];
|
|
item->pos.x = boxx + pad[0];
|
|
}
|
|
else
|
|
{
|
|
item->size.w = item->min.w - pad[0] - pad[1];
|
|
item->pos.x = boxx + ((boxw - item->size.w) * align[0]) + pad[0];
|
|
}
|
|
|
|
// vertically
|
|
if (max.h < INT_MAX)
|
|
{
|
|
item->size.h = MIN(MAX(item->min.h - pad[2] - pad[3], max.h), boxh);
|
|
item->pos.y = boxy + pad[2];
|
|
}
|
|
else if (fill[1])
|
|
{
|
|
// fill y
|
|
item->size.h = item->min.h - pad[2] - pad[3];
|
|
item->pos.y = boxy + pad[2];
|
|
}
|
|
else
|
|
{
|
|
item->size.h = item->min.h - pad[2] - pad[3];
|
|
item->pos.y = boxy + ((item->min.h - item->size.h) * align[1]) + pad[2];
|
|
}
|
|
}
|
|
|
|
static Eina_Size2D
|
|
_item_min_calc(Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Layout_Item* item)
|
|
{
|
|
Efl_Ui_List_View_Seg_Array_Node *itemnode = item->tree_node;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata;
|
|
Efl_Ui_List_View_Layout_Item *layout_item;
|
|
int i, pad[4];
|
|
|
|
Eina_Size2D min = efl_gfx_hint_size_combined_min_get(item->layout);
|
|
|
|
efl_gfx_hint_margin_get(item->layout, &pad[0], &pad[1], &pad[2], &pad[3]);
|
|
min.w += pad[0] + pad[1];
|
|
min.h += pad[2] + pad[3];
|
|
|
|
if (item->min.h == min.h && item->min.w == min.w)
|
|
return min;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(itemnode, min);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(itemnode->layout_data, min);
|
|
nodedata = itemnode->layout_data;
|
|
|
|
pd->min.h += min.h - item->min.h;
|
|
nodedata->min.h += min.h - item->min.h;
|
|
|
|
if (nodedata->min.w <= min.w)
|
|
nodedata->min.w = min.w;
|
|
else if (nodedata->min.w == item->min.w)
|
|
{
|
|
nodedata->min.w = 0;
|
|
for (i = 0; i != itemnode->length; ++i)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)itemnode->pointers[i];
|
|
if (nodedata->min.w < layout_item->min.w)
|
|
nodedata->min.w = layout_item->min.w;
|
|
|
|
if (item->min.w == layout_item->min.w)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pd->min.w <= min.w)
|
|
pd->min.w = min.w;
|
|
else if (pd->min.w == item->min.w)
|
|
{
|
|
Efl_Ui_List_View_Seg_Array_Node *node2;
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
pd->min.w = min.w;
|
|
|
|
EINA_ACCESSOR_FOREACH(nodes, i, node2)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata2 = node2->layout_data;
|
|
if (pd->min.w < nodedata2->min.w)
|
|
pd->min.w = nodedata2->min.w;
|
|
|
|
if (item->min.w == nodedata2->min.w)
|
|
break;
|
|
}
|
|
eina_accessor_free(nodes);
|
|
}
|
|
|
|
item->min.w = min.w;
|
|
item->min.h = min.h;
|
|
return item->min;
|
|
}
|
|
|
|
static void
|
|
_on_item_size_hint_change(void *data, Evas *e EINA_UNUSED,
|
|
Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Callback_Data *cb_data = data;
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd = cb_data->pd;
|
|
Efl_Ui_List_View_Layout_Item *item = cb_data->item;;
|
|
Efl_Ui_List_View_Seg_Array_Node *node = item->tree_node;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = node->layout_data;
|
|
|
|
_item_min_calc(pd, item);
|
|
if (!nodedata->realized)
|
|
{
|
|
free(evas_object_event_callback_del(item->layout, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_item_size_hint_change));
|
|
efl_ui_list_view_model_unrealize(pd->modeler, item);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_on_modeler_resize(void *data, Evas *e EINA_UNUSED,
|
|
Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd = data;
|
|
pd->resize = EINA_TRUE;
|
|
}
|
|
|
|
typedef struct _Request Request;
|
|
struct _Request
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd;
|
|
unsigned int index;
|
|
};
|
|
|
|
static Eina_Value
|
|
_children_get(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
|
|
{
|
|
Request *r = data;
|
|
|
|
if (eina_value_type_get(&v) == EINA_VALUE_TYPE_ERROR)
|
|
goto on_error;
|
|
|
|
efl_ui_list_view_seg_array_insert_value(r->pd->seg_array, r->index, v);
|
|
r->pd->recalc = EINA_TRUE;
|
|
evas_object_smart_changed(r->pd->modeler);
|
|
|
|
on_error:
|
|
free(r);
|
|
return v;
|
|
}
|
|
|
|
static void
|
|
_child_added_cb(void *data, const Efl_Event *event)
|
|
{
|
|
Efl_Model_Children_Event* evt = event->info;
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd = data;
|
|
Eina_Future *f;
|
|
Request *r;
|
|
|
|
r = calloc(1, sizeof (Request));
|
|
if (!r) return;
|
|
|
|
r->index = evt->index;
|
|
r->pd = pd;
|
|
|
|
f = efl_model_children_slice_get(pd->model, evt->index, 1);
|
|
f = eina_future_then(f, _children_get, r, NULL);
|
|
}
|
|
|
|
static void
|
|
_child_removed_cb(void *data, const Efl_Event *event)
|
|
{
|
|
Efl_Model_Children_Event* evt = event->info;
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd = data;
|
|
Efl_Ui_List_View_Layout_Item *layout_item, *litem;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata;
|
|
Efl_Ui_List_View_Seg_Array_Node *itemnode;
|
|
int i;
|
|
|
|
litem = efl_ui_list_view_seg_array_remove(pd->seg_array, evt->index);
|
|
if (!litem) return;
|
|
|
|
itemnode = litem->tree_node;
|
|
nodedata = itemnode->layout_data;
|
|
|
|
free(evas_object_event_callback_del(litem->layout, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_item_size_hint_change));
|
|
|
|
pd->min.h -= litem->min.h;
|
|
nodedata->min.h -= litem->min.h;
|
|
|
|
if (nodedata->min.w == litem->min.w)
|
|
nodedata->min.w = 0;
|
|
|
|
for (i = 0; i != itemnode->length; ++i)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)itemnode->pointers[i];
|
|
if (nodedata->min.w < layout_item->min.w)
|
|
nodedata->min.w = layout_item->min.w;
|
|
|
|
if (litem->min.w == layout_item->min.w)
|
|
{
|
|
nodedata->min.w = layout_item->min.w;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pd->min.w == litem->min.w)
|
|
{
|
|
Efl_Ui_List_View_Seg_Array_Node *node2;
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
pd->min.w = 0;
|
|
|
|
EINA_ACCESSOR_FOREACH(nodes, i, node2)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata2 = node2->layout_data;
|
|
if (pd->min.w < nodedata2->min.w)
|
|
pd->min.w = nodedata2->min.w;
|
|
|
|
if (litem->min.w == nodedata2->min.w)
|
|
break;
|
|
}
|
|
eina_accessor_free(nodes);
|
|
}
|
|
efl_ui_list_view_model_unrealize(pd->modeler, litem);
|
|
|
|
free(litem);
|
|
pd->recalc = EINA_TRUE;
|
|
evas_object_smart_changed(pd->modeler);
|
|
}
|
|
|
|
static void
|
|
_child_count_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd = data;
|
|
pd->count_total = efl_model_children_count_get(pd->model);
|
|
if (pd->count_total)
|
|
efl_event_callback_del(pd->model, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, _child_count_changed_cb, pd);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_initilize(Eo *obj EINA_UNUSED, Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Model *modeler, Efl_Ui_List_View_Seg_Array *seg_array)
|
|
{
|
|
if(pd->initialized)
|
|
return EINA_TRUE;
|
|
|
|
efl_replace(&pd->modeler, modeler);
|
|
|
|
if(!pd->model || !pd->modeler)
|
|
return EINA_FALSE;
|
|
|
|
pd->recalc = EINA_TRUE;
|
|
pd->initialized = EINA_TRUE;
|
|
|
|
pd->seg_array = seg_array;
|
|
|
|
efl_ui_list_view_model_load_range_set(pd->modeler, 0, pd->count_total); // load all
|
|
efl_event_callback_add(pd->model, EFL_MODEL_EVENT_CHILD_ADDED, _child_added_cb, pd);
|
|
efl_event_callback_add(pd->model, EFL_MODEL_EVENT_CHILD_REMOVED, _child_removed_cb, pd);
|
|
|
|
evas_object_event_callback_add(modeler, EVAS_CALLBACK_RESIZE, _on_modeler_resize, pd);
|
|
pd->min.w = 0;
|
|
pd->min.h = 0;
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_finalize(Eo *obj EINA_UNUSED, Efl_Ui_List_View_Precise_Layouter_Data *pd)
|
|
{
|
|
Efl_Ui_List_View_Seg_Array_Node* node;
|
|
int i = 0;
|
|
|
|
if (pd->model)
|
|
{
|
|
efl_event_callback_del(pd->model, EFL_MODEL_EVENT_CHILD_ADDED, _child_added_cb, pd);
|
|
efl_event_callback_del(pd->model, EFL_MODEL_EVENT_CHILD_REMOVED, _child_removed_cb, pd);
|
|
pd->count_total = 0;
|
|
}
|
|
|
|
if (pd->seg_array)
|
|
{
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
EINA_ACCESSOR_FOREACH(nodes, i, node)
|
|
{
|
|
_node_unrealize(pd, node);
|
|
free(node->layout_data);
|
|
}
|
|
|
|
eina_accessor_free(nodes);
|
|
}
|
|
|
|
pd->min.w = 0;
|
|
pd->min.h = 0;
|
|
|
|
if (pd->modeler)
|
|
{
|
|
evas_object_event_callback_del_full(pd->modeler, EVAS_CALLBACK_RESIZE, _on_modeler_resize, pd);
|
|
efl_ui_list_view_model_min_size_set(pd->modeler, pd->min);
|
|
}
|
|
|
|
pd->seg_array = NULL;
|
|
efl_replace(&pd->modeler, NULL);
|
|
|
|
pd->initialized = EINA_FALSE;
|
|
pd->recalc = EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_node_realize(Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Seg_Array_Node *node)
|
|
{
|
|
Efl_Ui_List_View_Layout_Item* layout_item;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = node->layout_data;
|
|
int i;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(nodedata);
|
|
if (nodedata->realized)
|
|
return;
|
|
|
|
nodedata->realized = EINA_TRUE;
|
|
|
|
for (i = 0; i != node->length; ++i)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)node->pointers[i];
|
|
efl_ui_list_view_model_realize(pd->modeler, layout_item);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_node_unrealize(Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Seg_Array_Node *node)
|
|
{
|
|
Efl_Ui_List_View_Layout_Item* layout_item;
|
|
Efl_Ui_List_View_Precise_Layouter_Callback_Data *cb_data;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = node->layout_data;
|
|
int i;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(nodedata);
|
|
if (!nodedata->realized)
|
|
return;
|
|
|
|
nodedata->realized = EINA_FALSE;
|
|
|
|
for (i = 0; i != node->length; ++i)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)node->pointers[i];
|
|
if (layout_item->layout)
|
|
{
|
|
cb_data = evas_object_event_callback_del(layout_item->layout, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_item_size_hint_change);
|
|
free(cb_data);
|
|
efl_ui_list_view_model_unrealize(pd->modeler, layout_item);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_calc_range(Efl_Ui_List_View_Precise_Layouter_Data *pd)
|
|
{
|
|
Efl_Ui_List_View_Seg_Array_Node *node;
|
|
Evas_Coord ch, ny;
|
|
Eina_Rect vgmt;
|
|
Eina_Position2D spos;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata;
|
|
int i;
|
|
|
|
vgmt = efl_ui_scrollable_viewport_geometry_get(pd->modeler);
|
|
spos = efl_ui_scrollable_content_pos_get(pd->modeler);
|
|
|
|
ny = spos.y - (vgmt.h / 2);
|
|
if (ny < 0) spos.y = 0;
|
|
else spos.y = ny;
|
|
vgmt.h *= 2;
|
|
|
|
ch = 0;
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
EINA_ACCESSOR_FOREACH(nodes, i, node)
|
|
{
|
|
nodedata = node->layout_data;
|
|
if (!nodedata || !nodedata->min.h)
|
|
continue;
|
|
|
|
if ((ch > spos.y || nodedata->min.h + ch > spos.y) && (ch < (spos.y + vgmt.h) || nodedata->min.h + ch < spos.y + vgmt.h))
|
|
_node_realize(pd, node);
|
|
else
|
|
_node_unrealize(pd, node);
|
|
|
|
ch += nodedata->min.h;
|
|
}
|
|
eina_accessor_free(nodes);
|
|
}
|
|
|
|
static void
|
|
_calc_size_job(void *data)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Data *pd;
|
|
Efl_Ui_List_View_Seg_Array_Node *node;
|
|
Efl_Ui_List_View_Layout_Item *layout_item;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata;
|
|
Eo *obj = data;
|
|
int i;
|
|
double start_time = ecore_time_get();
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(data);
|
|
pd = efl_data_scope_get(obj, MY_CLASS);
|
|
if (EINA_UNLIKELY(!pd)) return;
|
|
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
while (eina_accessor_data_get(nodes, pd->calc_progress, (void **)&node))
|
|
{
|
|
pd->calc_progress++;
|
|
if (!node->layout_data)
|
|
node->layout_data = calloc(1, sizeof(Efl_Ui_List_View_Precise_Layouter_Node_Data));
|
|
|
|
nodedata = node->layout_data;
|
|
if (pd->calc_progress == 1)
|
|
nodedata->realized = EINA_TRUE;
|
|
|
|
for (i = 0; i != node->length; ++i)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)node->pointers[i];
|
|
EINA_SAFETY_ON_NULL_RETURN(layout_item);
|
|
|
|
// cache size of new items
|
|
if ((layout_item->min.w == 0) && (layout_item->min.h == 0) && (!layout_item->layout))
|
|
efl_ui_list_view_model_realize(pd->modeler, layout_item);
|
|
}
|
|
|
|
if ( (ecore_time_get() - start_time ) > 0.01 )
|
|
{
|
|
ecore_job_del(pd->calc_job);
|
|
pd->calc_job = ecore_job_add(_calc_size_job, obj);
|
|
eina_accessor_free(nodes);
|
|
return;
|
|
}
|
|
}
|
|
eina_accessor_free(nodes);
|
|
pd->calc_progress = 0;
|
|
pd->calc_job = NULL;
|
|
pd->recalc = EINA_FALSE;
|
|
}
|
|
|
|
EOLIAN static Efl_Object *
|
|
_efl_ui_list_view_precise_layouter_efl_object_constructor(Eo *obj, Efl_Ui_List_View_Precise_Layouter_Data *pd)
|
|
{
|
|
obj = efl_constructor(efl_super(obj, MY_CLASS));
|
|
pd->initialized = EINA_FALSE;
|
|
|
|
return obj;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_list_view_precise_layouter_efl_ui_list_view_relayout_content_created(Eo *obj EINA_UNUSED, Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Ui_List_View_Layout_Item *item)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Callback_Data *cb_data;
|
|
EINA_SAFETY_ON_NULL_RETURN(item);
|
|
EINA_SAFETY_ON_NULL_RETURN(item->layout);
|
|
Efl_Ui_List_View_Seg_Array_Node *node = item->tree_node;
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = node->layout_data;
|
|
|
|
Eina_Size2D min = _item_min_calc(pd, item);
|
|
|
|
if (min.w && min.h && !nodedata->realized)
|
|
{
|
|
efl_ui_list_view_model_unrealize(pd->modeler, item);
|
|
return;
|
|
}
|
|
|
|
cb_data = calloc(1, sizeof(Efl_Ui_List_View_Precise_Layouter_Callback_Data));
|
|
if (!cb_data) return;
|
|
cb_data->pd = pd;
|
|
cb_data->item = item;
|
|
evas_object_event_callback_add(item->layout, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_item_size_hint_change, cb_data);
|
|
|
|
_item_size_calc(pd, item);
|
|
}
|
|
|
|
EOLIAN static Eina_List *
|
|
_efl_ui_list_view_precise_layouter_efl_ui_list_view_relayout_elements_get(const Eo *obj EINA_UNUSED, Efl_Ui_List_View_Precise_Layouter_Data *pd)
|
|
{
|
|
Eina_List *elements_order = NULL;
|
|
Efl_Ui_List_View_Layout_Item* layout_item;
|
|
Efl_Ui_List_View_Seg_Array_Node *items_node;
|
|
int i, j = 0;
|
|
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
EINA_ACCESSOR_FOREACH(nodes, i, items_node)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = items_node->layout_data;
|
|
if (!nodedata || !nodedata->realized)
|
|
continue;
|
|
|
|
for (j = 0; j != items_node->length;++j)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)items_node->pointers[j];
|
|
if (layout_item->layout)
|
|
elements_order = eina_list_append(elements_order, layout_item->layout);
|
|
}
|
|
}
|
|
|
|
eina_accessor_free(nodes);
|
|
return elements_order;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_list_view_precise_layouter_efl_ui_list_view_relayout_model_set(Eo *obj, Efl_Ui_List_View_Precise_Layouter_Data *pd, Efl_Model *model)
|
|
{
|
|
_finalize(obj, pd);
|
|
|
|
efl_replace(&pd->model, model);
|
|
|
|
if (pd->model)
|
|
{
|
|
pd->count_total = efl_model_children_count_get(pd->model);
|
|
if (pd->count_total && pd->modeler)
|
|
efl_ui_list_view_model_load_range_set(pd->modeler, 0, pd->count_total); // load all
|
|
else
|
|
efl_event_callback_add(pd->model, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, _child_count_changed_cb, pd);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_efl_ui_list_view_relayout_layout_do(Efl_Ui_List_View_Precise_Layouter_Data *pd)
|
|
{
|
|
Eina_Rect vgmt;
|
|
Eina_Position2D spos;
|
|
double cur_pos = 0;
|
|
Efl_Ui_List_View_Layout_Item* layout_item;
|
|
Efl_Ui_List_View_Seg_Array_Node *items_node;
|
|
int i, j = 0;
|
|
int boxx, boxy, boxw, boxh, extra = 0, rounding = 0;
|
|
int boxl = 0, boxr = 0, boxt = 0, boxb = 0;
|
|
|
|
_calc_range(pd);
|
|
|
|
evas_object_geometry_get(pd->modeler, &boxx, &boxy, &boxw, &boxh);
|
|
efl_gfx_hint_margin_get(pd->modeler, &boxl, &boxr, &boxt, &boxb);
|
|
|
|
// box outer margin
|
|
boxw -= boxl + boxr;
|
|
boxh -= boxt + boxb;
|
|
boxx += boxl;
|
|
boxy += boxt;
|
|
|
|
// available space. if <0 we overflow
|
|
extra = boxh - pd->min.h;
|
|
if (extra < 0) extra = 0;
|
|
|
|
efl_ui_list_view_model_min_size_set(pd->modeler, pd->min);
|
|
|
|
vgmt = efl_ui_scrollable_viewport_geometry_get(pd->modeler);
|
|
spos = efl_ui_scrollable_content_pos_get(pd->modeler);
|
|
|
|
Eina_Accessor *nodes = efl_ui_list_view_seg_array_node_accessor_get(pd->seg_array);
|
|
EINA_ACCESSOR_FOREACH(nodes, i, items_node)
|
|
{
|
|
Efl_Ui_List_View_Precise_Layouter_Node_Data *nodedata = items_node->layout_data;
|
|
if (!items_node->layout_data)
|
|
continue;
|
|
|
|
if (!nodedata->realized)
|
|
{
|
|
cur_pos += nodedata->min.h;
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j != items_node->length;++j)
|
|
{
|
|
layout_item = (Efl_Ui_List_View_Layout_Item *)items_node->pointers[j];
|
|
double x, y, w, h;
|
|
double weight_x, weight_y;
|
|
if (!(layout_item->min.w && layout_item->min.h))
|
|
continue;
|
|
|
|
// extra rounding up (compensate cumulative error)
|
|
if ((i == (pd->count_total - 1)) && (cur_pos - floor(cur_pos) >= 0.5))
|
|
rounding = 1;
|
|
|
|
if (layout_item->layout)
|
|
{
|
|
if (pd->resize)
|
|
_item_size_calc(pd, layout_item);
|
|
|
|
efl_gfx_hint_weight_get(layout_item->layout, &weight_x, &weight_y);
|
|
}
|
|
else
|
|
{
|
|
cur_pos += layout_item->size.h;
|
|
continue;
|
|
}
|
|
|
|
x = layout_item->pos.x;
|
|
y = layout_item->pos.y + cur_pos;
|
|
w = layout_item->size.w;
|
|
h = layout_item->size.h + rounding + weight_y * extra;
|
|
cur_pos += h;
|
|
|
|
if (w < pd->min.w) w = pd->min.w;
|
|
if (w > vgmt.w) w = vgmt.w;
|
|
|
|
evas_object_geometry_set(layout_item->layout, (x + 0 - spos.x), (y + 0 - spos.y), w, h);
|
|
}
|
|
}
|
|
eina_accessor_free(nodes);
|
|
|
|
pd->resize = EINA_FALSE;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_list_view_precise_layouter_efl_ui_list_view_relayout_layout_do
|
|
(Eo *obj EINA_UNUSED, Efl_Ui_List_View_Precise_Layouter_Data *pd
|
|
, Efl_Ui_List_View_Model *modeler, int first EINA_UNUSED, Efl_Ui_List_View_Seg_Array *seg_array)
|
|
{
|
|
if (!_initilize(obj, pd, modeler, seg_array) || !pd->seg_array)
|
|
return;
|
|
|
|
if (!pd->calc_job && pd->recalc && efl_ui_list_view_seg_array_count(seg_array) > 0)
|
|
{
|
|
// cache size of new items
|
|
pd->calc_progress = 0;
|
|
pd->calc_job = ecore_job_add(_calc_size_job, obj);
|
|
return;
|
|
}
|
|
|
|
_efl_ui_list_view_relayout_layout_do(pd);
|
|
}
|
|
|
|
#include "efl_ui_list_view_precise_layouter.eo.c"
|