summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2019-08-16 17:44:58 +0200
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2019-08-20 10:09:57 +0200
commit175b51f53ccede341155e2bff216715762b86fd0 (patch)
treeeae872db5d52cb6304bcca23efaa1f498b58c506
parent8ae670bb75c6f42925582994025830ee6840c248 (diff)
downloadefl-175b51f53ccede341155e2bff216715762b86fd0.tar.gz
efl_ui_position_manager_list: make it handle group items
this makes the group items stick at the top of the viewport, if the corresponding items in there do have the item available. For now items between two groups are not really handled, the group header will still just be displayed. The code for this feature is explicitly written in a single block, it was said that we might want to have this able to be enabled / disabled later on. This commit also shuffels the code here a bit, one single method just got too long. ref T8115 Differential Revision: https://phab.enlightenment.org/D9587
-rw-r--r--src/lib/elementary/efl_ui_position_manager_list.c213
1 files changed, 155 insertions, 58 deletions
diff --git a/src/lib/elementary/efl_ui_position_manager_list.c b/src/lib/elementary/efl_ui_position_manager_list.c
index a4bebde2d6..a3bd452522 100644
--- a/src/lib/elementary/efl_ui_position_manager_list.c
+++ b/src/lib/elementary/efl_ui_position_manager_list.c
@@ -13,6 +13,11 @@
#define MY_DATA_GET(obj, pd) \
Efl_Ui_Position_Manager_List_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+
+typedef struct {
+ unsigned int start_id, end_id;
+} Vis_Segment;
+
typedef struct {
Api_Callback min_size, object;
unsigned int size;
@@ -24,9 +29,8 @@ typedef struct {
int *size_cache;
int average_item_size;
int maximum_min_size;
- struct {
- unsigned int start_id, end_id;
- } prev_run;
+ Vis_Segment prev_run;
+ Efl_Gfx_Entity *last_group;
} Efl_Ui_Position_Manager_List_Data;
/*
@@ -132,101 +136,120 @@ recalc_absolut_size(Eo *obj, Efl_Ui_Position_Manager_List_Data *pd)
efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_CONTENT_MIN_SIZE_CHANGED, &min_size);
}
-static void
-position_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
+static inline Vis_Segment
+_search_visual_segment(Eo *obj, Efl_Ui_Position_Manager_List_Data *pd, int relevant_space_size, int relevant_viewport)
{
- Eina_Rect geom;
- Eina_Size2D space_size;
- unsigned int start_id = 0, end_id = 0, i;
- int relevant_space_size, relevant_viewport;
- const int len = 100;
- Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[len];
- Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[len];
- Efl_Ui_Position_Manager_Range_Update ev;
-
- if (!pd->size) return;
- if (pd->average_item_size <= 0) return;
-
- //space size contains the amount of space that is outside the viewport (either to the top or to the left)
- space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
- space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
-
- if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
- {
- relevant_space_size = space_size.h;
- relevant_viewport = pd->viewport.h;
- }
- else
- {
- relevant_space_size = space_size.w;
- relevant_viewport = pd->viewport.w;
- }
-
+ Vis_Segment cur;
//based on the average item size, we jump somewhere into the sum cache.
//After beeing in there, we are walking back, until we have less space then viewport size
- start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size);
- for (; cache_access(obj, pd, start_id) >= relevant_space_size && start_id > 0; start_id --) { }
+ cur.start_id = MIN((unsigned int)(relevant_space_size / pd->average_item_size), pd->size);
+ for (; cache_access(obj, pd, cur.start_id) >= relevant_space_size && cur.start_id > 0; cur.start_id --) { }
//starting on the start id, we are walking down until the sum of elements is bigger than the lower part of the viewport.
- end_id = start_id;
- for (; end_id <= pd->size && cache_access(obj, pd, end_id) <= relevant_space_size + relevant_viewport ; end_id ++) { }
- end_id = MAX(end_id, start_id + 1);
- end_id = MIN(end_id, pd->size);
+ cur.end_id = cur.start_id;
+ for (; cur.end_id <= pd->size && cache_access(obj, pd, cur.end_id) <= relevant_space_size + relevant_viewport ; cur.end_id ++) { }
+ cur.end_id = MAX(cur.end_id, cur.start_id + 1);
+ cur.end_id = MIN(cur.end_id, pd->size);
#ifdef DEBUG
- printf("space_size %d : starting point : %d : cached_space_starting_point %d end point : %d cache_space_end_point %d\n", space_size.h, start_id, pd->size_cache[start_id], end_id, pd->size_cache[end_id]);
+ printf("space_size %d : starting point : %d : cached_space_starting_point %d end point : %d cache_space_end_point %d\n", space_size.h, cur.start_id, pd->size_cache[cur.start_id], cur.end_id, pd->size_cache[cur.end_id]);
#endif
if (relevant_space_size > 0)
- EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, start_id) <= relevant_space_size);
- if (end_id != pd->size)
- EINA_SAFETY_ON_FALSE_RETURN(cache_access(obj, pd, end_id) >= relevant_space_size + relevant_viewport);
- EINA_SAFETY_ON_FALSE_RETURN(start_id <= end_id);
+ EINA_SAFETY_ON_FALSE_GOTO(cache_access(obj, pd, cur.start_id) <= relevant_space_size, err);
+ if (cur.end_id != pd->size)
+ EINA_SAFETY_ON_FALSE_GOTO(cache_access(obj, pd, cur.end_id) >= relevant_space_size + relevant_viewport, err);
+ EINA_SAFETY_ON_FALSE_GOTO(cur.start_id <= cur.end_id, err);
- //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
- //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
- if (end_id <= pd->prev_run.start_id || start_id >= pd->prev_run.end_id)
+ return cur;
+
+err:
+ cur.start_id = cur.end_id = 0;
+
+ return cur;
+}
+
+static inline void
+_visual_segment_swap(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd, Vis_Segment new, Vis_Segment old)
+{
+ if (new.end_id <= old.start_id || new.start_id >= old.end_id)
{
//it is important to first make the segment visible here, and then hide the rest
//otherwise we get a state where item_container has 0 subchildren, which triggers a lot of focus logic.
- vis_change_segment(&pd->object, start_id, end_id, EINA_TRUE);
- vis_change_segment(&pd->object, pd->prev_run.start_id, pd->prev_run.end_id, EINA_FALSE);
+ vis_change_segment(&pd->object, new.start_id, new.end_id, EINA_TRUE);
+ vis_change_segment(&pd->object, old.start_id, old.end_id, EINA_FALSE);
}
else
{
- vis_change_segment(&pd->object, pd->prev_run.start_id, start_id, (pd->prev_run.start_id > start_id));
- vis_change_segment(&pd->object, pd->prev_run.end_id, end_id, (pd->prev_run.end_id < end_id));
+ vis_change_segment(&pd->object, old.start_id, new.start_id, (old.start_id > new.start_id));
+ vis_change_segment(&pd->object, old.end_id, new.end_id, (old.end_id < new.end_id));
}
+}
+
+static inline void
+_position_items(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd, Vis_Segment new, int relevant_space_size)
+{
+ int group_id = -1;
+ Efl_Gfx_Entity *first_group = NULL, *first_fully_visual_group = NULL;
+ Eina_Size2D first_group_size;
+ Eina_Rect geom;
+ const int len = 100;
+ Efl_Ui_Position_Manager_Batch_Size_Access size_buffer[len];
+ Efl_Ui_Position_Manager_Batch_Entity_Access obj_buffer[len];
+ unsigned int i;
+ //placement of the plain items
geom = pd->viewport;
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
- geom.y -= (relevant_space_size - cache_access(obj, pd, start_id));
+ geom.y -= (relevant_space_size - cache_access(obj, pd, new.start_id));
else
- geom.x -= (relevant_space_size - cache_access(obj, pd, start_id));
+ geom.x -= (relevant_space_size - cache_access(obj, pd, new.start_id));
- for (i = start_id; i < end_id; ++i)
+ for (i = new.start_id; i < new.end_id; ++i)
{
Eina_Size2D size;
Efl_Gfx_Entity *ent = NULL;
- int buffer_id = (i-start_id) % len;
+ int buffer_id = (i-new.start_id) % len;
if (buffer_id == 0)
{
+ int tmp_group;
int res1, res2;
- res1 = _fill_buffer(&pd->object, i, len, NULL, obj_buffer);
+ res1 = _fill_buffer(&pd->object, i, len, &tmp_group, obj_buffer);
res2 = _fill_buffer(&pd->min_size, i, len, NULL, size_buffer);
EINA_SAFETY_ON_FALSE_RETURN(res1 == res2);
EINA_SAFETY_ON_FALSE_RETURN(res2 > 0);
+
+ if (i == new.start_id)
+ {
+ if (tmp_group > 0)
+ group_id = tmp_group;
+ else if (obj_buffer[0].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_GROUP)
+ group_id = i;
+ }
}
size = size_buffer[buffer_id].size;
ent = obj_buffer[buffer_id].entity;
+ if (ent == pd->last_group)
+ {
+ pd->last_group = NULL;
+ }
+
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
geom.h = size.h;
else
geom.w = size.w;
+
+ if (!first_fully_visual_group && obj_buffer[buffer_id].group == EFL_UI_POSITION_MANAGER_BATCH_GROUP_STATE_GROUP &&
+ eina_spans_intersect(geom.x, geom.w, pd->viewport.x, pd->viewport.w) &&
+ eina_spans_intersect(geom.y, geom.h, pd->viewport.y, pd->viewport.h))
+ {
+ first_fully_visual_group = obj_buffer[buffer_id].entity;
+ }
+
if (ent)
efl_gfx_entity_geometry_set(ent, geom);
if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
@@ -234,10 +257,84 @@ position_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
else
geom.x += size.w;
}
- if (pd->prev_run.start_id != start_id || pd->prev_run.end_id != end_id)
+ //Now place group items
+
+ if (group_id > 0)
+ {
+ EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->object, group_id, 1, NULL, obj_buffer) == 1);
+ EINA_SAFETY_ON_FALSE_RETURN(_fill_buffer(&pd->min_size, group_id, 1, NULL, size_buffer) == 1);
+ first_group = obj_buffer[0].entity;
+ first_group_size = size_buffer[0].size;
+ }
+ if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
+ first_group_size.w = pd->viewport.w;
+ else
+ first_group_size.h = pd->viewport.h;
+
+ //if there is a new group item, display the new one, and hide the old one
+ if (first_group != pd->last_group)
+ {
+ efl_gfx_entity_visible_set(pd->last_group, EINA_FALSE);
+ efl_gfx_stack_raise_to_top(first_group);
+ pd->last_group = first_group;
+ }
+ //we have to set the visibility again here, as changing the visual segments might overwrite our visibility state
+ efl_gfx_entity_visible_set(first_group, EINA_TRUE);
+
+ //in case there is another group item coming in, the new group item (which is placed as normal item) moves the group item to the top
+ Eina_Position2D first_group_pos = EINA_POSITION2D(pd->viewport.x, pd->viewport.y);
+ if (first_fully_visual_group && first_fully_visual_group != first_group)
+ {
+ Eina_Position2D first_visual_group;
+ first_visual_group = efl_gfx_entity_position_get(first_fully_visual_group);
+ if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
+ first_group_pos.y = MIN(first_group_pos.y, first_visual_group.y - first_group_size.h);
+ else
+ first_group_pos.x = MIN(first_group_pos.x, first_visual_group.x - first_group_size.w);
+ }
+
+ efl_gfx_entity_position_set(first_group, first_group_pos);
+ efl_gfx_entity_size_set(first_group, first_group_size);
+}
+
+
+static void
+position_content(Eo *obj EINA_UNUSED, Efl_Ui_Position_Manager_List_Data *pd)
+{
+ Eina_Size2D space_size;
+ Vis_Segment cur;
+ int relevant_space_size, relevant_viewport;
+ Efl_Ui_Position_Manager_Range_Update ev;
+
+ if (!pd->size) return;
+ if (pd->average_item_size <= 0) return;
+
+ //space size contains the amount of space that is outside the viewport (either to the top or to the left)
+ space_size.w = (MAX(pd->abs_size.w - pd->viewport.w, 0))*pd->scroll_position.x;
+ space_size.h = (MAX(pd->abs_size.h - pd->viewport.h, 0))*pd->scroll_position.y;
+
+ if (pd->dir == EFL_UI_LAYOUT_ORIENTATION_VERTICAL)
+ {
+ relevant_space_size = space_size.h;
+ relevant_viewport = pd->viewport.h;
+ }
+ else
+ {
+ relevant_space_size = space_size.w;
+ relevant_viewport = pd->viewport.w;
+ }
+
+ cur = _search_visual_segment(obj, pd, relevant_space_size, relevant_viewport);
+ //to performance optimize the whole widget, we are setting the objects that are outside the viewport to visibility false
+ //The code below ensures that things outside the viewport are always hidden, and things inside the viewport are visible
+ _visual_segment_swap(obj, pd, cur, pd->prev_run);
+
+ _position_items(obj, pd, cur, relevant_space_size);
+
+ if (pd->prev_run.start_id != cur.start_id || pd->prev_run.end_id != cur.end_id)
{
- ev.start_id = pd->prev_run.start_id = start_id;
- ev.end_id = pd->prev_run.end_id = end_id;
+ ev.start_id = pd->prev_run.start_id = cur.start_id;
+ ev.end_id = pd->prev_run.end_id = cur.end_id;
efl_event_callback_call(obj, EFL_UI_POSITION_MANAGER_ENTITY_EVENT_VISIBLE_RANGE_CHANGED, &ev);
}