From 1a517b4c2db78cf9666f3ffbb10ebb7903654308 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Tue, 20 Aug 2019 13:11:21 -0400 Subject: [PATCH] efl_ui: add scrollable_content mixin this allows content to be set with a scroller that automatically handles its own sizing calcs so that widgets/apps don't have to @feature Reviewed-by: Marcel Hollerbach Differential Revision: https://phab.enlightenment.org/D9674 --- src/lib/elementary/Efl_Ui.h | 1 + .../efl_ui_widget_scrollable_content.c | 192 ++++++++++++++++++ .../efl_ui_widget_scrollable_content.eo | 54 +++++ src/lib/elementary/meson.build | 2 + 4 files changed, 249 insertions(+) create mode 100644 src/lib/elementary/efl_ui_widget_scrollable_content.c create mode 100644 src/lib/elementary/efl_ui_widget_scrollable_content.eo diff --git a/src/lib/elementary/Efl_Ui.h b/src/lib/elementary/Efl_Ui.h index 0f377b7aec..a77a95e333 100644 --- a/src/lib/elementary/Efl_Ui.h +++ b/src/lib/elementary/Efl_Ui.h @@ -172,6 +172,7 @@ EAPI void efl_ui_focus_relation_free(Efl_Ui_Focus_Relations *rel); # include # include # include +# include # include # include # include diff --git a/src/lib/elementary/efl_ui_widget_scrollable_content.c b/src/lib/elementary/efl_ui_widget_scrollable_content.c new file mode 100644 index 0000000000..03672fb7a5 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_scrollable_content.c @@ -0,0 +1,192 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif +#define EFL_UI_WIDGET_SCROLLABLE_CONTENT_PROTECTED +#include "elm_priv.h" + +#define MY_CLASS EFL_UI_WIDGET_SCROLLABLE_CONTENT_MIXIN + +#define MY_CLASS_NAME "Efl_Ui_Widget_Scrollable_Content" + +typedef struct Efl_Ui_Widget_Scrollable_Content_Data +{ + Eo *scroller; + Eina_Bool did_group_calc : 1; +} Efl_Ui_Widget_Scrollable_Content_Data; + +static void +_scroller_sizing_eval(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd, + Eina_Size2D obj_min, Eina_Size2D scr_min) +{ + Eina_Size2D max_size, min_size, size; + max_size = efl_gfx_hint_size_max_get(obj); + + if (max_size.w != -1) + max_size.w = (obj_min.w > max_size.w) ? obj_min.w : max_size.w; + if (max_size.h != -1) + max_size.h = (obj_min.h > max_size.h) ? obj_min.h : max_size.h; + + min_size = efl_gfx_hint_size_min_get(obj); + + size.w = (obj_min.w > min_size.w) ? obj_min.w : min_size.w; + size.h = (obj_min.h > min_size.h) ? obj_min.h : min_size.h; + + Eina_Size2D new_min = obj_min; + + if ((max_size.w == -1) && (max_size.h == -1)) + { + efl_ui_scrollable_match_content_set(pd->scroller, EINA_FALSE, EINA_FALSE); + } + else if ((max_size.w == -1) && (max_size.h != -1)) + { + if (max_size.h < scr_min.h) + { + efl_ui_scrollable_match_content_set(pd->scroller, EINA_FALSE, EINA_FALSE); + size = EINA_SIZE2D(size.w, max_size.h); + } + else + { + new_min.h = scr_min.h; + efl_ui_scrollable_match_content_set(pd->scroller, EINA_FALSE, EINA_TRUE); + size = EINA_SIZE2D(size.w, scr_min.h); + } + } + else if ((max_size.w != -1) && (max_size.h == -1)) + { + if (max_size.w < scr_min.w) + { + efl_ui_scrollable_match_content_set(pd->scroller, EINA_FALSE, EINA_FALSE); + size = EINA_SIZE2D(max_size.w, size.h); + } + else + { + new_min.w = scr_min.w; + efl_ui_scrollable_match_content_set(pd->scroller, EINA_TRUE, EINA_FALSE); + size = EINA_SIZE2D(scr_min.w, size.h); + } + } + else if ((max_size.w != -1) && (max_size.h != -1)) + { + Eina_Bool min_limit_w = EINA_FALSE; + Eina_Bool min_limit_h = EINA_FALSE; + + if (max_size.w < scr_min.w) + { + size.w = max_size.w; + } + else + { + min_limit_w = EINA_TRUE; + new_min.w = scr_min.w; + size.w = scr_min.w; + } + + if (max_size.h < scr_min.h) + { + size.h = max_size.h; + } + else + { + min_limit_h = EINA_TRUE; + new_min.h = scr_min.h; + size.h = scr_min.h; + } + + efl_ui_scrollable_match_content_set(pd->scroller, min_limit_w, min_limit_h); + } + /* this event must come before the scroller recalc in order to ensure the scroller has the correct viewport size */ + efl_event_callback_call(obj, EFL_UI_WIDGET_SCROLLABLE_CONTENT_EVENT_OPTIMAL_SIZE_CALC, &size); + efl_canvas_group_calculate(pd->scroller); + + efl_gfx_hint_size_restricted_min_set(obj, new_min); +} + +static void +_sizing_eval(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); + Evas_Coord obj_minw = -1, obj_minh = -1; + Evas_Coord scr_minw = -1, scr_minh = -1; + + //Calculate popup's min size including scroller's min size + { + efl_ui_scrollable_match_content_set(pd->scroller, EINA_TRUE, EINA_TRUE); + efl_canvas_group_calculate(pd->scroller); + + elm_coords_finger_size_adjust(1, &scr_minw, 1, &scr_minh); + edje_object_size_min_restricted_calc + (wd->resize_obj, &scr_minw, &scr_minh, scr_minw, scr_minh); + } + + //Calculate popup's min size except scroller's min size + { + efl_ui_scrollable_match_content_set(pd->scroller, EINA_FALSE, EINA_FALSE); + efl_canvas_group_calculate(pd->scroller); + + elm_coords_finger_size_adjust(1, &obj_minw, 1, &obj_minh); + edje_object_size_min_restricted_calc + (wd->resize_obj, &obj_minw, &obj_minh, obj_minw, obj_minh); + } + _scroller_sizing_eval(obj, pd, EINA_SIZE2D(obj_minw, obj_minh), EINA_SIZE2D(scr_minw, scr_minh)); +} + +EOLIAN static void +_efl_ui_widget_scrollable_content_efl_canvas_group_calculate(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + pd->did_group_calc = EINA_FALSE; + if (!pd->scroller) + { + efl_canvas_group_calculate(efl_super(obj, MY_CLASS)); + return; + } + pd->did_group_calc = EINA_TRUE; + _sizing_eval(obj, pd); + efl_canvas_group_need_recalculate_set(pd->scroller, EINA_FALSE); + efl_canvas_group_need_recalculate_set(obj, EINA_FALSE); +} + +static void +_scroller_setup(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + pd->scroller = efl_add(EFL_UI_SCROLLER_CLASS, obj, + efl_ui_widget_style_set(efl_added, "popup/no_inset_shadow") + ); + efl_wref_add(pd->scroller, &pd->scroller); + efl_content_set(obj, pd->scroller); +} + +EOLIAN static Eina_Bool +_efl_ui_widget_scrollable_content_scrollable_content_did_group_calc_get(const Eo *obj EINA_UNUSED, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + return pd->did_group_calc; +} + +EOLIAN static Eina_Bool +_efl_ui_widget_scrollable_content_scrollable_content_set(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd, Eo *content) +{ + Eina_Bool ret; + + if (!pd->scroller) + _scroller_setup(obj, pd); + ret = efl_content_set(pd->scroller, content); + if (ret) efl_canvas_group_change(obj); + return ret; +} + +EOLIAN static Eo * +_efl_ui_widget_scrollable_content_scrollable_content_get(const Eo *obj EINA_UNUSED, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + return efl_content_get(pd->scroller); +} + +EOLIAN static void +_efl_ui_widget_scrollable_content_efl_object_destructor(Eo *obj, Efl_Ui_Widget_Scrollable_Content_Data *pd) +{ + if (pd->scroller) efl_wref_del(pd->scroller, &pd->scroller); + efl_destructor(efl_super(obj, MY_CLASS)); +} + +#define EFL_UI_WIDGET_SCROLLABLE_CONTENT_EXTRA_OPS \ + EFL_CANVAS_GROUP_CALC_OPS(efl_ui_widget_scrollable_content) + +#include "efl_ui_widget_scrollable_content.eo.c" diff --git a/src/lib/elementary/efl_ui_widget_scrollable_content.eo b/src/lib/elementary/efl_ui_widget_scrollable_content.eo new file mode 100644 index 0000000000..d110f3eaa9 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_scrollable_content.eo @@ -0,0 +1,54 @@ +import eina_types; + +mixin @beta Efl.Ui.Widget.Scrollable_Content requires Efl.Object +{ + [[Efl widget scrollable content mixin + + This can be used to provide scrollable contents and text for widgets. + + Widgets can set the "__efl_scrollable_content_scroller_style" key data during + their constructor to apply a style to the internal scroller widget. + ]] + c_prefix: efl_ui_widget; + event_prefix: efl_ui_widget_scrollable_content; + data: Efl_Ui_Widget_Scrollable_Content_Data; + methods { + @property scrollable_content_did_group_calc @protected @beta { + [[Widgets can call this function during their @Efl.Canvas.Group.group_calculate + implementation after the super call to determine whether the internal scroller + has performed sizing calculations. + + The @.optimal_size,calc event will have been emitted during the super call if + this method returns $true. + + In the case that this returns $true, it's likely that the widget should be completing + its internal sizing calculations from the @.optimal_size,calc callback using + + `efl_canvas_group_calculate(efl_super(ev->object, EFL_UI_WIDGET_SCROLLABLE_CONTENT_MIXIN));` + + in order to skip the scrollable sizing calc. + ]] + get {} + values { + did_group_calc: bool; [[Whether the internal scroller has done sizing calcs.]] + } + } + @property scrollable_content { + [[This is the content which will be placed in the internal scroller. + ]] + set { + return: bool; [[True on success]] + } + get {} + values { + content: Efl.Canvas.Object; [[The content object.]] + } + } + } + events { + optimal_size,calc: Eina.Size2D; [[The optimal size for the widget based on scrollable content.]] + } + implements { + Efl.Object.destructor; + } +} diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 6e82c93f84..536f00bbde 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -38,6 +38,7 @@ endforeach pub_eo_files = [ 'efl_ui_widget.eo', + 'efl_ui_widget_scrollable_content.eo', 'efl_ui_animation_view.eo', 'efl_ui_bg.eo', 'efl_ui_button.eo', @@ -851,6 +852,7 @@ elementary_src = [ 'elm_view_form.c', 'elm_web2.c', 'efl_ui_widget.c', + 'efl_ui_widget_scrollable_content.c', 'efl_ui_widget_common.c', 'efl_ui_win.c', 'efl_ui_win_inlined.c',