From 45e8d67a78b6c73b07bee277b0ac5102fc4df21e Mon Sep 17 00:00:00 2001 From: Gustavo Lima Chaves Date: Wed, 4 Jul 2012 21:41:01 +0000 Subject: [PATCH] [elm] New scrollable interface in. This is meant to be used for all widgets implementing scrolling views. An Elementary interface adds a given behaviour to whichever widget, from any class. SVN revision: 73307 --- legacy/elementary/src/lib/Makefile.am | 2 + .../src/lib/elm_interface_scrollable.c | 3744 +++++++++++++++++ .../src/lib/elm_interface_scrollable.h | 488 +++ legacy/elementary/src/lib/elm_widget.c | 51 +- legacy/elementary/src/lib/elm_widget.h | 4 + 5 files changed, 4285 insertions(+), 4 deletions(-) create mode 100644 legacy/elementary/src/lib/elm_interface_scrollable.c create mode 100644 legacy/elementary/src/lib/elm_interface_scrollable.h diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 7a6af4be5c..44d7f7c7a8 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -41,6 +41,7 @@ Elementary_Cursor.h includesdir = $(includedir)/elementary-@VMAJ@ includesunstable_HEADERS = \ +elm_interface_scrollable.h \ elm_widget.h \ elm_widget_button.h \ elm_widget_container.h \ @@ -186,6 +187,7 @@ elm_hover.c \ elm_icon.c \ elm_image.c \ elm_index.c \ +elm_interface_scrollable.c \ elm_inwin.c \ elm_label.c \ elm_layout.c \ diff --git a/legacy/elementary/src/lib/elm_interface_scrollable.c b/legacy/elementary/src/lib/elm_interface_scrollable.c new file mode 100644 index 0000000000..6bb76a9c2b --- /dev/null +++ b/legacy/elementary/src/lib/elm_interface_scrollable.c @@ -0,0 +1,3744 @@ +#include +#include "elm_priv.h" +#include "elm_interface_scrollable.h" + +static const char PAN_SMART_NAME[] = "elm_pan"; + +#define ELM_PAN_DATA_GET(o, sd) \ + Elm_Pan_Smart_Data * sd = evas_object_smart_data_get(o) + +#define ELM_PAN_DATA_GET_OR_RETURN(o, ptr) \ + ELM_PAN_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRITICAL("No smart data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return; \ + } + +#define ELM_PAN_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ + ELM_PAN_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRITICAL("No smart data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return val; \ + } + +static const char SIG_CHANGED[] = "changed"; +static const Evas_Smart_Cb_Description _smart_callbacks[] = { + {SIG_CHANGED, ""}, + {NULL, NULL} +}; + +ELM_INTERNAL_SMART_SUBCLASS_NEW + (PAN_SMART_NAME, _elm_pan, Elm_Pan_Smart_Class, Evas_Smart_Class, + evas_object_smart_clipped_class_get, _smart_callbacks); + +static void _elm_pan_content_set(Evas_Object *, Evas_Object *); + +EAPI const Elm_Pan_Smart_Class * +elm_pan_smart_class_get(void) +{ + static Elm_Pan_Smart_Class _sc = + ELM_PAN_SMART_CLASS_INIT_NAME_VERSION(PAN_SMART_NAME); + static const Elm_Pan_Smart_Class *class = NULL; + Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc; + + if (class) + return class; + + _elm_pan_smart_set(&_sc); + esc->callbacks = _smart_callbacks; + class = &_sc; + + return class; +} + +static void +_elm_pan_update(Elm_Pan_Smart_Data *psd) +{ + if (!psd->gravity_x && !psd->gravity_y) + { + evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py); + return; + } + + if ((!psd->px) && (!psd->py)) + { + psd->px = psd->delta_posx * psd->gravity_x; + psd->py = psd->delta_posy * psd->gravity_y; + } + psd->delta_posx += psd->content_w - psd->prev_cw; + psd->prev_cw = psd->content_w; + psd->delta_posy += psd->content_h - psd->prev_ch; + psd->prev_ch = psd->content_h; + + evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py); + psd->px = psd->delta_posx * psd->gravity_x; + psd->py = psd->delta_posy * psd->gravity_y; +} + +static void +_elm_pan_smart_add(Evas_Object *obj) +{ + const Evas_Smart_Class *sc; + const Evas_Smart *smart; + + EVAS_SMART_DATA_ALLOC(obj, Elm_Pan_Smart_Data); + + _elm_pan_parent_sc->add(obj); + + priv->self = obj; + + priv->x = 0; + priv->y = 0; + priv->w = 0; + priv->h = 0; + priv->gravity_x = 0.0; + priv->gravity_y = 0.0; + + smart = evas_object_smart_smart_get(obj); + sc = evas_smart_class_get(smart); + priv->api = (const Elm_Pan_Smart_Class *)sc; +} + +static void +_elm_pan_smart_del(Evas_Object *obj) +{ + _elm_pan_content_set(obj, NULL); + + _elm_pan_parent_sc->del(obj); +} + +static void +_elm_pan_smart_move(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y) +{ + ELM_PAN_DATA_GET(obj, psd); + + /* we don't want the clipped smart object version here */ + + psd->x = x; + psd->y = y; + + _elm_pan_update(psd); +} + +static void +_elm_pan_smart_resize(Evas_Object *obj, + Evas_Coord w, + Evas_Coord h) +{ + ELM_PAN_DATA_GET(obj, psd); + + psd->w = w; + psd->h = h; + + _elm_pan_update(psd); + evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL); +} + +static void +_elm_pan_smart_show(Evas_Object *obj) +{ + ELM_PAN_DATA_GET(obj, psd); + + _elm_pan_parent_sc->show(obj); + + if (psd->content) + evas_object_show(psd->content); +} + +static void +_elm_pan_smart_hide(Evas_Object *obj) +{ + ELM_PAN_DATA_GET(obj, psd); + + _elm_pan_parent_sc->hide(obj); + + if (psd->content) + evas_object_hide(psd->content); +} + +static void +_elm_pan_pos_set(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y) +{ + ELM_PAN_DATA_GET(obj, psd); + + if ((x == psd->px) && (y == psd->py)) return; + psd->px = x; + psd->py = y; + + _elm_pan_update(psd); + evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL); +} + +static void +_elm_pan_pos_get(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y) +{ + ELM_PAN_DATA_GET(obj, psd); + + if (x) *x = psd->px; + if (y) *y = psd->py; +} + +static void +_elm_pan_pos_max_get(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y) +{ + ELM_PAN_DATA_GET(obj, psd); + + if (x) + { + if (psd->w < psd->content_w) *x = psd->content_w - psd->w; + else *x = 0; + } + if (y) + { + if (psd->h < psd->content_h) *y = psd->content_h - psd->h; + else *y = 0; + } +} + +static void +_elm_pan_pos_min_get(const Evas_Object *obj __UNUSED__, + Evas_Coord *x, + Evas_Coord *y) +{ + if (x) + *x = 0; + if (y) + *y = 0; +} + +static void +_elm_pan_content_size_get(const Evas_Object *obj, + Evas_Coord *w, + Evas_Coord *h) +{ + ELM_PAN_DATA_GET(obj, psd); + + if (w) *w = psd->content_w; + if (h) *h = psd->content_h; +} + +static void +_elm_pan_gravity_set(Evas_Object *obj, + double x, + double y) +{ + ELM_PAN_DATA_GET(obj, psd); + + psd->gravity_x = x; + psd->gravity_y = y; + psd->prev_cw = psd->content_w; + psd->prev_ch = psd->content_h; + psd->delta_posx = 0; + psd->delta_posy = 0; +} + +static void +_elm_pan_gravity_get(const Evas_Object *obj, + double *x, + double *y) +{ + ELM_PAN_DATA_GET(obj, psd); + + if (x) *x = psd->gravity_x; + if (y) *y = psd->gravity_y; +} + +static void +_elm_pan_smart_set_user(Elm_Pan_Smart_Class *sc) +{ + sc->base.add = _elm_pan_smart_add; + sc->base.del = _elm_pan_smart_del; + sc->base.move = _elm_pan_smart_move; + sc->base.resize = _elm_pan_smart_resize; + sc->base.show = _elm_pan_smart_show; + sc->base.hide = _elm_pan_smart_hide; + + sc->pos_set = _elm_pan_pos_set; + sc->pos_get = _elm_pan_pos_get; + sc->pos_max_get = _elm_pan_pos_max_get; + sc->pos_min_get = _elm_pan_pos_min_get; + sc->content_size_get = _elm_pan_content_size_get; + sc->gravity_set = _elm_pan_gravity_set; + sc->gravity_get = _elm_pan_gravity_get; +} + +static Evas_Object * +_elm_pan_add(Evas *evas) +{ + return evas_object_smart_add(evas, _elm_pan_smart_class_new()); +} + +static void +_elm_pan_content_del_cb(void *data, + Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + Elm_Pan_Smart_Data *psd; + + psd = data; + psd->content = NULL; + psd->content_w = psd->content_h = psd->px = psd->py = + psd->prev_cw = psd->prev_ch = psd->delta_posx = psd->delta_posy = 0; + evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL); +} + +static void +_elm_pan_content_resize_cb(void *data, + Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + Elm_Pan_Smart_Data *psd; + Evas_Coord w, h; + + psd = data; + evas_object_geometry_get(psd->content, NULL, NULL, &w, &h); + if ((w != psd->content_w) || (h != psd->content_h)) + { + psd->content_w = w; + psd->content_h = h; + _elm_pan_update(psd); + } + evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL); +} + +static void +_elm_pan_content_set(Evas_Object *obj, + Evas_Object *content) +{ + Evas_Coord w, h; + + ELM_PAN_DATA_GET(obj, psd); + + if (content == psd->content) return; + if (psd->content) + { + evas_object_smart_member_del(psd->content); + evas_object_event_callback_del_full + (psd->content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd); + evas_object_event_callback_del_full + (psd->content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb, + psd); + psd->content = NULL; + } + if (!content) goto end; + + psd->content = content; + evas_object_smart_member_add(psd->content, psd->self); + evas_object_geometry_get(psd->content, NULL, NULL, &w, &h); + psd->content_w = w; + psd->content_h = h; + evas_object_event_callback_add + (content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd); + evas_object_event_callback_add + (content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb, psd); + + if (evas_object_visible_get(psd->self)) + evas_object_show(psd->content); + else + evas_object_hide(psd->content); + + _elm_pan_update(psd); + +end: + evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL); +} + +/* pan smart object on top, scroller interface on bottom */ +/* ============================================================ */ + +static const char SCROLL_SMART_NAME[] = "elm_scroll"; + +#define ELM_SCROLL_IFACE_DATA_GET(o, sid) \ + Elm_Scrollable_Smart_Interface_Data * sid = \ + evas_object_smart_interface_data_get(o, &(ELM_SCROLLABLE_IFACE.base)) + +#define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(o, ptr) \ + ELM_SCROLL_IFACE_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRITICAL("No interface data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return; \ + } + +#define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ + ELM_SCROLL_IFACE_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRITICAL("No interface data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return val; \ + } + +static void _elm_scroll_scroll_bar_size_adjust( + Elm_Scrollable_Smart_Interface_Data *); +static void _elm_scroll_wanted_region_set(Evas_Object *); +static void _elm_scroll_content_pos_get(const Evas_Object *, + Evas_Coord *, + Evas_Coord *); +static void _elm_scroll_content_pos_set(Evas_Object *, + Evas_Coord, + Evas_Coord); + +#define LEFT 0 +#define RIGHT 1 +#define UP 2 +#define DOWN 3 +#define EVTIME 1 +//#define SCROLLDBG 1 + +static int +_elm_scroll_scroll_bar_h_visibility_adjust( + Elm_Scrollable_Smart_Interface_Data *sid) +{ + int scroll_h_vis_change = 0; + Evas_Coord w, vw = 0, vh = 0; + + if (!sid->edje_obj) return 0; + + w = sid->content_info.w; + if (sid->pan_obj) + evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh); + if (sid->hbar_visible) + { + if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO) + { + if ((sid->content) || (sid->extern_pan)) + { + if (w <= vw) + { + scroll_h_vis_change = 1; + sid->hbar_visible = EINA_FALSE; + } + } + else + { + scroll_h_vis_change = 1; + sid->hbar_visible = EINA_FALSE; + } + } + else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF) + { + scroll_h_vis_change = 1; + sid->hbar_visible = EINA_FALSE; + } + } + else + { + if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO) + { + if ((sid->content) || (sid->extern_pan)) + { + if (w > vw) + { + scroll_h_vis_change = 1; + sid->hbar_visible = EINA_TRUE; + } + } + } + else if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON) + { + scroll_h_vis_change = 1; + sid->hbar_visible = EINA_TRUE; + } + } + if (scroll_h_vis_change) + { + if (sid->hbar_flags != ELM_SCROLLER_POLICY_OFF) + { + if (sid->hbar_visible) + edje_object_signal_emit + (sid->edje_obj, "elm,action,show,hbar", "elm"); + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,hbar", "elm"); + edje_object_message_signal_process(sid->edje_obj); + _elm_scroll_scroll_bar_size_adjust(sid); + } + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,hbar", "elm"); + _elm_scroll_scroll_bar_size_adjust(sid); + } + + return scroll_h_vis_change; +} + +static int +_elm_scroll_scroll_bar_v_visibility_adjust( + Elm_Scrollable_Smart_Interface_Data *sid) +{ + int scroll_v_vis_change = 0; + Evas_Coord h, vw = 0, vh = 0; + + if (!sid->edje_obj) return 0; + + h = sid->content_info.h; + if (sid->pan_obj) + evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh); + if (sid->vbar_visible) + { + if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO) + { + if ((sid->content) || (sid->extern_pan)) + { + if (h <= vh) + { + scroll_v_vis_change = 1; + sid->vbar_visible = EINA_FALSE; + } + } + else + { + scroll_v_vis_change = 1; + sid->vbar_visible = EINA_FALSE; + } + } + else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF) + { + scroll_v_vis_change = 1; + sid->vbar_visible = EINA_FALSE; + } + } + else + { + if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO) + { + if ((sid->content) || (sid->extern_pan)) + { + if (h > vh) + { + scroll_v_vis_change = 1; + sid->vbar_visible = EINA_TRUE; + } + } + } + else if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON) + { + scroll_v_vis_change = 1; + sid->vbar_visible = EINA_TRUE; + } + } + if (scroll_v_vis_change) + { + if (sid->vbar_flags != ELM_SCROLLER_POLICY_OFF) + { + if (sid->vbar_visible) + edje_object_signal_emit + (sid->edje_obj, "elm,action,show,vbar", "elm"); + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,vbar", "elm"); + + edje_object_message_signal_process(sid->edje_obj); + _elm_scroll_scroll_bar_size_adjust(sid); + } + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,vbar", "elm"); + } + + return scroll_v_vis_change; +} + +static void +_elm_scroll_scroll_bar_visibility_adjust( + Elm_Scrollable_Smart_Interface_Data *sid) +{ + int changed = 0; + + changed |= _elm_scroll_scroll_bar_h_visibility_adjust(sid); + changed |= _elm_scroll_scroll_bar_v_visibility_adjust(sid); + + if (changed) + { + _elm_scroll_scroll_bar_h_visibility_adjust(sid); + _elm_scroll_scroll_bar_v_visibility_adjust(sid); + } +} + +static void +_elm_scroll_scroll_bar_size_adjust(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if (!sid->pan_obj || !sid->edje_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if ((sid->content) || (sid->extern_pan)) + { + Evas_Coord x, y, w, h, mx = 0, my = 0, vw = 0, vh = 0, px, py, + minx = 0, miny = 0; + double vx, vy, size; + + edje_object_part_geometry_get + (sid->edje_obj, "elm.swallow.content", NULL, NULL, &vw, &vh); + w = sid->content_info.w; + if (w < 1) w = 1; + size = (double)vw / (double)w; + + if (size > 1.0) + { + size = 1.0; + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.hbar", 0.0, 0.0); + } + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.hbar", size, 1.0); + + h = sid->content_info.h; + if (h < 1) h = 1; + size = (double)vh / (double)h; + if (size > 1.0) + { + size = 1.0; + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, 0.0); + } + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.vbar", 1.0, size); + + edje_object_part_drag_value_get + (sid->edje_obj, "elm.dragable.hbar", &vx, NULL); + edje_object_part_drag_value_get + (sid->edje_obj, "elm.dragable.vbar", NULL, &vy); + + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + x = vx * mx + minx; + y = vy * my + miny; + + edje_object_part_drag_step_set + (sid->edje_obj, "elm.dragable.hbar", (double)sid->step.x / + (double)w, 0.0); + edje_object_part_drag_step_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, (double)sid->step.y / + (double)h); + if (sid->page.x > 0) + edje_object_part_drag_page_set + (sid->edje_obj, "elm.dragable.hbar", (double)sid->page.x / + (double)w, 0.0); + else + edje_object_part_drag_page_set + (sid->edje_obj, "elm.dragable.hbar", + -((double)sid->page.x * ((double)vw / (double)w)) / 100.0, 0.0); + if (sid->page.y > 0) + edje_object_part_drag_page_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, + (double)sid->page.y / (double)h); + else + edje_object_part_drag_page_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, + -((double)sid->page.y * ((double)vh / (double)h)) / 100.0); + + psd->api->pos_get(sid->pan_obj, &px, &py); + if (vx != mx) x = px; + if (vy != my) y = py; + psd->api->pos_set(sid->pan_obj, x, y); + } + else + { + Evas_Coord px = 0, py = 0, minx = 0, miny = 0; + + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.vbar", 1.0, 1.0); + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.hbar", 1.0, 1.0); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + psd->api->pos_get(sid->pan_obj, &px, &py); + psd->api->pos_set(sid->pan_obj, minx, miny); + if ((px != minx) || (py != miny)) + edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); + } + _elm_scroll_scroll_bar_visibility_adjust(sid); +} + +static void +_elm_scroll_scroll_bar_read_and_update( + Elm_Scrollable_Smart_Interface_Data *sid) +{ + Evas_Coord x, y, mx = 0, my = 0, px, py, minx = 0, miny = 0; + double vx, vy; + + if (!sid->edje_obj || !sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if ((sid->down.dragged) || (sid->down.bounce_x_animator) + || (sid->down.bounce_y_animator) || (sid->down.momentum_animator) + || (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) + return; + edje_object_part_drag_value_get + (sid->edje_obj, "elm.dragable.vbar", NULL, &vy); + edje_object_part_drag_value_get + (sid->edje_obj, "elm.dragable.hbar", &vx, NULL); + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + x = vx * (double)mx + minx; + y = vy * (double)my + miny; + psd->api->pos_get(sid->pan_obj, &px, &py); + psd->api->pos_set(sid->pan_obj, x, y); + if ((px != x) || (py != y)) + edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); +} + +static void +_elm_scroll_drag_start(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if (sid->cb_func.drag_start) + sid->cb_func.drag_start(sid->obj, NULL); +} + +static void +_elm_scroll_drag_stop(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if (sid->cb_func.drag_stop) + sid->cb_func.drag_stop(sid->obj, NULL); +} + +static void +_elm_scroll_anim_start(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if (sid->cb_func.animate_start) + sid->cb_func.animate_start(sid->obj, NULL); +} + +static void +_elm_scroll_anim_stop(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if (sid->cb_func.animate_stop) + sid->cb_func.animate_stop(sid->obj, NULL); +} + +static void +_elm_scroll_edje_drag_v_start_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); + _elm_scroll_drag_start(sid); + sid->freeze = EINA_TRUE; +} + +static void +_elm_scroll_edje_drag_v_stop_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); + _elm_scroll_drag_stop(sid); + sid->freeze = EINA_FALSE; +} + +static void +_elm_scroll_edje_drag_v_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); +} + +static void +_elm_scroll_edje_drag_h_start_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); + _elm_scroll_drag_start(sid); + sid->freeze = EINA_TRUE; +} + +static void +_elm_scroll_edje_drag_h_stop_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); + _elm_scroll_drag_stop(sid); + sid->freeze = EINA_FALSE; +} + +static void +_elm_scroll_edje_drag_h_cb(void *data, + Evas_Object *obj __UNUSED__, + const char *emission __UNUSED__, + const char *source __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + _elm_scroll_scroll_bar_read_and_update(sid); +} + +static void +_elm_scroll_content_size_get(const Evas_Object *obj, + Evas_Coord *w, + Evas_Coord *h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->content) return; + + evas_object_geometry_get(sid->content, NULL, NULL, w, h); +} + +static void +_elm_scroll_content_viewport_size_get(const Evas_Object *obj, + Evas_Coord *w, + Evas_Coord *h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj || !sid->edje_obj) return; + + edje_object_calc_force(sid->edje_obj); + evas_object_geometry_get(sid->pan_obj, NULL, NULL, w, h); +} + +static void +_elm_scroll_content_min_limit(Evas_Object *obj, + Eina_Bool w, + Eina_Bool h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj || !sid->edje_obj) return; + + if (!sid->cb_func.content_min_limit) + { + ERR("Content minimim size limiting is unimplemented -- you " + "must provide it yourself\n"); + return; + } + + sid->cb_func.content_min_limit(obj, w, h); +} + +static Evas_Coord +_elm_scroll_x_mirrored_get(const Evas_Object *obj, + Evas_Coord x) +{ + Evas_Coord cw, ch, w, ret; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, x); + + if (!sid->pan_obj) return 0; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + _elm_scroll_content_viewport_size_get(obj, &w, NULL); + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + ret = (cw - (x + w)); + + return (ret >= 0) ? ret : 0; +} + +/* Update the wanted coordinates according to the x, y passed + * widget directionality, content size and etc. */ +static void +_elm_scroll_wanted_coordinates_update(Elm_Scrollable_Smart_Interface_Data *sid, + Evas_Coord x, + Evas_Coord y) +{ + Evas_Coord cw, ch; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + + /* Update wx/y/w/h - and if the requested positions aren't legal + * adjust a bit. */ + _elm_scroll_content_viewport_size_get(sid->obj, &sid->ww, &sid->wh); + if (x < 0) + sid->wx = 0; + else if ((x + sid->ww) > cw) + sid->wx = cw - sid->ww; + else if (sid->is_mirrored) + sid->wx = _elm_scroll_x_mirrored_get(sid->obj, x); + else + sid->wx = x; + if (y < 0) sid->wy = 0; + else if ((y + sid->wh) > ch) + sid->wy = ch - sid->wh; + else sid->wy = y; +} + +static void +_elm_scroll_momentum_end(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator)) return; + if (sid->down.momentum_animator) + { + Evas_Coord px, py; + _elm_scroll_content_pos_get(sid->obj, &px, &py); + _elm_scroll_wanted_coordinates_update(sid, px, py); + + ecore_animator_del(sid->down.momentum_animator); + sid->down.momentum_animator = NULL; + sid->down.bounce_x_hold = EINA_FALSE; + sid->down.bounce_y_hold = EINA_FALSE; + sid->down.ax = 0; + sid->down.ay = 0; + sid->down.dx = 0; + sid->down.dy = 0; + sid->down.pdx = 0; + sid->down.pdy = 0; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } +} + +static Eina_Bool +_elm_scroll_bounce_x_animator(void *data) +{ + Elm_Scrollable_Smart_Interface_Data *sid; + Evas_Coord x, y, dx, w, odx, ed, md; + double t, p, dt, pd, r; + + sid = data; + t = ecore_loop_time_get(); + dt = t - sid->down.anim_start2; + if (dt >= 0.0) + { + dt = dt / _elm_config->thumbscroll_bounce_friction; + odx = sid->down.b2x - sid->down.bx; + _elm_scroll_content_viewport_size_get(sid->obj, &w, NULL); + if (!sid->down.momentum_animator && (w > abs(odx))) + { + pd = (double)odx / (double)w; + pd = (pd > 0) ? pd : -pd; + pd = 1.0 - ((1.0 - pd) * (1.0 - pd)); + dt = dt / pd; + } + if (dt > 1.0) dt = 1.0; + p = 1.0 - ((1.0 - dt) * (1.0 - dt)); + _elm_scroll_content_pos_get(sid->obj, &x, &y); + dx = (odx * p); + r = 1.0; + if (sid->down.momentum_animator) + { + ed = abs(sid->down.dx * (_elm_config->thumbscroll_friction + + sid->down.extra_time) - sid->down.b0x); + md = abs(_elm_config->thumbscroll_friction * 5 * w); + if (ed > md) r = (double)(md) / (double)ed; + } + x = sid->down.b2x + (int)((double)(dx - odx) * r); + if (!sid->down.cancelled) + _elm_scroll_content_pos_set(sid->obj, x, y); + if (dt >= 1.0) + { + if (sid->down.momentum_animator) + sid->down.bounce_x_hold = EINA_TRUE; + if ((!sid->down.bounce_y_animator) && + (!sid->scrollto.y.animator)) + _elm_scroll_anim_stop(sid); + sid->down.bounce_x_animator = NULL; + sid->down.pdx = 0; + sid->bouncemex = EINA_FALSE; + _elm_scroll_momentum_end(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + return ECORE_CALLBACK_CANCEL; + } + } + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_elm_scroll_bounce_y_animator(void *data) +{ + Elm_Scrollable_Smart_Interface_Data *sid; + Evas_Coord x, y, dy, h, ody, ed, md; + double t, p, dt, pd, r; + + sid = data; + t = ecore_loop_time_get(); + dt = t - sid->down.anim_start3; + if (dt >= 0.0) + { + dt = dt / _elm_config->thumbscroll_bounce_friction; + ody = sid->down.b2y - sid->down.by; + _elm_scroll_content_viewport_size_get(sid->obj, NULL, &h); + if (!sid->down.momentum_animator && (h > abs(ody))) + { + pd = (double)ody / (double)h; + pd = (pd > 0) ? pd : -pd; + pd = 1.0 - ((1.0 - pd) * (1.0 - pd)); + dt = dt / pd; + } + if (dt > 1.0) dt = 1.0; + p = 1.0 - ((1.0 - dt) * (1.0 - dt)); + _elm_scroll_content_pos_get(sid->obj, &x, &y); + dy = (ody * p); + r = 1.0; + if (sid->down.momentum_animator) + { + ed = abs(sid->down.dy * (_elm_config->thumbscroll_friction + + sid->down.extra_time) - sid->down.b0y); + md = abs(_elm_config->thumbscroll_friction * 5 * h); + if (ed > md) r = (double)(md) / (double)ed; + } + y = sid->down.b2y + (int)((double)(dy - ody) * r); + if (!sid->down.cancelled) + _elm_scroll_content_pos_set(sid->obj, x, y); + if (dt >= 1.0) + { + if (sid->down.momentum_animator) + sid->down.bounce_y_hold = EINA_TRUE; + if ((!sid->down.bounce_x_animator) && + (!sid->scrollto.y.animator)) + _elm_scroll_anim_stop(sid); + sid->down.bounce_y_animator = NULL; + sid->down.pdy = 0; + sid->bouncemey = EINA_FALSE; + _elm_scroll_momentum_end(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + return ECORE_CALLBACK_CANCEL; + } + } + + return ECORE_CALLBACK_RENEW; +} + +static void +_elm_scroll_bounce_eval(Elm_Scrollable_Smart_Interface_Data *sid) +{ + Evas_Coord mx, my, px, py, bx, by, b2x, b2y, minx = 0, miny = 0; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if (sid->freeze) return; + if ((!sid->bouncemex) && (!sid->bouncemey)) return; + if (sid->down.now) return; // down bounce while still held down + if (sid->down.onhold_animator) + { + ecore_animator_del(sid->down.onhold_animator); + sid->down.onhold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.hold_animator) + { + ecore_animator_del(sid->down.hold_animator); + sid->down.hold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + psd->api->pos_get(sid->pan_obj, &px, &py); + bx = px; + by = py; + if (px < minx) px = minx; + if ((px - minx) > mx) px = mx + minx; + if (py < miny) py = miny; + if ((py - miny) > my) py = my + miny; + b2x = px; + b2y = py; + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get(sid->obj))) + { + if ((!sid->down.bounce_x_animator) && (!sid->bounce_animator_disabled)) + { + if (sid->bouncemex) + { + if (sid->scrollto.x.animator) + { + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + } + sid->down.bounce_x_animator = + ecore_animator_add(_elm_scroll_bounce_x_animator, sid); + sid->down.anim_start2 = ecore_loop_time_get(); + sid->down.bx = bx; + sid->down.bx0 = bx; + sid->down.b2x = b2x; + if (sid->down.momentum_animator) + sid->down.b0x = sid->down.ax; + else sid->down.b0x = 0; + } + } + } + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get(sid->obj))) + { + if ((!sid->down.bounce_y_animator) && (!sid->bounce_animator_disabled)) + { + if (sid->bouncemey) + { + if (sid->scrollto.y.animator) + { + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + } + sid->down.bounce_y_animator = + ecore_animator_add(_elm_scroll_bounce_y_animator, sid); + sid->down.anim_start3 = ecore_loop_time_get(); + sid->down.by = by; + sid->down.by0 = by; + sid->down.b2y = b2y; + if (sid->down.momentum_animator) + sid->down.b0y = sid->down.ay; + else sid->down.b0y = 0; + } + } + } +} + +static void +_elm_scroll_content_pos_get(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->pos_get(sid->pan_obj, x, y); +} + +static void +_elm_scroll_content_pos_set(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y) +{ + Evas_Coord mx = 0, my = 0, px = 0, py = 0, minx = 0, miny = 0; + double vx, vy; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->edje_obj || !sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + // FIXME: allow for bounce outside of range + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + + if (mx > 0) vx = (double)(x - minx) / (double)mx; + else vx = 0.0; + + if (vx < 0.0) vx = 0.0; + else if (vx > 1.0) + vx = 1.0; + + if (my > 0) vy = (double)(y - miny) / (double)my; + else vy = 0.0; + + if (vy < 0.0) vy = 0.0; + else if (vy > 1.0) + vy = 1.0; + + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, vy); + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.hbar", vx, 0.0); + psd->api->pos_get(sid->pan_obj, &px, &py); + if (!_elm_config->thumbscroll_bounce_enable) + { + if (x < minx) x = minx; + if ((x - minx) > mx) x = mx + minx; + if (y < miny) y = miny; + if ((y - miny) > my) y = my + miny; + } + + if (!sid->bounce_horiz) + { + if (x < minx) x = minx; + if ((x - minx) > mx) x = mx + minx; + } + if (!sid->bounce_vert) + { + if (y < miny) y = miny; + if (y - miny > my) y = my + miny; + } + + psd->api->pos_set(sid->pan_obj, x, y); + if ((px != x) || (py != y)) + edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); + if (!sid->down.bounce_x_animator) + { + if (((x < minx) && (0 <= sid->down.dx)) || + ((x > (mx + minx)) && (0 >= sid->down.dx))) + { + sid->bouncemex = EINA_TRUE; + _elm_scroll_bounce_eval(sid); + } + else + sid->bouncemex = EINA_FALSE; + } + if (!sid->down.bounce_y_animator) + { + if (((y < miny) && (0 <= sid->down.dy)) || + ((y > (my + miny)) && (0 >= sid->down.dy))) + { + sid->bouncemey = EINA_TRUE; + _elm_scroll_bounce_eval(sid); + } + else + sid->bouncemey = EINA_FALSE; + } + + if ((x != px) || (y != py)) + { + if (sid->cb_func.scroll) + sid->cb_func.scroll(obj, NULL); + } + if (x != px) + { + if (x == minx) + { + if (sid->cb_func.edge_left) + sid->cb_func.edge_left(obj, NULL); + } + if (x == (mx + minx)) + { + if (sid->cb_func.edge_right) + sid->cb_func.edge_right(obj, NULL); + } + } + if (y != py) + { + if (y == miny) + { + if (sid->cb_func.edge_top) + sid->cb_func.edge_top(obj, NULL); + } + if (y == my + miny) + { + if (sid->cb_func.edge_bottom) + sid->cb_func.edge_bottom(obj, NULL); + } + } +} + +static void +_elm_scroll_mirrored_set(Evas_Object *obj, + Eina_Bool mirrored) +{ + Evas_Coord wx; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->edje_obj) return; + + mirrored = !!mirrored; + + if (sid->is_mirrored == mirrored) + return; + + sid->is_mirrored = mirrored; + edje_object_mirrored_set(sid->edje_obj, mirrored); + + if (sid->is_mirrored) + wx = _elm_scroll_x_mirrored_get(sid->obj, sid->wx); + else + wx = sid->wx; + + _elm_scroll_content_pos_set(sid->obj, wx, sid->wy); +} + +/* returns TRUE when we need to move the scroller, FALSE otherwise. + * Updates w and h either way, so save them if you need them. */ +static Eina_Bool +_elm_scroll_content_region_show_internal(Evas_Object *obj, + Evas_Coord *_x, + Evas_Coord *_y, + Evas_Coord w, + Evas_Coord h) +{ + Evas_Coord mx = 0, my = 0, cw = 0, ch = 0, px = 0, py = 0, nx, ny, + minx = 0, miny = 0, pw = 0, ph = 0, x = *_x, y = *_y; + + ELM_SCROLL_IFACE_DATA_GET(obj, sid); + + if (!sid->pan_obj) return EINA_FALSE; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + psd->api->pos_get(sid->pan_obj, &px, &py); + evas_object_geometry_get(sid->pan_obj, NULL, NULL, &pw, &ph); + + nx = px; + if ((x < px) && ((x + w) < (px + (cw - mx)))) nx = x; + else if ((x > px) && ((x + w) > (px + (cw - mx)))) + nx = x + w - (cw - mx); + ny = py; + if ((y < py) && ((y + h) < (py + (ch - my)))) ny = y; + else if ((y > py) && ((y + h) > (py + (ch - my)))) + ny = y + h - (ch - my); + + if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || + (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) + { + _elm_scroll_anim_stop(sid); + } + if (sid->scrollto.x.animator) + { + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + } + if (sid->scrollto.y.animator) + { + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + } + if (sid->down.bounce_x_animator) + { + ecore_animator_del(sid->down.bounce_x_animator); + sid->down.bounce_x_animator = NULL; + sid->bouncemex = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.bounce_y_animator) + { + ecore_animator_del(sid->down.bounce_y_animator); + sid->down.bounce_y_animator = NULL; + sid->bouncemey = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.hold_animator) + { + ecore_animator_del(sid->down.hold_animator); + sid->down.hold_animator = NULL; + _elm_scroll_drag_stop(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.momentum_animator) + { + ecore_animator_del(sid->down.momentum_animator); + sid->down.momentum_animator = NULL; + sid->down.bounce_x_hold = EINA_FALSE; + sid->down.bounce_y_hold = EINA_FALSE; + sid->down.ax = 0; + sid->down.ay = 0; + sid->down.pdx = 0; + sid->down.pdy = 0; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + + x = nx; + if ((x + pw) > cw) x = cw - pw; + if (x < minx) x = minx; + y = ny; + if ((y + ph) > ch) y = ch - ph; + if (y < miny) y = miny; + + if ((x == px) && (y == py)) return EINA_FALSE; + *_x = x; + *_y = y; + return EINA_TRUE; +} + +/* Set should be used for calculated positions, for example, when we move + * because of an animation or because this is the correct position after + * constraints. */ +static void +_elm_scroll_content_region_set(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) + { + _elm_scroll_content_pos_set(obj, x, y); + sid->down.sx = x; + sid->down.sy = y; + sid->down.x = sid->down.history[0].x; + sid->down.y = sid->down.history[0].y; + } +} + +/* Set should be used for setting the wanted position, for example a + * user scroll or moving the cursor in an entry. */ +static void +_elm_scroll_content_region_show(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->wx = x; + sid->wy = y; + sid->ww = w; + sid->wh = h; + if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) + { + _elm_scroll_content_pos_set(obj, x, y); + sid->down.sx = x; + sid->down.sy = y; + sid->down.x = sid->down.history[0].x; + sid->down.y = sid->down.history[0].y; + } +} + +static void +_elm_scroll_wanted_region_set(Evas_Object *obj) +{ + Evas_Coord ww, wh, wx; + + ELM_SCROLL_IFACE_DATA_GET(obj, sid); + + wx = sid->wx; + + if (sid->down.now || sid->down.momentum_animator || + sid->down.bounce_x_animator || sid->down.bounce_y_animator || + sid->down.hold_animator || sid->down.onhold_animator || + sid->scrollto.x.animator || sid->scrollto.y.animator) + return; + + sid->content_info.resized = EINA_FALSE; + + /* Flip to RTL cords only if init in RTL mode */ + if (sid->is_mirrored) + wx = _elm_scroll_x_mirrored_get(obj, sid->wx); + + if (sid->ww == -1) + { + _elm_scroll_content_viewport_size_get(obj, &ww, &wh); + } + else + { + ww = sid->ww; + wh = sid->wh; + } + + _elm_scroll_content_region_set(obj, wx, sid->wy, ww, wh); +} + +static void +_elm_scroll_wheel_event_cb(void *data, + Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info) +{ + Elm_Scrollable_Smart_Interface_Data *sid; + Evas_Event_Mouse_Wheel *ev; + Evas_Coord x = 0, y = 0; + int direction = 0; + + sid = data; + ev = event_info; + direction = ev->direction; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; + if ((evas_key_modifier_is_set(ev->modifiers, "Control")) || + (evas_key_modifier_is_set(ev->modifiers, "Alt")) || + (evas_key_modifier_is_set(ev->modifiers, "Meta")) || + (evas_key_modifier_is_set(ev->modifiers, "Hyper")) || + (evas_key_modifier_is_set(ev->modifiers, "Super"))) + return; + else if (evas_key_modifier_is_set(ev->modifiers, "Shift")) + direction = 1; + _elm_scroll_content_pos_get(sid->obj, &x, &y); + if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || + (sid->scrollto.x.animator) || (sid->scrollto.y.animator)) + { + _elm_scroll_anim_stop(sid); + } + if (sid->scrollto.x.animator) + { + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + } + if (sid->scrollto.y.animator) + { + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + } + if (sid->down.bounce_x_animator) + { + ecore_animator_del(sid->down.bounce_x_animator); + sid->down.bounce_x_animator = NULL; + sid->bouncemex = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.bounce_y_animator) + { + ecore_animator_del(sid->down.bounce_y_animator); + sid->down.bounce_y_animator = NULL; + sid->bouncemey = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (!direction) + y += ev->z * sid->step.y; + else if (direction == 1) + x += ev->z * sid->step.x; + + if ((!sid->hold) && (!sid->freeze)) + { + _elm_scroll_wanted_coordinates_update(sid, x, y); + _elm_scroll_content_pos_set(sid->obj, x, y); + } +} + +static Eina_Bool +_elm_scroll_post_event_up(void *data, + Evas *e __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + if (sid->obj) + { + if (sid->down.dragged) + { + elm_widget_drag_lock_x_set(sid->obj, EINA_FALSE); + elm_widget_drag_lock_y_set(sid->obj, EINA_FALSE); + } + } + return EINA_TRUE; +} + +static Eina_Bool +_paging_is_enabled(Elm_Scrollable_Smart_Interface_Data *sid) +{ + if ((sid->pagerel_h == 0.0) && (!sid->pagesize_h) && + (sid->pagerel_v == 0.0) && (!sid->pagesize_v)) + return EINA_FALSE; + return EINA_TRUE; +} + +static Eina_Bool +_elm_scroll_momentum_animator(void *data) +{ + double t, dt, p; + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Coord x, y, dx, dy, px, py, maxx, maxy, minx, miny; + Eina_Bool no_bounce_x_end = EINA_FALSE, no_bounce_y_end = EINA_FALSE; + + if (!sid->pan_obj) return ECORE_CALLBACK_CANCEL; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + t = ecore_loop_time_get(); + dt = t - sid->down.anim_start; + if (dt >= 0.0) + { + dt = dt / (_elm_config->thumbscroll_friction + sid->down.extra_time); + if (dt > 1.0) dt = 1.0; + p = 1.0 - ((1.0 - dt) * (1.0 - dt)); + dx = (sid->down.dx * (_elm_config->thumbscroll_friction + + sid->down.extra_time) * p); + dy = (sid->down.dy * (_elm_config->thumbscroll_friction + + sid->down.extra_time) * p); + sid->down.ax = dx; + sid->down.ay = dy; + x = sid->down.sx - dx; + y = sid->down.sy - dy; + _elm_scroll_content_pos_get(sid->obj, &px, &py); + if ((sid->down.bounce_x_animator) || + (sid->down.bounce_x_hold)) + { + sid->down.bx = sid->down.bx0 - dx + sid->down.b0x; + x = px; + } + if ((sid->down.bounce_y_animator) || + (sid->down.bounce_y_hold)) + { + sid->down.by = sid->down.by0 - dy + sid->down.b0y; + y = py; + } + _elm_scroll_content_pos_set(sid->obj, x, y); + _elm_scroll_wanted_coordinates_update(sid, x, y); + psd->api->pos_max_get(sid->pan_obj, &maxx, &maxy); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + if (!sid->bounce_horiz) + { + if (x <= minx) no_bounce_x_end = EINA_TRUE; + if ((x - minx) >= maxx) no_bounce_x_end = EINA_TRUE; + } + if (!sid->bounce_vert) + { + if (y <= miny) no_bounce_y_end = EINA_TRUE; + if ((y - miny) >= maxy) no_bounce_y_end = EINA_TRUE; + } + if ((dt >= 1.0) || + ((sid->down.bounce_x_hold) && (sid->down.bounce_y_hold)) || + (no_bounce_x_end && no_bounce_y_end)) + { + _elm_scroll_anim_stop(sid); + + sid->down.momentum_animator = NULL; + sid->down.bounce_x_hold = EINA_FALSE; + sid->down.bounce_y_hold = EINA_FALSE; + sid->down.ax = 0; + sid->down.ay = 0; + sid->down.pdx = 0; + sid->down.pdy = 0; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + + return ECORE_CALLBACK_CANCEL; + } + } + + return ECORE_CALLBACK_RENEW; +} + +static Evas_Coord +_elm_scroll_page_x_get(Elm_Scrollable_Smart_Interface_Data *sid, + int offset) +{ + Evas_Coord x, y, w, h, cw, ch, minx = 0; + + if (!sid->pan_obj) return 0; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + _elm_scroll_content_pos_get(sid->obj, &x, &y); + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + psd->api->pos_min_get(sid->pan_obj, &minx, NULL); + + x += offset; + + if (sid->pagerel_h > 0.0) + sid->pagesize_h = w * sid->pagerel_h; + if (sid->pagesize_h > 0) + { + x = x + (sid->pagesize_h * 0.5); + x = x / (sid->pagesize_h); + x = x * (sid->pagesize_h); + } + if ((x + w) > cw) x = cw - w; + if (x < minx) x = minx; + + return x; +} + +static Evas_Coord +_elm_scroll_page_y_get(Elm_Scrollable_Smart_Interface_Data *sid, + int offset) +{ + Evas_Coord x, y, w, h, cw, ch, miny = 0; + + if (!sid->pan_obj) return 0; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + _elm_scroll_content_pos_get(sid->obj, &x, &y); + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + psd->api->pos_min_get(sid->pan_obj, NULL, &miny); + + y += offset; + + if (sid->pagerel_v > 0.0) + sid->pagesize_v = h * sid->pagerel_v; + if (sid->pagesize_v > 0) + { + y = y + (sid->pagesize_v * 0.5); + y = y / (sid->pagesize_v); + y = y * (sid->pagesize_v); + } + if ((y + h) > ch) y = ch - h; + if (y < miny) y = miny; + + return y; +} + +static Eina_Bool +_elm_scroll_scroll_to_x_animator(void *data) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Coord px, py; + double t, tt; + + if (!sid->pan_obj) return ECORE_CALLBACK_CANCEL; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + t = ecore_loop_time_get(); + tt = (t - sid->scrollto.x.t_start) / + (sid->scrollto.x.t_end - sid->scrollto.x.t_start); + tt = 1.0 - tt; + tt = 1.0 - (tt * tt); + psd->api->pos_get(sid->pan_obj, &px, &py); + px = (sid->scrollto.x.start * (1.0 - tt)) + + (sid->scrollto.x.end * tt); + if (t >= sid->scrollto.x.t_end) + { + px = sid->scrollto.x.end; + _elm_scroll_content_pos_set(sid->obj, px, py); + sid->down.sx = px; + sid->down.x = sid->down.history[0].x; + _elm_scroll_wanted_coordinates_update(sid, px, py); + sid->scrollto.x.animator = NULL; + if ((!sid->scrollto.y.animator) && (!sid->down.bounce_y_animator)) + _elm_scroll_anim_stop(sid); + return ECORE_CALLBACK_CANCEL; + } + _elm_scroll_content_pos_set(sid->obj, px, py); + _elm_scroll_wanted_coordinates_update(sid, px, py); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_elm_scroll_scroll_to_y_animator(void *data) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Coord px, py; + double t, tt; + + if (!sid->pan_obj) return EINA_FALSE; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + t = ecore_loop_time_get(); + tt = (t - sid->scrollto.y.t_start) / + (sid->scrollto.y.t_end - sid->scrollto.y.t_start); + tt = 1.0 - tt; + tt = 1.0 - (tt * tt); + psd->api->pos_get(sid->pan_obj, &px, &py); + py = (sid->scrollto.y.start * (1.0 - tt)) + + (sid->scrollto.y.end * tt); + if (t >= sid->scrollto.y.t_end) + { + py = sid->scrollto.y.end; + _elm_scroll_content_pos_set(sid->obj, px, py); + sid->down.sy = py; + sid->down.y = sid->down.history[0].y; + _elm_scroll_wanted_coordinates_update(sid, px, py); + sid->scrollto.y.animator = NULL; + if ((!sid->scrollto.x.animator) && (!sid->down.bounce_x_animator)) + _elm_scroll_anim_stop(sid); + return ECORE_CALLBACK_CANCEL; + } + _elm_scroll_content_pos_set(sid->obj, px, py); + _elm_scroll_wanted_coordinates_update(sid, px, py); + + return ECORE_CALLBACK_RENEW; +} + +static void +_elm_scroll_scroll_to_y(Elm_Scrollable_Smart_Interface_Data *sid, + double t_in, + Evas_Coord pos_y) +{ + Evas_Coord px, py, x, y, w, h; + double t; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if (sid->freeze) return; + if (t_in <= 0.0) + { + _elm_scroll_content_pos_get(sid->obj, &x, &y); + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + y = pos_y; + _elm_scroll_content_region_set(sid->obj, x, y, w, h); + return; + } + t = ecore_loop_time_get(); + psd->api->pos_get(sid->pan_obj, &px, &py); + sid->scrollto.y.start = py; + sid->scrollto.y.end = pos_y; + sid->scrollto.y.t_start = t; + sid->scrollto.y.t_end = t + t_in; + if (!sid->scrollto.y.animator) + { + sid->scrollto.y.animator = + ecore_animator_add(_elm_scroll_scroll_to_y_animator, sid); + if (!sid->scrollto.x.animator) + _elm_scroll_anim_start(sid); + } + if (sid->down.bounce_y_animator) + { + ecore_animator_del(sid->down.bounce_y_animator); + sid->down.bounce_y_animator = NULL; + _elm_scroll_momentum_end(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + sid->bouncemey = EINA_FALSE; +} + +static void +_elm_scroll_scroll_to_x(Elm_Scrollable_Smart_Interface_Data *sid, + double t_in, + Evas_Coord pos_x) +{ + Evas_Coord px, py, x, y, w, h; + double t; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if (sid->freeze) return; + if (t_in <= 0.0) + { + _elm_scroll_content_pos_get(sid->obj, &x, &y); + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + x = pos_x; + _elm_scroll_content_region_set(sid->obj, x, y, w, h); + return; + } + t = ecore_loop_time_get(); + psd->api->pos_get(sid->pan_obj, &px, &py); + sid->scrollto.x.start = px; + sid->scrollto.x.end = pos_x; + sid->scrollto.x.t_start = t; + sid->scrollto.x.t_end = t + t_in; + if (!sid->scrollto.x.animator) + { + sid->scrollto.x.animator = + ecore_animator_add(_elm_scroll_scroll_to_x_animator, sid); + if (!sid->scrollto.y.animator) + _elm_scroll_anim_start(sid); + } + if (sid->down.bounce_x_animator) + { + ecore_animator_del(sid->down.bounce_x_animator); + sid->down.bounce_x_animator = NULL; + _elm_scroll_momentum_end(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + sid->bouncemex = EINA_FALSE; +} + +static void +_elm_scroll_mouse_up_event_cb(void *data, + Evas *e, + Evas_Object *obj __UNUSED__, + void *event_info) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Coord x = 0, y = 0, ox = 0, oy = 0; + Evas_Event_Mouse_Down *ev; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + ev = event_info; + sid->down.hold_parent = EINA_FALSE; + sid->down.dx = 0; + sid->down.dy = 0; + evas_post_event_callback_push(e, _elm_scroll_post_event_up, sid); + + // FIXME: respect elm_widget_scroll_hold_get of parent container + if (!_elm_config->thumbscroll_enable) return; + + if (ev->button == 1) + { + if (sid->down.onhold_animator) + { + ecore_animator_del(sid->down.onhold_animator); + sid->down.onhold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + x = ev->canvas.x - sid->down.x; + y = ev->canvas.y - sid->down.y; + if (sid->down.dragged) + { + _elm_scroll_drag_stop(sid); + if ((!sid->hold) && (!sid->freeze)) + { + int i; + double t, at, dt; + Evas_Coord ax, ay, dx, dy, vel; + +#ifdef EVTIME + t = ev->timestamp / 1000.0; +#else + t = ecore_loop_time_get(); +#endif + + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + ax = ev->canvas.x; + ay = ev->canvas.y; + at = 0.0; +#ifdef SCROLLDBG + DBG("------ %i %i\n", ev->canvas.x, ev->canvas.y); +#endif + for (i = 0; i < 60; i++) + { + dt = t - sid->down.history[i].timestamp; + if (dt > 0.2) break; +#ifdef SCROLLDBG + DBG("H: %i %i @ %1.3f\n", + sid->down.history[i].x, + sid->down.history[i].y, dt); +#endif + at += dt; + ax += sid->down.history[i].x; + ay += sid->down.history[i].y; + } + ax /= (i + 1); + ay /= (i + 1); + at /= (i + 1); + at /= _elm_config->thumbscroll_sensitivity_friction; + dx = ev->canvas.x - ax; + dy = ev->canvas.y - ay; + if (at > 0) + { + vel = sqrt((dx * dx) + (dy * dy)) / at; + if ((_elm_config->thumbscroll_friction > 0.0) && + (vel > _elm_config->thumbscroll_momentum_threshold)) + { + int minx, miny, mx, my, px, py; + + psd->api->pos_min_get + (sid->pan_obj, &minx, &miny); + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_get(sid->pan_obj, &px, &py); + sid->down.dx = ((double)dx / at); + sid->down.dy = ((double)dy / at); + if (((sid->down.dx > 0) && (sid->down.pdx > 0)) || + ((sid->down.dx < 0) && (sid->down.pdx < 0))) + if (px > minx && px < mx) + sid->down.dx += (double)sid->down.pdx * 1.5; + // FIXME: * 1.5 - probably should be config + if (((sid->down.dy > 0) && (sid->down.pdy > 0)) || + ((sid->down.dy < 0) && (sid->down.pdy < 0))) + if (py > miny && py < my) + sid->down.dy += (double)sid->down.pdy * 1.5; + // FIXME: * 1.5 - probably should be config + if (((sid->down.dx > 0) && (sid->down.pdx > 0)) || + ((sid->down.dx < 0) && (sid->down.pdx < 0)) || + ((sid->down.dy > 0) && (sid->down.pdy > 0)) || + ((sid->down.dy < 0) && (sid->down.pdy < 0))) + { + double tt = ecore_loop_time_get(); + double dtt = tt - sid->down.anim_start; + + if (dtt < 0.0) dtt = 0.0; + else if (dtt > + _elm_config->thumbscroll_friction) + dtt = _elm_config->thumbscroll_friction; + sid->down.extra_time = + _elm_config->thumbscroll_friction - dtt; + } + else + sid->down.extra_time = 0.0; + sid->down.pdx = sid->down.dx; + sid->down.pdy = sid->down.dy; + ox = -sid->down.dx; + oy = -sid->down.dy; + if (!_paging_is_enabled(sid)) + { + if ((!sid->down.momentum_animator) && + (!sid->momentum_animator_disabled) && + (sid->obj) && + (!elm_widget_drag_child_locked_y_get + (sid->obj))) + { + sid->down.momentum_animator = + ecore_animator_add + (_elm_scroll_momentum_animator, sid); + ev->event_flags |= + EVAS_EVENT_FLAG_ON_SCROLL; + _elm_scroll_anim_start(sid); + } + sid->down.anim_start = ecore_loop_time_get(); + _elm_scroll_content_pos_get(sid->obj, &x, &y); + sid->down.sx = x; + sid->down.sy = y; + sid->down.b0x = 0; + sid->down.b0y = 0; + } + } + } + } + else + { + sid->down.pdx = 0; + sid->down.pdy = 0; + } + evas_event_feed_hold(e, 0, ev->timestamp, ev->data); + if (_paging_is_enabled(sid)) + { + Evas_Coord pgx, pgy; + + _elm_scroll_content_pos_get(sid->obj, &x, &y); + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get + (sid->obj))) + { + pgx = _elm_scroll_page_x_get(sid, ox); + if (pgx != x) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; + _elm_scroll_scroll_to_x + (sid, _elm_config->page_scroll_friction, pgx); + } + } + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get + (sid->obj))) + { + pgy = _elm_scroll_page_y_get(sid, oy); + if (pgy != y) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; + _elm_scroll_scroll_to_y + (sid, _elm_config->page_scroll_friction, pgy); + } + } + } + } + else + { + sid->down.pdx = 0; + sid->down.pdy = 0; + if (_paging_is_enabled(sid)) + { + Evas_Coord pgx, pgy; + + _elm_scroll_content_pos_get(sid->obj, &x, &y); + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get + (sid->obj))) + { + pgx = _elm_scroll_page_x_get(sid, ox); + if (pgx != x) + _elm_scroll_scroll_to_x + (sid, _elm_config->page_scroll_friction, pgx); + } + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get + (sid->obj))) + { + pgy = _elm_scroll_page_y_get(sid, oy); + if (pgy != y) + _elm_scroll_scroll_to_y + (sid, _elm_config->page_scroll_friction, pgy); + } + } + } + if (sid->down.hold_animator) + { + ecore_animator_del(sid->down.hold_animator); + sid->down.hold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.scroll) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; + sid->down.scroll = EINA_FALSE; + } + if (sid->down.hold) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + sid->down.hold = EINA_FALSE; + } + sid->down.dragged_began = EINA_FALSE; + sid->down.dir_x = EINA_FALSE; + sid->down.dir_y = EINA_FALSE; + sid->down.want_dragged = EINA_FALSE; + sid->down.dragged = EINA_FALSE; + sid->down.now = EINA_FALSE; + _elm_scroll_content_pos_get(sid->obj, &x, &y); + _elm_scroll_content_pos_set(sid->obj, x, y); + _elm_scroll_wanted_coordinates_update(sid, x, y); + + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + + if (!_paging_is_enabled(sid)) + _elm_scroll_bounce_eval(sid); + } +} + +static void +_elm_scroll_mouse_down_event_cb(void *data, + Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info) +{ + Elm_Scrollable_Smart_Interface_Data *sid; + Evas_Event_Mouse_Down *ev; + Evas_Coord x = 0, y = 0; + + sid = data; + ev = event_info; + + if (_elm_config->thumbscroll_enable) + { + sid->down.hold = EINA_FALSE; + if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) || + (sid->down.momentum_animator) || (sid->scrollto.x.animator) || + (sid->scrollto.y.animator)) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL | + EVAS_EVENT_FLAG_ON_HOLD; + sid->down.scroll = EINA_TRUE; + sid->down.hold = EINA_TRUE; + _elm_scroll_anim_stop(sid); + } + if (sid->scrollto.x.animator) + { + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + } + if (sid->scrollto.y.animator) + { + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + } + if (sid->down.bounce_x_animator) + { + ecore_animator_del(sid->down.bounce_x_animator); + sid->down.bounce_x_animator = NULL; + sid->bouncemex = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.bounce_y_animator) + { + ecore_animator_del(sid->down.bounce_y_animator); + sid->down.bounce_y_animator = NULL; + sid->bouncemey = EINA_FALSE; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.hold_animator) + { + ecore_animator_del(sid->down.hold_animator); + sid->down.hold_animator = NULL; + _elm_scroll_drag_stop(sid); + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (sid->down.momentum_animator) + { + ecore_animator_del(sid->down.momentum_animator); + sid->down.momentum_animator = NULL; + sid->down.bounce_x_hold = EINA_FALSE; + sid->down.bounce_y_hold = EINA_FALSE; + sid->down.ax = 0; + sid->down.ay = 0; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + if (ev->button == 1) + { + sid->down.hist.est_timestamp_diff = + ecore_loop_time_get() - ((double)ev->timestamp / 1000.0); + sid->down.hist.tadd = 0.0; + sid->down.hist.dxsum = 0.0; + sid->down.hist.dysum = 0.0; + sid->down.now = EINA_TRUE; + sid->down.dragged = EINA_FALSE; + sid->down.dir_x = EINA_FALSE; + sid->down.dir_y = EINA_FALSE; + sid->down.x = ev->canvas.x; + sid->down.y = ev->canvas.y; + _elm_scroll_content_pos_get(sid->obj, &x, &y); + sid->down.sx = x; + sid->down.sy = y; + sid->down.locked = EINA_FALSE; + memset(&(sid->down.history[0]), 0, + sizeof(sid->down.history[0]) * 60); +#ifdef EVTIME + sid->down.history[0].timestamp = ev->timestamp / 1000.0; + sid->down.history[0].localtimestamp = ecore_loop_time_get(); +#else + sid->down.history[0].timestamp = ecore_loop_time_get(); +#endif + sid->down.history[0].x = ev->canvas.x; + sid->down.history[0].y = ev->canvas.y; + } + sid->down.dragged_began = EINA_FALSE; + sid->down.hold_parent = EINA_FALSE; + sid->down.cancelled = EINA_FALSE; + if (sid->hold || sid->freeze) + sid->down.want_reset = EINA_TRUE; + else + sid->down.want_reset = EINA_FALSE; + } +} + +static Eina_Bool +_elm_scroll_can_scroll(Elm_Scrollable_Smart_Interface_Data *sid, + int dir) +{ + Evas_Coord mx = 0, my = 0, px = 0, py = 0, minx = 0, miny = 0; + + if (!sid->pan_obj) return EINA_FALSE; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->pos_max_get(sid->pan_obj, &mx, &my); + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + psd->api->pos_get(sid->pan_obj, &px, &py); + switch (dir) + { + case LEFT: + if (px > minx) return EINA_TRUE; + break; + + case RIGHT: + if ((px - minx) < mx) return EINA_TRUE; + break; + + case UP: + if (py > miny) return EINA_TRUE; + break; + + case DOWN: + if ((py - miny) < my) return EINA_TRUE; + break; + + default: + break; + } + return EINA_FALSE; +} + +static Eina_Bool +_elm_scroll_post_event_move(void *data, + Evas *e __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + if (sid->down.want_dragged) + { + int start = 0; + + if (sid->down.hold_parent) + { + if ((sid->down.dir_x) && + !_elm_scroll_can_scroll(sid, sid->down.hdir)) + { + sid->down.dir_x = EINA_FALSE; + } + if ((sid->down.dir_y) && + !_elm_scroll_can_scroll(sid, sid->down.vdir)) + { + sid->down.dir_y = EINA_FALSE; + } + } + if (sid->down.dir_x) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get(sid->obj))) + { + sid->down.want_dragged = EINA_FALSE; + sid->down.dragged = EINA_TRUE; + if (sid->obj) + { + elm_widget_drag_lock_x_set(sid->obj, 1); + } + start = 1; + } + else + sid->down.dir_x = EINA_FALSE; + } + if (sid->down.dir_y) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get(sid->obj))) + { + sid->down.want_dragged = EINA_FALSE; + sid->down.dragged = EINA_TRUE; + if (sid->obj) + { + elm_widget_drag_lock_y_set + (sid->obj, EINA_TRUE); + } + start = 1; + } + else + sid->down.dir_y = EINA_FALSE; + } + if ((!sid->down.dir_x) && (!sid->down.dir_y)) + { + sid->down.cancelled = EINA_TRUE; + } + if (start) _elm_scroll_drag_start(sid); + } + + return EINA_TRUE; +} + +static void +_elm_scroll_down_coord_eval(Elm_Scrollable_Smart_Interface_Data *sid, + Evas_Coord *x, + Evas_Coord *y) +{ + Evas_Coord minx, miny; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + if (sid->down.dir_x) *x = sid->down.sx - (*x - sid->down.x); + else *x = sid->down.sx; + if (sid->down.dir_y) *y = sid->down.sy - (*y - sid->down.y); + else *y = sid->down.sy; + + if ((sid->down.dir_x) || (sid->down.dir_y)) + { + if (!((sid->down.dir_x) && (sid->down.dir_y))) + { + if (sid->down.dir_x) *y = sid->down.locked_y; + else *x = sid->down.locked_x; + } + } + + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + + if (*x < minx) + *x += (minx - *x) * _elm_config->thumbscroll_border_friction; + else if (sid->content_info.w <= sid->w) + *x += (sid->down.sx - *x) * _elm_config->thumbscroll_border_friction; + else if ((sid->content_info.w - sid->w + minx) < *x) + *x += (sid->content_info.w - sid->w + minx - *x) * + _elm_config->thumbscroll_border_friction; + + if (*y < miny) + *y += (miny - *y) * _elm_config->thumbscroll_border_friction; + else if (sid->content_info.h <= sid->h) + *y += (sid->down.sy - *y) * _elm_config->thumbscroll_border_friction; + else if ((sid->content_info.h - sid->h + miny) < *y) + *y += (sid->content_info.h - sid->h + miny - *y) * + _elm_config->thumbscroll_border_friction; +} + +static Eina_Bool +_elm_scroll_hold_animator(void *data) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Coord ox = 0, oy = 0, fx = 0, fy = 0; + + fx = sid->down.hold_x; + fy = sid->down.hold_y; + if (_elm_config->scroll_smooth_amount > 0.0) + { + int i, count = 0; + Evas_Coord basex = 0, basey = 0, x, y; + double dt, t, tdiff, tnow, twin; + struct + { + Evas_Coord x, y, dx, dy; + double t, dt; + } pos[60]; + + tdiff = sid->down.hist.est_timestamp_diff; + tnow = ecore_time_get() - tdiff; + t = tnow; + twin = _elm_config->scroll_smooth_time_window; + for (i = 0; i < 60; i++) + { + // oldest point is sid->down.history[i] + // newset is sid->down.history[0] + dt = t - sid->down.history[i].timestamp; + if (dt > twin) + { + i--; + break; + } + x = sid->down.history[i].x; + y = sid->down.history[i].y; + _elm_scroll_down_coord_eval(sid, &x, &y); + if (i == 0) + { + basex = x; + basey = y; + } + pos[i].x = x - basex; + pos[i].y = y - basey; + pos[i].t = + sid->down.history[i].timestamp - sid->down.history[0].timestamp; + count++; + } + count = i; + if (count >= 2) + { + double dtsum = 0.0, tadd, maxdt; + double dxsum = 0.0, dysum = 0.0, xsum = 0.0, ysum = 0.0; + + for (i = 0; i < (count - 1); i++) + { + pos[i].dx = pos[i].x - pos[i + 1].x; + pos[i].dy = pos[i].y - pos[i + 1].y; + pos[i].dt = pos[i].t - pos[i + 1].t; + dxsum += pos[i].dx; + dysum += pos[i].dy; + dtsum += pos[i].dt; + xsum += pos[i].x; + ysum += pos[i].y; + } + maxdt = pos[i].t; + dxsum /= (double)i; + dysum /= (double)i; + dtsum /= (double)i; + xsum /= (double)i; + ysum /= (double)i; + tadd = tnow - sid->down.history[0].timestamp + + _elm_config->scroll_smooth_future_time; + tadd = tadd - (maxdt / 2); +#define WEIGHT(n, o, v) n = (((double)o * (1.0 - v)) + ((double)n * v)) + WEIGHT(tadd, sid->down.hist.tadd, + _elm_config->scroll_smooth_history_weight); + WEIGHT(dxsum, sid->down.hist.dxsum, + _elm_config->scroll_smooth_history_weight); + WEIGHT(dysum, sid->down.hist.dysum, + _elm_config->scroll_smooth_history_weight); + fx = basex + xsum + ((dxsum * tadd) / dtsum); + fy = basey + ysum + ((dysum * tadd) / dtsum); + sid->down.hist.tadd = tadd; + sid->down.hist.dxsum = dxsum; + sid->down.hist.dysum = dysum; + WEIGHT(fx, sid->down.hold_x, _elm_config->scroll_smooth_amount); + WEIGHT(fy, sid->down.hold_y, _elm_config->scroll_smooth_amount); +#undef WEIGHT + } + } + + _elm_scroll_content_pos_get(sid->obj, &ox, &oy); + if (sid->down.dir_x) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get(sid->obj))) + ox = fx; + } + if (sid->down.dir_y) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get(sid->obj))) + oy = fy; + } + + _elm_scroll_content_pos_set(sid->obj, ox, oy); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_elm_scroll_on_hold_animator(void *data) +{ + double t, td; + double vx, vy; + Evas_Coord x, y, ox, oy; + Elm_Scrollable_Smart_Interface_Data *sid; + + sid = data; + t = ecore_loop_time_get(); + if (sid->down.onhold_tlast > 0.0) + { + td = t - sid->down.onhold_tlast; + vx = sid->down.onhold_vx * td * + (double)_elm_config->thumbscroll_threshold * 2.0; + vy = sid->down.onhold_vy * td * + (double)_elm_config->thumbscroll_threshold * 2.0; + _elm_scroll_content_pos_get(sid->obj, &ox, &oy); + x = ox; + y = oy; + + if (sid->down.dir_x) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_x_get(sid->obj))) + { + sid->down.onhold_vxe += vx; + x = ox + (int)sid->down.onhold_vxe; + sid->down.onhold_vxe -= (int)sid->down.onhold_vxe; + } + } + + if (sid->down.dir_y) + { + if ((!sid->obj) || + (!elm_widget_drag_child_locked_y_get(sid->obj))) + { + sid->down.onhold_vye += vy; + y = oy + (int)sid->down.onhold_vye; + sid->down.onhold_vye -= (int)sid->down.onhold_vye; + } + } + + _elm_scroll_content_pos_set(sid->obj, x, y); + } + sid->down.onhold_tlast = t; + + return ECORE_CALLBACK_RENEW; +} + +static void +_elm_scroll_mouse_move_event_cb(void *data, + Evas *e, + Evas_Object *obj __UNUSED__, + void *event_info) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + Evas_Event_Mouse_Move *ev; + Evas_Coord x = 0, y = 0; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + ev = event_info; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) + sid->down.hold_parent = EINA_TRUE; + evas_post_event_callback_push(e, _elm_scroll_post_event_move, sid); + + // FIXME: respect elm_widget_scroll_hold_get of parent container + if (_elm_config->thumbscroll_enable) + { + if (sid->down.now) + { + int dodir = 0; + + if ((sid->scrollto.x.animator) && (!sid->hold) && (!sid->freeze)) + { + Evas_Coord px; + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + psd->api->pos_get(sid->pan_obj, &px, NULL); + sid->down.sx = px; + sid->down.x = sid->down.history[0].x; + } + + if ((sid->scrollto.y.animator) && (!sid->hold) && (!sid->freeze)) + { + Evas_Coord py; + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + psd->api->pos_get(sid->pan_obj, NULL, &py); + sid->down.sy = py; + sid->down.y = sid->down.history[0].y; + } + +#ifdef SCROLLDBG + DBG("::: %i %i\n", ev->cur.canvas.x, ev->cur.canvas.y); +#endif + memmove(&(sid->down.history[1]), &(sid->down.history[0]), + sizeof(sid->down.history[0]) * (60 - 1)); +#ifdef EVTIME + sid->down.history[0].timestamp = ev->timestamp / 1000.0; + sid->down.history[0].localtimestamp = ecore_loop_time_get(); +#else + sid->down.history[0].timestamp = ecore_loop_time_get(); +#endif + sid->down.history[0].x = ev->cur.canvas.x; + sid->down.history[0].y = ev->cur.canvas.y; + + if (!sid->down.dragged_began) + { + x = ev->cur.canvas.x - sid->down.x; + y = ev->cur.canvas.y - sid->down.y; + + sid->down.hdir = -1; + sid->down.vdir = -1; + + if (x > 0) sid->down.hdir = LEFT; + else if (x < 0) + sid->down.hdir = RIGHT; + if (y > 0) sid->down.vdir = UP; + else if (y < 0) + sid->down.vdir = DOWN; + + if (x < 0) x = -x; + if (y < 0) y = -y; + + if ((sid->one_direction_at_a_time) && + (!((sid->down.dir_x) || (sid->down.dir_y)))) + { + if (x > _elm_config->thumbscroll_threshold) + { + if (x > (y * 2)) + { + sid->down.dir_x = EINA_TRUE; + sid->down.dir_y = EINA_FALSE; + dodir++; + } + } + if (y > _elm_config->thumbscroll_threshold) + { + if (y > (x * 2)) + { + sid->down.dir_x = EINA_FALSE; + sid->down.dir_y = EINA_TRUE; + dodir++; + } + } + if (!dodir) + { + sid->down.dir_x = EINA_TRUE; + sid->down.dir_y = EINA_TRUE; + } + } + else + { + sid->down.dir_x = EINA_TRUE; + sid->down.dir_y = EINA_TRUE; + } + } + if ((!sid->hold) && (!sid->freeze)) + { + if ((sid->down.dragged) || + (((x * x) + (y * y)) > + (_elm_config->thumbscroll_threshold * + _elm_config->thumbscroll_threshold))) + { + sid->down.dragged_began = EINA_TRUE; + if (!sid->down.dragged) + { + sid->down.want_dragged = EINA_TRUE; + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + } + if (sid->down.dragged) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + } + if (sid->down.dir_x) + x = sid->down.sx - (ev->cur.canvas.x - sid->down.x); + else + x = sid->down.sx; + if (sid->down.dir_y) + y = sid->down.sy - (ev->cur.canvas.y - sid->down.y); + else + y = sid->down.sy; + if (sid->down.want_reset) + { + sid->down.x = ev->cur.canvas.x; + sid->down.y = ev->cur.canvas.y; + sid->down.want_reset = EINA_FALSE; + } + if ((sid->down.dir_x) || (sid->down.dir_y)) + { + if (!sid->down.locked) + { + sid->down.locked_x = x; + sid->down.locked_y = y; + sid->down.locked = EINA_TRUE; + } + if (!((sid->down.dir_x) && (sid->down.dir_y))) + { + if (sid->down.dir_x) y = sid->down.locked_y; + else x = sid->down.locked_x; + } + } + { + Evas_Coord minx, miny; + + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + if (y < miny) + y += (miny - y) * + _elm_config->thumbscroll_border_friction; + else if (sid->content_info.h <= sid->h) + y += (sid->down.sy - y) * + _elm_config->thumbscroll_border_friction; + else if ((sid->content_info.h - sid->h + miny) < y) + y += (sid->content_info.h - sid->h + miny - y) * + _elm_config->thumbscroll_border_friction; + if (x < minx) + x += (minx - x) * + _elm_config->thumbscroll_border_friction; + else if (sid->content_info.w <= sid->w) + x += (sid->down.sx - x) * + _elm_config->thumbscroll_border_friction; + else if ((sid->content_info.w - sid->w + minx) < x) + x += (sid->content_info.w - sid->w + minx - x) * + _elm_config->thumbscroll_border_friction; + } + + sid->down.hold_x = x; + sid->down.hold_y = y; + if (!sid->down.hold_animator) + sid->down.hold_animator = + ecore_animator_add(_elm_scroll_hold_animator, sid); + } + else + { + if (sid->down.dragged_began) + { + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + if (!sid->down.hold) + { + sid->down.hold = EINA_TRUE; + evas_event_feed_hold + (e, 1, ev->timestamp, ev->data); + } + } + } + } + else if (!sid->freeze) + { + double vx = 0.0, vy = 0.0; + + x = ev->cur.canvas.x - sid->x; + y = ev->cur.canvas.y - sid->y; + if (x < _elm_config->thumbscroll_threshold) + { + if (_elm_config->thumbscroll_threshold > 0.0) + vx = -(double)(_elm_config->thumbscroll_threshold - x) + / _elm_config->thumbscroll_threshold; + else + vx = -1.0; + } + else if (x > (sid->w - _elm_config->thumbscroll_threshold)) + { + if (_elm_config->thumbscroll_threshold > 0.0) + vx = (double)(_elm_config->thumbscroll_threshold - + (sid->w - x)) / + _elm_config->thumbscroll_threshold; + else + vx = 1.0; + } + if (y < _elm_config->thumbscroll_threshold) + { + if (_elm_config->thumbscroll_threshold > 0.0) + vy = -(double)(_elm_config->thumbscroll_threshold - y) + / _elm_config->thumbscroll_threshold; + else + vy = -1.0; + } + else if (y > (sid->h - _elm_config->thumbscroll_threshold)) + { + if (_elm_config->thumbscroll_threshold > 0.0) + vy = (double)(_elm_config->thumbscroll_threshold - + (sid->h - y)) / + _elm_config->thumbscroll_threshold; + else + vy = 1.0; + } + if ((vx != 0.0) || (vy != 0.0)) + { + sid->down.onhold_vx = vx; + sid->down.onhold_vy = vy; + if (!sid->down.onhold_animator) + { + sid->down.onhold_vxe = 0.0; + sid->down.onhold_vye = 0.0; + sid->down.onhold_tlast = 0.0; + sid->down.onhold_animator = ecore_animator_add + (_elm_scroll_on_hold_animator, sid); + } + } + else + { + if (sid->down.onhold_animator) + { + ecore_animator_del(sid->down.onhold_animator); + sid->down.onhold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + } + } + } + } +} + +static void +_elm_scroll_page_adjust(Elm_Scrollable_Smart_Interface_Data *sid) +{ + Evas_Coord x, y, w, h; + + if (!_paging_is_enabled(sid)) return; + + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + + x = _elm_scroll_page_x_get(sid, 0); + y = _elm_scroll_page_y_get(sid, 0); + + _elm_scroll_content_region_set(sid->obj, x, y, w, h); +} + +static void +_elm_scroll_reconfigure(Elm_Scrollable_Smart_Interface_Data *sid) +{ + _elm_scroll_scroll_bar_size_adjust(sid); + _elm_scroll_page_adjust(sid); +} + +static void +_on_edje_move(void *data, + Evas *e __UNUSED__, + Evas_Object *edje_obj, + void *event_info __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + int x, y; + + evas_object_geometry_get(edje_obj, &x, &y, NULL, NULL); + + sid->x = x; + sid->y = y; + + _elm_scroll_reconfigure(sid); +} + +static void +_on_edje_resize(void *data, + Evas *e __UNUSED__, + Evas_Object *edje_obj, + void *event_info __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + int w, h; + + evas_object_geometry_get(edje_obj, NULL, NULL, &w, &h); + + sid->w = w; + sid->h = h; + + _elm_scroll_reconfigure(sid); + _elm_scroll_wanted_region_set(sid->obj); +} + +static void +_elm_scroll_objects_attach(Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET(obj, sid); + + evas_object_event_callback_add + (sid->edje_obj, EVAS_CALLBACK_RESIZE, _on_edje_resize, sid); + evas_object_event_callback_add + (sid->edje_obj, EVAS_CALLBACK_MOVE, _on_edje_move, sid); + + edje_object_signal_callback_add + (sid->edje_obj, "drag", "elm.dragable.vbar", _elm_scroll_edje_drag_v_cb, + sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,set", "elm.dragable.vbar", + _elm_scroll_edje_drag_v_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,start", "elm.dragable.vbar", + _elm_scroll_edje_drag_v_start_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,stop", "elm.dragable.vbar", + _elm_scroll_edje_drag_v_stop_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,step", "elm.dragable.vbar", + _elm_scroll_edje_drag_v_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,page", "elm.dragable.vbar", + _elm_scroll_edje_drag_v_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag", "elm.dragable.hbar", _elm_scroll_edje_drag_h_cb, + sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,set", "elm.dragable.hbar", + _elm_scroll_edje_drag_h_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,start", "elm.dragable.hbar", + _elm_scroll_edje_drag_h_start_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,stop", "elm.dragable.hbar", + _elm_scroll_edje_drag_h_stop_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,step", "elm.dragable.hbar", + _elm_scroll_edje_drag_h_cb, sid); + edje_object_signal_callback_add + (sid->edje_obj, "drag,page", "elm.dragable.hbar", + _elm_scroll_edje_drag_h_cb, sid); + + evas_object_event_callback_add + (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb, + sid); + evas_object_event_callback_add + (sid->event_rect, EVAS_CALLBACK_MOUSE_DOWN, + _elm_scroll_mouse_down_event_cb, sid); + evas_object_event_callback_add + (sid->event_rect, EVAS_CALLBACK_MOUSE_UP, + _elm_scroll_mouse_up_event_cb, sid); + evas_object_event_callback_add + (sid->event_rect, EVAS_CALLBACK_MOUSE_MOVE, + _elm_scroll_mouse_move_event_cb, sid); +} + +static void +_elm_scroll_objects_set(Evas_Object *obj, + Evas_Object *edje_object, + Evas_Object *hit_rectangle) +{ + Evas_Coord mw, mh; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!edje_object) return; + if (sid->edje_obj) evas_object_del(sid->edje_obj); + sid->edje_obj = edje_object; + + if (sid->event_rect) evas_object_del(sid->event_rect); + sid->event_rect = hit_rectangle; + evas_object_repeat_events_set(hit_rectangle, EINA_TRUE); + + _elm_scroll_objects_attach(obj); + + edje_object_scale_set + (sid->edje_obj, elm_widget_scale_get(sid->obj) * + elm_config_scale_get()); + + mw = mh = -1; + elm_coords_finger_size_adjust(1, &mw, 1, &mh); + if (edje_object_part_exists(sid->edje_obj, "elm.scrollbar.base")) + { + Evas_Object *base; + + base = edje_object_part_swallow_get + (sid->edje_obj, "elm.scrollbar.base"); + if (!base) + { + base = evas_object_rectangle_add + (evas_object_evas_get(sid->edje_obj)); + evas_object_color_set(base, 0, 0, 0, 0); + edje_object_part_swallow + (sid->edje_obj, "elm.scrollbar.base", base); + } + if (!_elm_config->thumbscroll_enable) + evas_object_size_hint_min_set(base, mw, mh); + } + sid->vbar_visible = !sid->vbar_visible; + sid->hbar_visible = !sid->hbar_visible; + + _elm_scroll_scroll_bar_visibility_adjust(sid); +} + +static void +_elm_scroll_scroll_bar_reset(Elm_Scrollable_Smart_Interface_Data *sid) +{ + Evas_Coord px = 0, py = 0, minx = 0, miny = 0; + + if (!sid->edje_obj) return; + + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.vbar", 0.0, 0.0); + edje_object_part_drag_value_set + (sid->edje_obj, "elm.dragable.hbar", 0.0, 0.0); + if ((!sid->content) && (!sid->extern_pan)) + { + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.vbar", 1.0, 1.0); + edje_object_part_drag_size_set + (sid->edje_obj, "elm.dragable.hbar", 1.0, 1.0); + } + if (sid->pan_obj) + { + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->pos_min_get(sid->pan_obj, &minx, &miny); + psd->api->pos_get(sid->pan_obj, &px, &py); + psd->api->pos_set(sid->pan_obj, minx, miny); + } + if ((px != minx) || (py != miny)) + edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm"); +} + +/* even external pan objects get this */ +static void +_elm_scroll_pan_changed_cb(void *data, + Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + Evas_Coord w, h; + Elm_Scrollable_Smart_Interface_Data *sid = data; + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->content_size_get(sid->pan_obj, &w, &h); + if ((w != sid->content_info.w) || (h != sid->content_info.h)) + { + sid->content_info.w = w; + sid->content_info.h = h; + _elm_scroll_scroll_bar_size_adjust(sid); + + evas_object_size_hint_min_set + (sid->edje_obj, sid->content_info.w, sid->content_info.h); + sid->content_info.resized = EINA_TRUE; + _elm_scroll_wanted_region_set(sid->obj); + } +} + +static void +_elm_scroll_content_del_cb(void *data, + Evas *e __UNUSED__, + Evas_Object *obj __UNUSED__, + void *event_info __UNUSED__) +{ + Elm_Scrollable_Smart_Interface_Data *sid = data; + + sid->content = NULL; + _elm_scroll_scroll_bar_size_adjust(sid); + _elm_scroll_scroll_bar_reset(sid); +} + +static void +_elm_scroll_content_set(Evas_Object *obj, + Evas_Object *content) +{ + Evas_Coord w, h; + Evas_Object *o; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->edje_obj) return; + + if (sid->content) + { + /* if we had content, for sure we had a pan object */ + _elm_pan_content_set(sid->pan_obj, NULL); + evas_object_event_callback_del_full + (sid->content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid); + } + + sid->content = content; + sid->wx = sid->wy = 0; + /* (-1) means want viewports size */ + sid->ww = sid->wh = -1; + if (!content) return; + + if (!sid->pan_obj) + { + o = _elm_pan_add(evas_object_evas_get(obj)); + sid->pan_obj = o; + evas_object_smart_callback_add + (o, SIG_CHANGED, _elm_scroll_pan_changed_cb, sid); + edje_object_part_swallow(sid->edje_obj, "elm.swallow.content", o); + } + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + evas_object_event_callback_add + (content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid); + + _elm_pan_content_set(sid->pan_obj, content); + psd->api->content_size_get(sid->pan_obj, &w, &h); + sid->content_info.w = w; + sid->content_info.h = h; + + _elm_scroll_scroll_bar_size_adjust(sid); + _elm_scroll_scroll_bar_reset(sid); +} + +static void +_elm_scroll_extern_pan_set(Evas_Object *obj, + Evas_Object *pan) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->edje_obj) return; + + _elm_scroll_content_set(obj, NULL); + + if (sid->pan_obj) + { + evas_object_smart_callback_del + (sid->pan_obj, SIG_CHANGED, _elm_scroll_pan_changed_cb); + } + + if (sid->extern_pan) + { + if (sid->pan_obj) + { + /* not owned by scroller, just leave (was external already) */ + edje_object_part_unswallow(sid->edje_obj, sid->pan_obj); + sid->pan_obj = NULL; + } + } + else + { + if (sid->pan_obj) + { + evas_object_del(sid->pan_obj); + sid->pan_obj = NULL; + } + } + if (!pan) + { + sid->extern_pan = EINA_FALSE; + return; + } + + sid->pan_obj = pan; + + sid->extern_pan = EINA_TRUE; + evas_object_smart_callback_add + (sid->pan_obj, SIG_CHANGED, _elm_scroll_pan_changed_cb, sid); + edje_object_part_swallow + (sid->edje_obj, "elm.swallow.content", sid->pan_obj); +} + +static void +_elm_scroll_drag_start_cb_set(Evas_Object *obj, + void (*drag_start_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.drag_start = drag_start_cb; +} + +static void +_elm_scroll_drag_stop_cb_set(Evas_Object *obj, + void (*drag_stop_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.drag_stop = drag_stop_cb; +} + +static void +_elm_scroll_animate_start_cb_set(Evas_Object *obj, + void (*animate_start_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.animate_start = animate_start_cb; +} + +static void +_elm_scroll_animate_stop_cb_set(Evas_Object *obj, + void (*animate_stop_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.animate_stop = animate_stop_cb; +} + +static void +_elm_scroll_scroll_cb_set(Evas_Object *obj, + void (*scroll_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.scroll = scroll_cb; +} + +static void +_elm_scroll_edge_left_cb_set(Evas_Object *obj, + void (*edge_left_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.edge_left = edge_left_cb; +} + +static void +_elm_scroll_edge_right_cb_set(Evas_Object *obj, + void (*edge_right_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.edge_right = edge_right_cb; +} + +static void +_elm_scroll_edge_top_cb_set(Evas_Object *obj, + void (*edge_top_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.edge_top = edge_top_cb; +} + +static void +_elm_scroll_edge_bottom_cb_set(Evas_Object *obj, + void (*edge_bottom_cb)(Evas_Object *obj, + void *data)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.edge_bottom = edge_bottom_cb; +} + +static void +_elm_content_min_limit_cb_set(Evas_Object *obj, + void (*content_min_limit_cb)(Evas_Object *obj, + Eina_Bool w, + Eina_Bool h)) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->cb_func.content_min_limit = content_min_limit_cb; +} + +static Eina_Bool +_elm_scroll_momentum_animator_disabled_get(const Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE); + + return sid->momentum_animator_disabled; +} + +static void +_elm_scroll_momentum_animator_disabled_set(Evas_Object *obj, + Eina_Bool disabled) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->momentum_animator_disabled = disabled; + if (sid->momentum_animator_disabled) + { + if (sid->down.momentum_animator) + { + ecore_animator_del(sid->down.momentum_animator); + sid->down.momentum_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + } +} + +static Eina_Bool +_elm_scroll_bounce_animator_disabled_get(const Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE); + + return sid->bounce_animator_disabled; +} + +static void +_elm_scroll_bounce_animator_disabled_set(Evas_Object *obj, + Eina_Bool disabled) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->bounce_animator_disabled = disabled; + if (sid->bounce_animator_disabled) + { + if (sid->scrollto.x.animator) + { + ecore_animator_del(sid->scrollto.x.animator); + sid->scrollto.x.animator = NULL; + } + + if (sid->scrollto.y.animator) + { + ecore_animator_del(sid->scrollto.y.animator); + sid->scrollto.y.animator = NULL; + } + } +} + +static Eina_Bool +_elm_scroll_wheel_disabled_get(const Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE); + + return sid->wheel_disabled; +} + +static void +_elm_scroll_wheel_disabled_set(Evas_Object *obj, + Eina_Bool disabled) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->event_rect) return; + + if ((!sid->wheel_disabled) && (disabled)) + evas_object_event_callback_del_full + (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, + _elm_scroll_wheel_event_cb, sid); + else if ((sid->wheel_disabled) && (!disabled)) + evas_object_event_callback_add + (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, + _elm_scroll_wheel_event_cb, sid); + sid->wheel_disabled = disabled; +} + +static void +_elm_scroll_step_size_set(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (x < 1) x = 1; + if (y < 1) y = 1; + sid->step.x = x; + sid->step.y = y; + + _elm_scroll_scroll_bar_size_adjust(sid); +} + +static void +_elm_scroll_step_size_get(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (x) *x = sid->step.x; + if (y) *y = sid->step.y; +} + +static void +_elm_scroll_page_size_set(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->page.x = x; + sid->page.y = y; + + _elm_scroll_scroll_bar_size_adjust(sid); +} + +static void +_elm_scroll_page_size_get(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (x) *x = sid->page.x; + if (y) *y = sid->page.y; +} + +static void +_elm_scroll_policy_set(Evas_Object *obj, + Elm_Scroller_Policy hbar, + Elm_Scroller_Policy vbar) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->edje_obj) return; + + if ((sid->hbar_flags == hbar) && (sid->vbar_flags == vbar)) return; + + sid->hbar_flags = hbar; + sid->vbar_flags = vbar; + if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON) + edje_object_signal_emit + (sid->edje_obj, "elm,action,show_always,hbar", "elm"); + else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF) + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,hbar", "elm"); + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,show_notalways,hbar", "elm"); + if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON) + edje_object_signal_emit + (sid->edje_obj, "elm,action,show_always,vbar", "elm"); + else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF) + edje_object_signal_emit + (sid->edje_obj, "elm,action,hide,vbar", "elm"); + else + edje_object_signal_emit + (sid->edje_obj, "elm,action,show_notalways,vbar", "elm"); + + _elm_scroll_scroll_bar_size_adjust(sid); +} + +static void +_elm_scroll_policy_get(const Evas_Object *obj, + Elm_Scroller_Policy *hbar, + Elm_Scroller_Policy *vbar) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (hbar) *hbar = sid->hbar_flags; + if (vbar) *vbar = sid->vbar_flags; +} + +static void +_elm_scroll_single_direction_set(Evas_Object *obj, + Eina_Bool single_dir) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->one_direction_at_a_time = single_dir; +} + +static Eina_Bool +_elm_scroll_single_direction_get(const Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE); + + return sid->one_direction_at_a_time; +} + +static void +_elm_scroll_hold_set(Evas_Object *obj, + Eina_Bool hold) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->hold = hold; +} + +static void +_elm_scroll_freeze_set(Evas_Object *obj, + Eina_Bool freeze) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->freeze = freeze; + if (sid->freeze) + { + if (sid->down.onhold_animator) + { + ecore_animator_del(sid->down.onhold_animator); + sid->down.onhold_animator = NULL; + if (sid->content_info.resized) + _elm_scroll_wanted_region_set(sid->obj); + } + } + else + _elm_scroll_bounce_eval(sid); +} + +static void +_elm_scroll_bounce_allow_set(Evas_Object *obj, + Eina_Bool horiz, + Eina_Bool vert) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->bounce_horiz = !!horiz; + sid->bounce_vert = !!vert; +} + +static void +_elm_scroll_bounce_allow_get(const Evas_Object *obj, + Eina_Bool *horiz, + Eina_Bool *vert) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (horiz) *horiz = sid->bounce_horiz; + if (vert) *vert = sid->bounce_vert; +} + +static void +_elm_scroll_paging_set(Evas_Object *obj, + double pagerel_h, + double pagerel_v, + Evas_Coord pagesize_h, + Evas_Coord pagesize_v) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + sid->pagerel_h = pagerel_h; + sid->pagerel_v = pagerel_v; + sid->pagesize_h = pagesize_h; + sid->pagesize_v = pagesize_v; + + _elm_scroll_page_adjust(sid); +} + +static void +_elm_scroll_paging_get(const Evas_Object *obj, + double *pagerel_h, + double *pagerel_v, + Evas_Coord *pagesize_h, + Evas_Coord *pagesize_v) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (pagerel_h) *pagerel_h = sid->pagerel_h; + if (pagerel_v) *pagerel_v = sid->pagerel_v; + if (pagesize_h) *pagesize_h = sid->pagesize_h; + if (pagesize_v) *pagesize_v = sid->pagesize_v; +} + +static void +_elm_scroll_current_page_get(const Evas_Object *obj, + int *pagenumber_h, + int *pagenumber_v) +{ + Evas_Coord x, y; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + _elm_scroll_content_pos_get(sid->obj, &x, &y); + if (pagenumber_h) + { + if (sid->pagesize_h > 0) + *pagenumber_h = (x + sid->pagesize_h - 1) / sid->pagesize_h; + else + *pagenumber_h = 0; + } + if (pagenumber_v) + { + if (sid->pagesize_v > 0) + *pagenumber_v = (y + sid->pagesize_v - 1) / sid->pagesize_v; + else + *pagenumber_v = 0; + } +} + +static void +_elm_scroll_last_page_get(const Evas_Object *obj, + int *pagenumber_h, + int *pagenumber_v) +{ + Evas_Coord cw, ch; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->content_size_get(sid->pan_obj, &cw, &ch); + if (pagenumber_h) + { + if (sid->pagesize_h > 0) + *pagenumber_h = cw / sid->pagesize_h + 1; + else + *pagenumber_h = 0; + } + if (pagenumber_v) + { + if (sid->pagesize_v > 0) + *pagenumber_v = ch / sid->pagesize_v + 1; + else + *pagenumber_v = 0; + } +} + +static void +_elm_scroll_page_show(Evas_Object *obj, + int pagenumber_h, + int pagenumber_v) +{ + Evas_Coord x, y, w, h; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + if (pagenumber_h >= 0) x = sid->pagesize_h * pagenumber_h; + if (pagenumber_v >= 0) y = sid->pagesize_v * pagenumber_v; + if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) + _elm_scroll_content_pos_set(obj, x, y); +} + +static void +_elm_scroll_page_bring_in(Evas_Object *obj, + int pagenumber_h, + int pagenumber_v) +{ + Evas_Coord x, y, w, h; + + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + _elm_scroll_content_viewport_size_get(sid->obj, &w, &h); + if (pagenumber_h >= 0) x = sid->pagesize_h * pagenumber_h; + if (pagenumber_v >= 0) y = sid->pagesize_v * pagenumber_v; + if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) + { + _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x); + _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y); + } +} + +static void +_elm_scroll_region_bring_in(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h)) + { + _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x); + _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y); + } +} + +static void +_elm_scroll_gravity_set(Evas_Object *obj, + double x, + double y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->gravity_set(sid->pan_obj, x, y); +} + +static void +_elm_scroll_gravity_get(const Evas_Object *obj, + double *x, + double *y) +{ + ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid); + + if (!sid->pan_obj) return; + + ELM_PAN_DATA_GET(sid->pan_obj, psd); + + psd->api->gravity_get(sid->pan_obj, x, y); +} + +static Eina_Bool +_elm_scroll_interface_add(Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET(obj, sid); + + memset(sid, 0, sizeof(*sid)); + + sid->obj = obj; + + sid->x = 0; + sid->y = 0; + sid->w = 0; + sid->h = 0; + sid->step.x = 32; + sid->step.y = 32; + sid->page.x = -50; + sid->page.y = -50; + sid->hbar_flags = ELM_SCROLLER_POLICY_AUTO; + sid->vbar_flags = ELM_SCROLLER_POLICY_AUTO; + sid->hbar_visible = EINA_TRUE; + sid->vbar_visible = EINA_TRUE; + + sid->bounce_horiz = EINA_TRUE; + sid->bounce_vert = EINA_TRUE; + + sid->one_direction_at_a_time = EINA_TRUE; + sid->momentum_animator_disabled = EINA_FALSE; + sid->bounce_animator_disabled = EINA_FALSE; + + _elm_scroll_scroll_bar_reset(sid); + + return EINA_TRUE; +} + +static void +_elm_scroll_interface_del(Evas_Object *obj) +{ + ELM_SCROLL_IFACE_DATA_GET(obj, sid); + + _elm_scroll_content_set(obj, NULL); + if (!sid->extern_pan) evas_object_del(sid->pan_obj); + + if (sid->down.hold_animator) + ecore_animator_del(sid->down.hold_animator); + if (sid->down.onhold_animator) + ecore_animator_del(sid->down.onhold_animator); + if (sid->down.momentum_animator) + ecore_animator_del(sid->down.momentum_animator); + if (sid->down.bounce_x_animator) + ecore_animator_del(sid->down.bounce_x_animator); + if (sid->down.bounce_y_animator) + ecore_animator_del(sid->down.bounce_y_animator); + if (sid->scrollto.x.animator) ecore_animator_del(sid->scrollto.x.animator); + if (sid->scrollto.y.animator) ecore_animator_del(sid->scrollto.y.animator); +} + +EAPI const char ELM_SCROLLABLE_IFACE_NAME[] = "elm_interface_scrollable"; + +EAPI const Elm_Scrollable_Smart_Interface ELM_SCROLLABLE_IFACE = +{ + { + ELM_SCROLLABLE_IFACE_NAME, + sizeof(Elm_Scrollable_Smart_Interface_Data), + _elm_scroll_interface_add, + _elm_scroll_interface_del + }, + + _elm_scroll_objects_set, + _elm_scroll_content_set, + _elm_scroll_extern_pan_set, + _elm_scroll_drag_start_cb_set, + _elm_scroll_drag_stop_cb_set, + _elm_scroll_animate_start_cb_set, + _elm_scroll_animate_stop_cb_set, + _elm_scroll_scroll_cb_set, + _elm_scroll_edge_left_cb_set, + _elm_scroll_edge_right_cb_set, + _elm_scroll_edge_top_cb_set, + _elm_scroll_edge_bottom_cb_set, + _elm_content_min_limit_cb_set, + _elm_scroll_content_pos_set, + _elm_scroll_content_pos_get, + _elm_scroll_content_region_show, + _elm_scroll_content_region_set, + _elm_scroll_content_size_get, + _elm_scroll_content_viewport_size_get, + _elm_scroll_content_min_limit, + _elm_scroll_step_size_set, + _elm_scroll_step_size_get, + _elm_scroll_page_size_set, + _elm_scroll_page_size_get, + _elm_scroll_policy_set, + _elm_scroll_policy_get, + _elm_scroll_single_direction_set, + _elm_scroll_single_direction_get, + _elm_scroll_mirrored_set, + _elm_scroll_hold_set, + _elm_scroll_freeze_set, + _elm_scroll_bounce_allow_set, + _elm_scroll_bounce_allow_get, + _elm_scroll_paging_set, + _elm_scroll_paging_get, + _elm_scroll_current_page_get, + _elm_scroll_last_page_get, + _elm_scroll_page_show, + _elm_scroll_page_bring_in, + _elm_scroll_region_bring_in, + _elm_scroll_gravity_set, + _elm_scroll_gravity_get, + _elm_scroll_momentum_animator_disabled_get, + _elm_scroll_momentum_animator_disabled_set, + _elm_scroll_bounce_animator_disabled_set, + _elm_scroll_bounce_animator_disabled_get, + _elm_scroll_wheel_disabled_get, + _elm_scroll_wheel_disabled_set +}; diff --git a/legacy/elementary/src/lib/elm_interface_scrollable.h b/legacy/elementary/src/lib/elm_interface_scrollable.h new file mode 100644 index 0000000000..b945a6f0e1 --- /dev/null +++ b/legacy/elementary/src/lib/elm_interface_scrollable.h @@ -0,0 +1,488 @@ +#ifndef ELM_INTEFARCE_SCROLLER_H +#define ELM_INTEFARCE_SCROLLER_H + +/** + * @addtogroup Widget + * @{ + * + * @section elm-scrollable-interface The Elementary Scrollable Interface + * + * This is a common interface for widgets having @b scrollable views. + * Widgets using/implementing this must use the + * @c EVAS_SMART_SUBCLASS_IFACE_NEW macro (instead of the + * @c EVAS_SMART_SUBCLASS_NEW one) when declaring its smart class, + * so an interface is also declared. + * + * The scrollable interface comes built with Elementary and is exposed + * as #ELM_SCROLLABLE_IFACE. + * + * The interface API is explained in details at + * #Elm_Scrollable_Smart_Interface. + * + * An Elementary scrollable interface will handle an internal @b + * panning object. It has the function of clipping and moving the + * actual scrollable content around, by the command of the scrollable + * interface calls. Though it's not the common case, one might + * want/have to change some aspects of the internal panning object + * behavior. For that, we have it also exposed here -- + * #Elm_Pan_Smart_Class. Use elm_pan_smart_class_get() to build your + * custom panning object, when creating a scrollable widget (again, + * only if you need a custom panning object) and set it with + * Elm_Scrollable_Smart_Interface::extern_pan_set. + */ + +/** + * @def ELM_PAN_CLASS + * + * Use this macro to cast whichever subclass of + * #Elm_Pan_Smart_Class into it, so to access its fields. + * + * @ingroup Widget + */ + #define ELM_PAN_CLASS(x) ((Elm_Pan_Smart_Class *)x) + +/** + * @def ELM_PAN_SMART_CLASS_VERSION + * + * Current version for Elementary pan @b base smart class, a value + * which goes to _Elm_Pan_Smart_Class::version. + * + * @ingroup Widget + */ +#define ELM_PAN_SMART_CLASS_VERSION 1 + +/** + * @def ELM_PAN_SMART_CLASS_INIT + * + * Initializer for a whole #Elm_Pan_Smart_Class structure, with + * @c NULL values on its specific fields. + * + * @param smart_class_init initializer to use for the "base" field + * (#Evas_Smart_Class). + * + * @see EVAS_SMART_CLASS_INIT_NULL + * @see EVAS_SMART_CLASS_INIT_NAME_VERSION + * @see ELM_PAN_SMART_CLASS_INIT_NULL + * @see ELM_PAN_SMART_CLASS_INIT_NAME_VERSION + * + * @ingroup Widget + */ +#define ELM_PAN_SMART_CLASS_INIT(smart_class_init) \ + {smart_class_init, ELM_PAN_SMART_CLASS_VERSION, NULL, NULL, NULL, NULL, \ + NULL, NULL, NULL} + +/** + * @def ELM_PAN_SMART_CLASS_INIT_NULL + * + * Initializer to zero out a whole #Elm_Pan_Smart_Class structure. + * + * @see ELM_PAN_SMART_CLASS_INIT_NAME_VERSION + * @see ELM_PAN_SMART_CLASS_INIT + * + * @ingroup Widget + */ +#define ELM_PAN_SMART_CLASS_INIT_NULL \ + ELM_PAN_SMART_CLASS_INIT(EVAS_SMART_CLASS_INIT_NULL) + +/** + * @def ELM_PAN_SMART_CLASS_INIT_NAME_VERSION + * + * Initializer to zero out a whole #Elm_Pan_Smart_Class structure and + * set its name and version. + * + * This is similar to #ELM_PAN_SMART_CLASS_INIT_NULL, but it will + * also set the version field of #Elm_Pan_Smart_Class (base field) + * to the latest #ELM_PAN_SMART_CLASS_VERSION and name it to the + * specific value. + * + * It will keep a reference to the name field as a "const char *", + * i.e., the name must be available while the structure is + * used (hint: static or global variable!) and must not be modified. + * + * @see ELM_PAN_SMART_CLASS_INIT_NULL + * @see ELM_PAN_SMART_CLASS_INIT + * + * @ingroup Widget + */ +#define ELM_PAN_SMART_CLASS_INIT_NAME_VERSION(name) \ + ELM_PAN_SMART_CLASS_INIT(EVAS_SMART_CLASS_INIT_NAME_VERSION(name)) + +/** + * Elementary scroller panning base smart class. This inherits + * directly from the Evas smart clipped class (an object clipping + * children to its viewport/size). It is exposed here only to build + * widgets needing a custom panning behavior. + */ +typedef struct _Elm_Pan_Smart_Class Elm_Pan_Smart_Class; +struct _Elm_Pan_Smart_Class +{ + Evas_Smart_Class base; /* it's a clipped smart object */ + + int version; /**< Version of this smart class definition */ + + void (*pos_set)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y); + void (*pos_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*pos_max_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*pos_min_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*content_size_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*gravity_set)(Evas_Object *obj, + double x, + double y); + void (*gravity_get)(const Evas_Object *obj, + double *x, + double *y); +}; + +/** + * Elementary scroller panning base smart data. + */ +typedef struct _Elm_Pan_Smart_Data Elm_Pan_Smart_Data; +struct _Elm_Pan_Smart_Data +{ + Evas_Object_Smart_Clipped_Data base; + + const Elm_Pan_Smart_Class *api; /**< This is the pointer to the object's class, from where we can reach/call its class functions */ + + Evas_Object *self; + Evas_Object *content; + Evas_Coord x, y, w, h; + Evas_Coord content_w, content_h, px, py; + double gravity_x, gravity_y; + Evas_Coord prev_cw, prev_ch, delta_posx, delta_posy; +}; + +/** + * Elementary scrollable interface base data. + */ +typedef struct _Elm_Scrollable_Smart_Interface_Data + Elm_Scrollable_Smart_Interface_Data; +struct _Elm_Scrollable_Smart_Interface_Data +{ + Evas_Coord x, y, w, h; + Evas_Coord wx, wy, ww, wh; /**< Last "wanted" geometry */ + + Evas_Object *obj; + Evas_Object *content; + Evas_Object *pan_obj; + Evas_Object *edje_obj; + Evas_Object *event_rect; + + Evas_Object *parent_widget; + + Elm_Scroller_Policy hbar_flags, vbar_flags; + + struct + { + Evas_Coord x, y; + Evas_Coord sx, sy; + Evas_Coord dx, dy; + Evas_Coord pdx, pdy; + Evas_Coord bx, by; + Evas_Coord ax, ay; + Evas_Coord bx0, by0; + Evas_Coord b0x, b0y; + Evas_Coord b2x, b2y; + + struct + { + Evas_Coord x, y; + double timestamp, localtimestamp; + } history[60]; + + struct + { + double tadd, dxsum, dysum; + double est_timestamp_diff; + } hist; + + double anim_start; + double anim_start2; + double anim_start3; + double onhold_vx, onhold_vy, onhold_tlast, + onhold_vxe, onhold_vye; + double extra_time; + + Evas_Coord hold_x, hold_y; + Evas_Coord locked_x, locked_y; + int hdir, vdir; + + Ecore_Animator *hold_animator; + Ecore_Animator *onhold_animator; + Ecore_Animator *momentum_animator; + Ecore_Animator *bounce_x_animator; + Ecore_Animator *bounce_y_animator; + + Eina_Bool bounce_x_hold : 1; + Eina_Bool bounce_y_hold : 1; + Eina_Bool dragged_began : 1; + Eina_Bool want_dragged : 1; + Eina_Bool hold_parent : 1; + Eina_Bool want_reset : 1; + Eina_Bool cancelled : 1; + Eina_Bool dragged : 1; + Eina_Bool locked : 1; + Eina_Bool scroll : 1; + Eina_Bool dir_x : 1; + Eina_Bool dir_y : 1; + Eina_Bool hold : 1; + Eina_Bool now : 1; + } down; + + struct + { + Evas_Coord w, h; + Eina_Bool resized : 1; + } content_info; + + struct + { + Evas_Coord x, y; + } step, page; + + struct + { + void (*drag_start)(Evas_Object *obj, + void *data); + void (*drag_stop)(Evas_Object *obj, + void *data); + void (*animate_start)(Evas_Object *obj, + void *data); + void (*animate_stop)(Evas_Object *obj, + void *data); + void (*scroll)(Evas_Object *obj, + void *data); + void (*edge_left)(Evas_Object *obj, + void *data); + void (*edge_right)(Evas_Object *obj, + void *data); + void (*edge_top)(Evas_Object *obj, + void *data); + void (*edge_bottom)(Evas_Object *obj, + void *data); + void (*content_min_limit)(Evas_Object *obj, + Eina_Bool w, + Eina_Bool h); + } cb_func; + + struct + { + struct + { + Evas_Coord start, end; + double t_start, t_end; + Ecore_Animator *animator; + } x, y; + } scrollto; + + double pagerel_h, pagerel_v; + Evas_Coord pagesize_h, pagesize_v; + + Eina_Bool momentum_animator_disabled : 1; + Eina_Bool bounce_animator_disabled : 1; + Eina_Bool one_direction_at_a_time : 1; + Eina_Bool wheel_disabled : 1; + Eina_Bool hbar_visible : 1; + Eina_Bool vbar_visible : 1; + Eina_Bool bounce_horiz : 1; + Eina_Bool bounce_vert : 1; + Eina_Bool is_mirrored : 1; + Eina_Bool extern_pan : 1; + Eina_Bool bouncemey : 1; + Eina_Bool bouncemex : 1; + Eina_Bool freeze : 1; + Eina_Bool hold : 1; +}; + +typedef struct _Elm_Scrollable_Smart_Interface Elm_Scrollable_Smart_Interface; +struct _Elm_Scrollable_Smart_Interface +{ + Evas_Smart_Interface base; + + void (*objects_set)(Evas_Object *obj, + Evas_Object *edje_obj, + Evas_Object *hit_rectangle); + void (*content_set)(Evas_Object *obj, + Evas_Object *content); + + void (*extern_pan_set)(Evas_Object *obj, + Evas_Object *pan); + + void (*drag_start_cb_set)(Evas_Object *obj, + void (*d_start_cb)(Evas_Object *obj, + void *data)); + void (*drag_stop_cb_set)(Evas_Object *obj, + void (*d_stop_cb)(Evas_Object *obj, + void *data)); + void (*animate_start_cb_set)(Evas_Object *obj, + void (*a_start_cb)(Evas_Object *obj, + void *data)); + void (*animate_stop_cb_set)(Evas_Object *obj, + void (*a_stop_cb)(Evas_Object *obj, + void *data)); + void (*scroll_cb_set)(Evas_Object *obj, + void (*s_cb)(Evas_Object *obj, + void *data)); + void (*edge_left_cb_set)(Evas_Object *obj, + void (*e_left_cb)(Evas_Object *obj, + void *data)); + void (*edge_right_cb_set)(Evas_Object *obj, + void (*e_right_cb)(Evas_Object *obj, + void *data)); + void (*edge_top_cb_set)(Evas_Object *obj, + void (*e_top_cb)(Evas_Object *obj, + void *data)); + void (*edge_bottom_cb_set)(Evas_Object *obj, + void (*e_bottom_cb)(Evas_Object *obj, + void *data)); + + void (*content_min_limit_cb_set)(Evas_Object *obj, + void (*c_limit_cb)(Evas_Object *obj, + Eina_Bool w, + Eina_Bool h)); + + /* set the position of content object inside the scrolling region, + * immediately */ + void (*content_pos_set)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y); + void (*content_pos_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + + void (*content_region_show)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h); + void (*content_region_set)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h); + + void (*content_size_get)(const Evas_Object *obj, + Evas_Coord *w, + Evas_Coord *h); + + /* get the size of the actual viewport area (swallowed into + * scroller Edje object) */ + void (*content_viewport_size_get)(const Evas_Object *obj, + Evas_Coord *w, + Evas_Coord *h); + + /* this one issues the respective callback, only */ + void (*content_min_limit)(Evas_Object *obj, + Eina_Bool w, + Eina_Bool h); + + void (*step_size_set)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y); + void (*step_size_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*page_size_set)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y); + void (*page_size_get)(const Evas_Object *obj, + Evas_Coord *x, + Evas_Coord *y); + void (*policy_set)(Evas_Object *obj, + Elm_Scroller_Policy hbar, + Elm_Scroller_Policy vbar); + void (*policy_get)(const Evas_Object *obj, + Elm_Scroller_Policy *hbar, + Elm_Scroller_Policy *vbar); + + void (*single_direction_set)(Evas_Object *obj, + Eina_Bool single_dir); + Eina_Bool (*single_direction_get)(const Evas_Object *obj); + + void (*mirrored_set)(Evas_Object *obj, + Eina_Bool mirrored); + + void (*hold_set)(Evas_Object *obj, + Eina_Bool hold); + void (*freeze_set)(Evas_Object *obj, + Eina_Bool freeze); + + void (*bounce_allow_set)(Evas_Object *obj, + Eina_Bool horiz, + Eina_Bool vert); + void (*bounce_allow_get)(const Evas_Object *obj, + Eina_Bool *horiz, + Eina_Bool *vert); + + void (*paging_set)(Evas_Object *obj, + double pagerel_h, + double pagerel_v, + Evas_Coord pagesize_h, + Evas_Coord pagesize_v); + void (*paging_get)(const Evas_Object *obj, + double *pagerel_h, + double *pagerel_v, + Evas_Coord *pagesize_h, + Evas_Coord *pagesize_v); + void (*current_page_get)(const Evas_Object *obj, + int *pagenumber_h, + int *pagenumber_v); + void (*last_page_get)(const Evas_Object *obj, + int *pagenumber_h, + int *pagenumber_v); + void (*page_show)(Evas_Object *obj, + int pagenumber_h, + int pagenumber_v); + void (*page_bring_in)(Evas_Object *obj, + int pagenumber_h, + int pagenumber_v); + + void (*region_bring_in)(Evas_Object *obj, + Evas_Coord x, + Evas_Coord y, + Evas_Coord w, + Evas_Coord h); + + void (*gravity_set)(Evas_Object *obj, + double x, + double y); + void (*gravity_get)(const Evas_Object *obj, + double *x, + double *y); + + Eina_Bool (*momentum_animator_disabled_get)(const Evas_Object *obj); + void (*momentum_animator_disabled_set)(Evas_Object *obj, + Eina_Bool disabled); + + void (*bounce_animator_disabled_set)(Evas_Object *obj, + Eina_Bool disabled); + Eina_Bool (*bounce_animator_disabled_get)(const Evas_Object *obj); + + Eina_Bool (*wheel_disabled_get)(const Evas_Object *obj); + void (*wheel_disabled_set)(Evas_Object *obj, + Eina_Bool disabled); +}; + +EAPI extern const char ELM_SCROLLABLE_IFACE_NAME[]; +EAPI extern const Elm_Scrollable_Smart_Interface ELM_SCROLLABLE_IFACE; + +EAPI const Elm_Pan_Smart_Class *elm_pan_smart_class_get(void); + +#define ELM_SCROLLABLE_IFACE_GET(obj, iface) \ + const Elm_Scrollable_Smart_Interface * iface; \ + iface = evas_object_smart_interface_get(obj, ELM_SCROLLABLE_IFACE_NAME); + +/** + * @} + */ + +#endif diff --git a/legacy/elementary/src/lib/elm_widget.c b/legacy/elementary/src/lib/elm_widget.c index ad17961682..d84748ce96 100644 --- a/legacy/elementary/src/lib/elm_widget.c +++ b/legacy/elementary/src/lib/elm_widget.c @@ -1,6 +1,7 @@ #include #include "elm_priv.h" #include "elm_widget_container.h" +#include "elm_interface_scrollable.h" static const char SMART_NAME[] = "elm_widget"; static const char SMART_NAME_COMPAT[] = "elm_widget_compat"; @@ -221,6 +222,12 @@ _elm_legacy_is(const Evas_Object *obj) return evas_object_smart_type_check_ptr(obj, SMART_NAME_COMPAT); } +static inline Eina_Bool +_elm_scrollable_is(const Evas_Object *obj) +{ + return !!evas_object_smart_interface_get(obj, ELM_SCROLLABLE_IFACE_NAME); +} + /* what follows are both basic (unimplemented) smart class functions * and proxies from those to smart data (instance) widget * functions. one by one we'll be surpassing the proxies on the @@ -3038,7 +3045,16 @@ elm_widget_scroll_hold_push(Evas_Object *obj) API_ENTRY return; sd->scroll_hold++; if (sd->scroll_hold == 1) - evas_object_smart_callback_call(obj, "scroll-hold-on", obj); + { + if (_elm_scrollable_is(obj)) + { + ELM_SCROLLABLE_IFACE_GET(obj, s_iface); + s_iface->hold_set(obj, EINA_TRUE); + } + else /* FIXME: this will vanish as soon as we don't have + * any legacy widget anymore */ + evas_object_smart_callback_call(obj, "scroll-hold-on", obj); + } if (sd->parent_obj) elm_widget_scroll_hold_push(sd->parent_obj); // FIXME: on delete/reparent hold pop } @@ -3049,7 +3065,16 @@ elm_widget_scroll_hold_pop(Evas_Object *obj) API_ENTRY return; sd->scroll_hold--; if (!sd->scroll_hold) - evas_object_smart_callback_call(obj, "scroll-hold-off", obj); + { + if (_elm_scrollable_is(obj)) + { + ELM_SCROLLABLE_IFACE_GET(obj, s_iface); + s_iface->hold_set(obj, EINA_FALSE); + } + else /* FIXME: this will vanish as soon as we don't have + * any legacy widget anymore */ + evas_object_smart_callback_call(obj, "scroll-hold-off", obj); + } if (sd->parent_obj) elm_widget_scroll_hold_pop(sd->parent_obj); if (sd->scroll_hold < 0) sd->scroll_hold = 0; } @@ -3067,7 +3092,16 @@ elm_widget_scroll_freeze_push(Evas_Object *obj) API_ENTRY return; sd->scroll_freeze++; if (sd->scroll_freeze == 1) - evas_object_smart_callback_call(obj, "scroll-freeze-on", obj); + { + if (_elm_scrollable_is(obj)) + { + ELM_SCROLLABLE_IFACE_GET(obj, s_iface); + s_iface->freeze_set(obj, EINA_TRUE); + } + else /* FIXME: this will vanish as soon as we don't have + * any legacy widget anymore */ + evas_object_smart_callback_call(obj, "scroll-freeze-on", obj); + } if (sd->parent_obj) elm_widget_scroll_freeze_push(sd->parent_obj); // FIXME: on delete/reparent freeze pop } @@ -3078,7 +3112,16 @@ elm_widget_scroll_freeze_pop(Evas_Object *obj) API_ENTRY return; sd->scroll_freeze--; if (!sd->scroll_freeze) - evas_object_smart_callback_call(obj, "scroll-freeze-off", obj); + { + if (_elm_scrollable_is(obj)) + { + ELM_SCROLLABLE_IFACE_GET(obj, s_iface); + s_iface->freeze_set(obj, EINA_FALSE); + } + else /* FIXME: this will vanish as soon as we don't have + * any legacy widget anymore */ + evas_object_smart_callback_call(obj, "scroll-freeze-off", obj); + } if (sd->parent_obj) elm_widget_scroll_freeze_pop(sd->parent_obj); if (sd->scroll_freeze < 0) sd->scroll_freeze = 0; } diff --git a/legacy/elementary/src/lib/elm_widget.h b/legacy/elementary/src/lib/elm_widget.h index c3844071f6..c4294bef0d 100644 --- a/legacy/elementary/src/lib/elm_widget.h +++ b/legacy/elementary/src/lib/elm_widget.h @@ -1064,6 +1064,10 @@ EAPI void elm_widget_tree_dot_dump(const Evas_Object *top, FILE *out #define ELM_OBJ_ITEM_CHECK_OR_GOTO(it, label) \ ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_GOTO(it, label); +/* to be used by INTERNAL classes on Elementary, so that the widgets + * parsing script skips it */ +#define ELM_INTERNAL_SMART_SUBCLASS_NEW EVAS_SMART_SUBCLASS_NEW + /** * The drag and drop API. * Currently experimental, and will change when it does dynamic type