diff --git a/src/lib/efl/interfaces/efl_ui_scrollbar.eo b/src/lib/efl/interfaces/efl_ui_scrollbar.eo index 5741df6b8f..3b4696f00b 100644 --- a/src/lib/efl/interfaces/efl_ui_scrollbar.eo +++ b/src/lib/efl/interfaces/efl_ui_scrollbar.eo @@ -1,80 +1,92 @@ import efl_ui_layout_orientable; enum Efl.Ui.Scrollbar_Mode { [[When should the scrollbar be shown. @since 1.23]] auto = 0, [[Visible if necessary.]] on, [[Always visible.]] off, [[Always invisible.]] last [[For internal use only.]] } interface Efl.Ui.Scrollbar { [[Interface used by widgets which can display scrollbars, enabling them to hold more content than actually visible through the viewport. A scrollbar contains a draggable part (thumb) which allows the user to move the viewport around the content. The size of the thumb relates to the size of the viewport compared to the whole content. @since 1.23 ]] methods { @property bar_mode { [[Scrollbar visibility mode, for each of the scrollbars.]] set { } get { } values { hbar: Efl.Ui.Scrollbar_Mode(Efl.Ui.Scrollbar_Mode.auto); [[Horizontal scrollbar mode.]] vbar: Efl.Ui.Scrollbar_Mode(Efl.Ui.Scrollbar_Mode.auto); [[Vertical scrollbar mode.]] } } @property bar_size { [[This returns the relative size the thumb should have, given the current size of the viewport and the content. $[0.0] means the viewport is much smaller than the content: the thumb will have its minimum size. $[1.0] means the viewport has the same size as the content (or bigger): the thumb will have the same size as the scrollbar and cannot move. ]] get { } values { width: double; [[Value between $[0.0] and $[1.0].]] height: double; [[Value between $[0.0] and $[1.0].]] } } @property bar_position { [[Position of the thumb (the draggable zone) inside the scrollbar. It is calculated based on current position of the viewport inside the total content. ]] set { } get { } values { posx: double; [[Value between $[0.0] (the left side of the thumb is touching the left edge of the widget) and $[1.0] (the right side of the thumb is touching the right edge of the widget).]] posy: double; [[Value between $[0.0] (the top side of the thumb is touching the top edge of the widget) and $[1.0] (the bottom side of the thumb is touching the bottom edge of the widget).]] } } + @property bar_visibility { + [[Current visibility state of the scrollbars. + This is useful in @Efl.Ui.Scrollbar_Mode.auto mode where EFL decides if the scrollbars + are shown or hidden. See also the @[.bar,show] and @[.bar,hide] events. + ]] + get { + } + values { + hbar: bool; [[Whether the horizontal scrollbar is currently visible.]] + vbar: bool; [[Whether the vertical scrollbar is currently visible.]] + } + } bar_visibility_update @protected @beta{ [[Update bar visibility. The object will call this function whenever the bar needs to be shown or hidden. ]] } } events { bar,pressed: Efl.Ui.Layout_Orientation; [[Emitted when thumb is pressed.]] bar,unpressed: Efl.Ui.Layout_Orientation; [[Emitted when thumb is unpressed.]] bar,dragged: Efl.Ui.Layout_Orientation; [[Emitted when thumb is dragged.]] bar,size,changed: void; [[Emitted when thumb size has changed.]] bar,pos,changed: void; [[Emitted when thumb position has changed.]] bar,show: Efl.Ui.Layout_Orientation; [[Emitted when scrollbar is shown.]] bar,hide: Efl.Ui.Layout_Orientation; [[Emitted when scrollbar is hidden.]] } } diff --git a/src/lib/elementary/efl_ui_scroll_manager.c b/src/lib/elementary/efl_ui_scroll_manager.c index 3d332e94fb..650a726bc1 100644 --- a/src/lib/elementary/efl_ui_scroll_manager.c +++ b/src/lib/elementary/efl_ui_scroll_manager.c @@ -1,2506 +1,2513 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_UI_SCROLL_MANAGER_PROTECTED #define EFL_UI_SCROLLBAR_PROTECTED #include #include "elm_priv.h" #include "efl_ui_widget_scroll_manager.h" #define MY_CLASS EFL_UI_SCROLL_MANAGER_CLASS #define MY_CLASS_NAME "Efl.Ui.Scroll.Manager" #define ELM_ANIMATOR_CONNECT(Obj, Bool, Callback, Data) \ efl_event_callback_del(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ efl_event_callback_add(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ Bool = 1; #define ELM_ANIMATOR_DISCONNECT(Obj, Bool, Callback, Data) \ efl_event_callback_del(Obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, Callback, Data); \ Bool = 0; static double _scroll_manager_linear_interp(void *data EINA_UNUSED, double progress) { return progress; } static double _scroll_manager_accel_interp(void *data EINA_UNUSED, double progress) { return progress * progress; } static double _scroll_manager_decel_interp(void *data EINA_UNUSED, double progress) { return (1.0 - (1.0 - progress) * (1.0 - progress)); } static Interpolator _scroll_manager_interp_get(InterpType interp) { if (interp == INTERP_ACCEL) return _scroll_manager_accel_interp; else if (interp == INTERP_DECEL) return _scroll_manager_decel_interp; return _scroll_manager_linear_interp; } // Prototypes --- // // ANIMATORS - tick function static void _efl_ui_scroll_manager_hold_animator(void *data, const Efl_Event *event); static void _efl_ui_scroll_manager_on_hold_animator(void *data, const Efl_Event *event); static void _efl_ui_scroll_manager_scroll_to_y_animator(void *data, const Efl_Event *event); static void _efl_ui_scroll_manager_scroll_to_x_animator(void *data, const Efl_Event *event); static void _efl_ui_scroll_manager_bounce_y_animator(void *data, const Efl_Event *event); static void _efl_ui_scroll_manager_bounce_x_animator(void *data, const Efl_Event *event); // ANIMATORS - manipulate function static void _scroll_manager_hold_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x, Evas_Coord y); static Eina_Bool _scroll_manager_hold_animator_del(Efl_Ui_Scroll_Manager_Data *sd); static void _scroll_manager_on_hold_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx, double vy); static Eina_Bool _scroll_manager_on_hold_animator_del(Efl_Ui_Scroll_Manager_Data *sd); /// Constant scrolling static void _scroll_manager_scrollto_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord cx, Evas_Coord cy, Evas_Coord x, Evas_Coord y, double tx, double ty, InterpType interp); static Eina_Bool _scroll_manager_scrollto_animator_del(Efl_Ui_Scroll_Manager_Data *sd); static void _scroll_manager_scrollto_x_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord cx, Evas_Coord x, double t, InterpType interp); static Eina_Bool _scroll_manager_scrollto_x_animator_del(Efl_Ui_Scroll_Manager_Data *sd); static void _scroll_manager_scrollto_y_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord cy, Evas_Coord y, double t, InterpType interp); static Eina_Bool _scroll_manager_scrollto_y_animator_del(Efl_Ui_Scroll_Manager_Data *sd); /// Flicking static void _scroll_manager_momentum_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx, double vy); // Bounce static void _scroll_manager_bounce_x_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx); static Eina_Bool _scroll_manager_bounce_x_animator_del(Efl_Ui_Scroll_Manager_Data *sd); static void _scroll_manager_bounce_y_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vy); static Eina_Bool _scroll_manager_bounce_y_animator_del(Efl_Ui_Scroll_Manager_Data *sd); // Util static void _scroll_manager_scrollto(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x, Evas_Coord y); static void _scroll_manager_animators_drop(Evas_Object *obj); // ETC static void _efl_ui_scroll_manager_wanted_coordinates_update(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x,Evas_Coord y); // --- Prototypes // static inline double _round(double value, int pos) { double temp; temp = value * pow( 10, pos ); temp = floor( temp + 0.5 ); temp *= pow( 10, -pos ); return temp; } #define EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(o, ptr) \ Efl_Ui_Scroll_Manager_Data *ptr = \ (!efl_isa(o, MY_CLASS) ? NULL : \ efl_data_scope_safe_get(o, MY_CLASS)); \ if (!ptr) \ { \ CRI("No interface data for object %p (%s)", \ o, evas_object_type_get(o)); \ return; \ } #define EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ Efl_Ui_Scroll_Manager_Data *ptr = \ (!efl_isa(o, MY_CLASS) ? NULL : \ efl_data_scope_safe_get(o, MY_CLASS)); \ if (!ptr) \ { \ CRI("No interface data for object %p (%s)", \ o, evas_object_type_get(o)); \ return val; \ } static void _efl_ui_scroll_manager_wanted_region_set(Evas_Object *obj); #define LEFT 0 #define RIGHT 1 #define UP 2 #define DOWN 3 //#define SCROLLDBG 1 /* smoothness debug calls - for debugging how much smooth your app is */ static inline Eina_Bool _scroll_manager_thumb_scrollable_get(Efl_Ui_Scroll_Manager_Data *sd) { if (!sd) return EINA_FALSE; if ((sd->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) && (sd->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) return EINA_FALSE; if (!_elm_config->thumbscroll_enable) return EINA_FALSE; return EINA_TRUE; } static inline Eina_Bool _scroll_manager_animating_get(Efl_Ui_Scroll_Manager_Data *sd) { if (!sd) return EINA_FALSE; return ((sd->bounce.x.animator) || (sd->bounce.y.animator) || (sd->scrollto.x.animator) || (sd->scrollto.y.animator)); } static void _efl_ui_scroll_manager_scroll_start(Efl_Ui_Scroll_Manager_Data *sd) { sd->scrolling = EINA_TRUE; efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_STARTED, NULL); } static void _efl_ui_scroll_manager_scroll_stop(Efl_Ui_Scroll_Manager_Data *sd) { sd->scrolling = EINA_FALSE; efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_FINISHED, NULL); } static void _efl_ui_scroll_manager_drag_start(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_DRAG_STARTED, NULL); if (!sd->scrolling) _efl_ui_scroll_manager_scroll_start(sd); } static void _efl_ui_scroll_manager_drag_stop(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_DRAG_FINISHED, NULL); } static void _efl_ui_scroll_manager_anim_start(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_ANIM_STARTED, NULL); if (!sd->scrolling) _efl_ui_scroll_manager_scroll_start(sd); } static void _efl_ui_scroll_manager_anim_stop(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_ANIM_FINISHED, NULL); if (sd->scrolling) _efl_ui_scroll_manager_scroll_stop(sd); } static void _efl_ui_scroll_manager_scroll(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_CHANGED, NULL); } static void _efl_ui_scroll_manager_scroll_up(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_UP, NULL); } static void _efl_ui_scroll_manager_scroll_down(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_DOWN, NULL); } static void _efl_ui_scroll_manager_scroll_left(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_LEFT, NULL); } static void _efl_ui_scroll_manager_scroll_right(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_SCROLL_RIGHT, NULL); } static void _efl_ui_scroll_manager_edge_up(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_EDGE_UP, NULL); } static void _efl_ui_scroll_manager_edge_down(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_EDGE_DOWN, NULL); } static void _efl_ui_scroll_manager_edge_left(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_EDGE_LEFT, NULL); } static void _efl_ui_scroll_manager_edge_right(Efl_Ui_Scroll_Manager_Data *sd) { efl_event_callback_call(sd->parent, EFL_UI_EVENT_EDGE_RIGHT, NULL); } EOLIAN static Eina_Size2D _efl_ui_scroll_manager_efl_ui_scrollable_content_size_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { return efl_ui_pan_content_size_get(sd->pan_obj); } EOLIAN static Eina_Rect _efl_ui_scroll_manager_efl_ui_scrollable_viewport_geometry_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { if (!sd->pan_obj) return EINA_RECT(0, 0, 0, 0); return efl_gfx_entity_geometry_get(sd->pan_obj); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_match_content_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool w, Eina_Bool h) { sd->match_content_w = !!w; sd->match_content_h = !!h; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_step_size_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Position2D step) { sd->step.x = step.x * elm_config_scale_get(); sd->step.y = step.y * elm_config_scale_get(); } EOLIAN static Eina_Position2D _efl_ui_scroll_manager_efl_ui_scrollable_step_size_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { return EINA_POSITION2D(sd->step.x, sd->step.y); } static Evas_Coord _efl_ui_scroll_manager_x_mirrored_get(const Evas_Object *obj, Evas_Coord x) { Evas_Coord ret; Eina_Position2D min = {0, 0}, max = {0, 0}; EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN_VAL(obj, sd, x); if (!sd->pan_obj) return 0; min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); ret = max.x - (x - min.x); return (ret >= min.x) ? ret : min.x; } /* Update the wanted coordinates according to the x, y passed * widget directionality, content size and etc. */ static void _efl_ui_scroll_manager_wanted_coordinates_update(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x, Evas_Coord y) { Eina_Position2D min = {0, 0}, max = {0, 0}; if (!sd->pan_obj) return; min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); /* Update wx/y/w/h - and if the requested positions aren't legal * adjust a bit. */ Eina_Rect r = efl_ui_scrollable_viewport_geometry_get(sd->obj); sd->ww = r.w; sd->wh = r.h; if (x < min.x && !sd->is_mirrored) { if (!sd->loop_h) sd->wx = min.x; else sd->wx = max.x; } else if (sd->is_mirrored) sd->wx = _efl_ui_scroll_manager_x_mirrored_get(sd->obj, x); else if (!sd->loop_h && (x > max.x)) sd->wx = max.x; else if (sd->loop_h && x >= (sd->ww + max.x)) sd->wx = min.x; else sd->wx = x; if (y < min.y) { if (!sd->loop_v) sd->wy = min.y; else sd->wy = max.y; } else if (!sd->loop_v && (y > max.y)) sd->wy = max.y; else if (sd->loop_v && y >= (sd->wh + max.y)) sd->wy = min.y; else sd->wy = y; } static void _scroll_manager_animator_velocity_get(Efl_Ui_Scroll_Manager_Data *sd, double *velx, double *vely) { Evas_Coord dx = 0, dy = 0; double vx = 0.0, vy = 0.0; double t = ecore_loop_time_get(); Eina_Position2D cur = efl_ui_pan_position_get(sd->pan_obj); if (t < sd->scrollto.x.start_t + sd->scrollto.x.dur) { dx = sd->scrollto.x.end - cur.x; vx = (double)(dx /((sd->scrollto.x.start_t + sd->scrollto.x.dur) - t)); if (sd->scrollto.x.interp) vx = sd->scrollto.x.interp(NULL, t/(sd->scrollto.x.start_t + sd->scrollto.x.dur)) * vx; } if (t < sd->scrollto.y.start_t + sd->scrollto.y.dur) { dy = sd->scrollto.y.end - cur.y; vy = (double)(dy /((sd->scrollto.y.start_t + sd->scrollto.y.dur) - t)); if (sd->scrollto.y.interp) vy = sd->scrollto.y.interp(NULL, t/(sd->scrollto.y.start_t + sd->scrollto.y.dur)) * vy; } if (velx) *velx = vx; if (vely) *vely = vy; } static void _efl_ui_scroll_manager_bounce_eval(Efl_Ui_Scroll_Manager_Data *sd) { double vx = 0.0, vy = 0.0; if (!sd->pan_obj) return; if (sd->freeze) return; if ((!sd->bouncemex) && (!sd->bouncemey)) return; if (sd->down.now) return; // down bounce while still held down _scroll_manager_on_hold_animator_del(sd); _scroll_manager_hold_animator_del(sd); _scroll_manager_animator_velocity_get(sd, &vx, &vy); if (!sd->bounce.x.animator) { if (sd->bouncemex) { _scroll_manager_scrollto_x_animator_del(sd); _scroll_manager_bounce_x_animator_add(sd,vx); } } if (!sd->bounce.y.animator) { if (sd->bouncemey) { _scroll_manager_scrollto_y_animator_del(sd); _scroll_manager_bounce_y_animator_add(sd,vy); } } } EOLIAN static Eina_Position2D _efl_ui_scroll_manager_efl_ui_scrollable_content_pos_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { if (!sd->pan_obj) return EINA_POSITION2D(0, 0); return efl_ui_pan_position_get(sd->pan_obj); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_content_pos_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Position2D pos) { Evas_Coord x = pos.x, y = pos.y; Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; Eina_Size2D content = {0, 0}; if (!sd->pan_obj) return; // FIXME: allow for bounce outsde of range max = efl_ui_pan_position_max_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); content = efl_ui_pan_content_size_get(sd->pan_obj); cur = efl_ui_pan_position_get(sd->pan_obj); if (sd->loop_h && content.w > 0) { if (x < 0) x = content.w + (x % content.w); else if (x >= content.w) x = (x % content.w); } if (sd->loop_v && content.h > 0) { if (y < 0) y = content.h + (y % content.h); else if (y >= content.h) y = (y % content.h); } if (!_elm_config->thumbscroll_bounce_enable) { if (x < min.x) x = min.x; if (!sd->loop_h && (x - min.x) > max.x) x = max.x + min.x; if (y < min.y) y = min.y; if (!sd->loop_v && (y - min.y) > max.y) y = max.y + min.y; } if (!sd->bounce_horiz) { if (x < min.x) x = min.x; if (!sd->loop_h && (x - min.x) > max.x) x = max.x + min.x; } if (!sd->bounce_vert) { if (y < min.y) y = min.y; if (!sd->loop_v && (y - min.y) > max.y) y = max.y + min.y; } efl_ui_pan_position_set(sd->pan_obj, EINA_POSITION2D(x, y)); if (!sd->loop_h && !sd->bounce.x.animator) { if ((x < min.x) ||(x > max.x + min.x)) { sd->bouncemex = EINA_TRUE; _efl_ui_scroll_manager_bounce_eval(sd); } else sd->bouncemex = EINA_FALSE; } if (!sd->loop_v && !sd->bounce.y.animator) { if ((y < min.y) ||(y > max.y + min.y)) { sd->bouncemey = EINA_TRUE; _efl_ui_scroll_manager_bounce_eval(sd); } else sd->bouncemey = EINA_FALSE; } { if ((x != cur.x) || (y != cur.y)) { _efl_ui_scroll_manager_scroll(sd); if (x < cur.x) { _efl_ui_scroll_manager_scroll_left(sd); } if (x > cur.x) { _efl_ui_scroll_manager_scroll_right(sd); } if (y < cur.y) { _efl_ui_scroll_manager_scroll_up(sd); } if (y > cur.y) { _efl_ui_scroll_manager_scroll_down(sd); } } if (x != cur.x) { if (x == min.x) { _efl_ui_scroll_manager_edge_left(sd); } if (x == (max.x + min.x)) { _efl_ui_scroll_manager_edge_right(sd); } } if (y != cur.y) { if (y == min.y) { _efl_ui_scroll_manager_edge_up(sd); } if (y == max.y + min.y) { _efl_ui_scroll_manager_edge_down(sd); } } } } EOLIAN static void _efl_ui_scroll_manager_efl_ui_i18n_mirrored_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool mirrored) { Evas_Coord wx; mirrored = !!mirrored; if (sd->is_mirrored == mirrored) return; sd->is_mirrored = mirrored; if (sd->is_mirrored) wx = _efl_ui_scroll_manager_x_mirrored_get(sd->obj, sd->wx); else wx = sd->wx; efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(wx, sd->wy)); } static void _scroll_manager_animators_drop(Evas_Object *obj) { EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); if ((sd->bounce.x.animator) || (sd->bounce.y.animator) || (sd->scrollto.x.animator) || (sd->scrollto.y.animator)) { if (_scroll_manager_scrollto_x_animator_del(sd)) { } if (_scroll_manager_scrollto_y_animator_del(sd)) { } if (_scroll_manager_bounce_x_animator_del(sd)) { sd->bouncemex = EINA_FALSE; if (sd->content_info.resized) _efl_ui_scroll_manager_wanted_region_set(sd->obj); } if (_scroll_manager_bounce_y_animator_del(sd)) { sd->bouncemey = EINA_FALSE; if (sd->content_info.resized) _efl_ui_scroll_manager_wanted_region_set(sd->obj); } _efl_ui_scroll_manager_anim_stop(sd); } if (_scroll_manager_hold_animator_del(sd)) { _efl_ui_scroll_manager_drag_stop(sd); if (sd->content_info.resized) _efl_ui_scroll_manager_wanted_region_set(sd->obj); } } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Efl_Ui_Scrollbar_Mode hmode, Efl_Ui_Scrollbar_Mode vmode) { sd->hbar_mode = hmode; sd->vbar_mode = vmode; if (sd->hbar_timer && hmode == EFL_UI_SCROLLBAR_MODE_ON) ELM_SAFE_FREE(sd->hbar_timer, ecore_timer_del); if (sd->vbar_timer && vmode == EFL_UI_SCROLLBAR_MODE_ON) ELM_SAFE_FREE(sd->vbar_timer, ecore_timer_del); efl_ui_scrollbar_bar_visibility_update(sd->obj); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Efl_Ui_Scrollbar_Mode *hmode, Efl_Ui_Scrollbar_Mode *vmode) { if (hmode) *hmode = sd->hbar_mode; if (vmode) *vmode = sd->vbar_mode; } /* 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 _efl_ui_scroll_manager_content_region_show_internal(Evas_Object *obj, Evas_Coord *_x, Evas_Coord *_y, Evas_Coord w, Evas_Coord h) { Evas_Coord nx, ny, x = *_x, y = *_y; Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; Eina_Size2D pan = {0, 0}; EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN_VAL(obj, sd, EINA_FALSE); if (!sd->pan_obj) return EINA_FALSE; min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); cur = efl_ui_pan_position_get(sd->pan_obj); pan = efl_gfx_entity_size_get(sd->pan_obj); nx = x; if ((x > cur.x) && (w < pan.w)) { if ((cur.x + pan.w) < (x + w)) nx = x - pan.w + w; else nx = cur.x; } ny = y; if ((y > cur.y) && (h < pan.h)) { if ((cur.y + pan.h) < (y + h)) ny = y - pan.h + h; else ny = cur.y; } x = nx; y = ny; if (!sd->loop_h) { if (x > max.x) x = max.x; if (x < min.x) x = min.x; } if (!sd->loop_v) { if (y > max.y) y = max.y; if (y < min.y) y = min.y; } if ((x == cur.x) && (y == cur.y)) return EINA_FALSE; *_x = x; *_y = y; return EINA_TRUE; } static void _efl_ui_scroll_manager_content_region_set(Eo *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); _scroll_manager_animators_drop(obj); if (_efl_ui_scroll_manager_content_region_show_internal(obj, &x, &y, w, h)) { efl_ui_scrollable_content_pos_set(obj, EINA_POSITION2D(x, y)); sd->down.sx = x; sd->down.sy = y; sd->down.x = sd->down.history[0].x; sd->down.y = sd->down.history[0].y; } } static void _efl_ui_scroll_manager_wanted_region_set(Evas_Object *obj) { Evas_Coord ww, wh, wx; Eina_Position2D max = {0, 0}; EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); wx = sd->wx; if (_scroll_manager_animating_get(sd) || sd->down.now || sd->down.hold_animator || sd->down.onhold_animator) return; sd->content_info.resized = EINA_FALSE; /* Flip to RTL cords only if init in RTL mode */ if (sd->is_mirrored) wx = _efl_ui_scroll_manager_x_mirrored_get(obj, sd->wx); if (sd->ww == -1) { Eina_Rect r = efl_ui_scrollable_viewport_geometry_get(sd->obj); ww = r.w; wh = r.h; } else { ww = sd->ww; wh = sd->wh; } max = efl_ui_pan_position_max_get(sd->pan_obj); wx += (max.x - sd->prev_cw) * sd->gravity_x; sd->wy += (max.y - sd->prev_ch) * sd->gravity_y; sd->prev_cw = max.x; sd->prev_ch = max.y; _efl_ui_scroll_manager_content_region_set(obj, wx, sd->wy, ww, wh); } static Eina_Value _scroll_wheel_post_event_job(void *data, const Eina_Value v, const Eina_Future *ev EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; // Animations are disabled if we are here efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(sd->wx, sd->wy)); return v; } static inline void _scroll_wheel_post_event_go(Efl_Ui_Scroll_Manager_Data *sd, int x, int y) { Eina_Position2D cur; if (sd->hold || sd->freeze) return; _efl_ui_scroll_manager_wanted_coordinates_update(sd, x, y); if (_elm_config->scroll_animation_disable) { Eina_Future *f; f = eina_future_then(efl_loop_job(efl_loop_get(sd->obj)), _scroll_wheel_post_event_job, sd, NULL); efl_future_then(sd->obj, f); } else { cur = efl_ui_pan_position_get(sd->pan_obj); _scroll_manager_scrollto_animator_add(sd, cur.x, cur.y, x, y, _elm_config->bring_in_scroll_friction, _elm_config->bring_in_scroll_friction, INTERP_DECEL); } } static Eina_Bool _scroll_wheel_post_event_cb(void *data, Evas *e EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Event_Mouse_Wheel *ev = sd->event_info; Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; Eina_Size2D content = {0, 0}; Evas_Coord x = 0, y = 0, vw = 0, vh = 0; Eina_Bool hold = EINA_FALSE; Evas_Coord pwx, pwy; double t; int direction; EINA_SAFETY_ON_NULL_RETURN_VAL(ev, EINA_TRUE); sd->event_info = NULL; direction = ev->direction; pwx = sd->wx; pwy = sd->wy; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE; if (evas_key_modifier_is_set(ev->modifiers, "Shift")) direction = !direction; cur = efl_ui_pan_position_get(sd->pan_obj); x = cur.x; y = cur.y; if (sd->scrollto.x.animator) { if (((ev->z > 0) && (sd->scrollto.x.end > x)) || ((ev->z < 0) && (sd->scrollto.x.end < x))) x = sd->scrollto.x.end; } if (sd->scrollto.y.animator) { if (((ev->z > 0) && (sd->scrollto.y.end > y)) || ((ev->z < 0) && (sd->scrollto.y.end < y))) y = sd->scrollto.y.end; } max = efl_ui_pan_position_max_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); if (x < min.x) x = min.x; if (x > max.x) x = max.x; if (y < min.y) y = min.y; if (y > max.y) y = max.y; t = ecore_loop_time_get(); _scroll_manager_animators_drop(sd->obj); Eina_Rect r = efl_ui_scrollable_viewport_geometry_get(sd->obj); vw = r.w; vh = r.h; if (sd->pan_obj) content = efl_ui_pan_content_size_get(sd->pan_obj); int d = ev->z; double delta_t = (double)(ev->timestamp - sd->last_wheel) / 1000.0; double mul; if (delta_t > 0.2) sd->last_wheel_mul = 0.0; if (delta_t > 0.2) delta_t = 0.2; mul = 1.0 + (_elm_config->scroll_accel_factor * ((0.2 - delta_t) / 0.2)); mul = mul * (1.0 + (0.15 * sd->last_wheel_mul)); d *= mul; sd->last_wheel = ev->timestamp; sd->last_wheel_mul = mul; if (!direction) { if ((content.h > vh) || (content.w <= vw)) y += d * sd->step.y; else { x += d * sd->step.x; direction = 1; } } else { if ((content.w > vw) || (content.h <= vh)) x += d * sd->step.x; else { y += d * sd->step.y; direction = 0; } } _scroll_wheel_post_event_go(sd, x, y); if (direction) { if ((sd->bounce_horiz) || (pwx != sd->wx) || (((t - sd->down.last_time_x_wheel) < 0.5) && (sd->down.last_hold_x_wheel))) { sd->down.last_hold_x_wheel = EINA_TRUE; hold = EINA_TRUE; } else sd->down.last_hold_x_wheel = EINA_FALSE; sd->down.last_time_x_wheel = t; } else { if ((sd->bounce_vert) || (pwy != sd->wy) || (((t - sd->down.last_time_y_wheel) < 0.5) && (sd->down.last_hold_y_wheel))) { sd->down.last_hold_y_wheel = EINA_TRUE; hold = EINA_TRUE; } else sd->down.last_hold_y_wheel = EINA_FALSE; sd->down.last_time_y_wheel = t; } return !hold; } static void _efl_ui_scroll_manager_wheel_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Efl_Ui_Scroll_Manager_Data *sd; Evas_Event_Mouse_Wheel *ev; int direction; sd = data; ev = event_info; sd->event_info = 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; if (direction) { if (sd->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL) return; } else { if (sd->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL) return; } evas_post_event_callback_push(e, _scroll_wheel_post_event_cb, sd); } static void _efl_ui_scroll_manager_scroll_to_x_animator(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Eina_Position2D min = {0, 0}, max = {0, 0}; Evas_Coord nx = 0; double t = 0.0, dt = 0.0, progx = 0.0, rx = 0.0; Interpolator interp = NULL; Eina_Bool no_bounce_x_end = EINA_FALSE; t = ecore_loop_time_get(); dt = t - sd->scrollto.x.start_t; if ( dt > sd->scrollto.x.dur) progx = 1.0; else progx = dt / sd->scrollto.x.dur; if (sd->scrollto.x.interp) interp = sd->scrollto.x.interp; else interp = _scroll_manager_interp_get(INTERP_LINEAR); rx = interp(NULL, progx); nx = sd->scrollto.x.start + (sd->scrollto.x.end - sd->scrollto.x.start) * rx; Eina_Position2D cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(nx, cur.y)); _efl_ui_scroll_manager_wanted_coordinates_update(sd, nx, cur.y); min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); if (!_elm_config->thumbscroll_bounce_enable || !sd->bounce_horiz) { if (nx < min.x) no_bounce_x_end = EINA_TRUE; if (!sd->loop_h && (nx - min.x) > max.x) no_bounce_x_end = EINA_TRUE; } if (dt >= sd->scrollto.x.dur || no_bounce_x_end) { if ((!sd->scrollto.y.animator) && (!sd->bounce.y.animator)) _efl_ui_scroll_manager_anim_stop(sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.x.animator, _efl_ui_scroll_manager_scroll_to_x_animator, sd); } } static void _efl_ui_scroll_manager_scroll_to_y_animator(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Eina_Position2D min = {0, 0}, max = {0, 0}; Evas_Coord ny = 0; double t = 0.0, dt = 0.0, progy = 0.0, ry = 0.0; Interpolator interp = NULL; Eina_Bool no_bounce_y_end = EINA_FALSE; t = ecore_loop_time_get(); dt = t - sd->scrollto.y.start_t; if ( dt > sd->scrollto.y.dur) progy = 1.0; else progy = dt / sd->scrollto.y.dur; if (sd->scrollto.y.interp) interp = sd->scrollto.y.interp; else interp = _scroll_manager_interp_get(INTERP_LINEAR); ry = interp(NULL, progy); ny = sd->scrollto.y.start + (sd->scrollto.y.end - sd->scrollto.y.start) * ry; Eina_Position2D cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(cur.x, ny)); _efl_ui_scroll_manager_wanted_coordinates_update(sd, cur.x, ny); min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); if (!_elm_config->thumbscroll_bounce_enable || !sd->bounce_vert) { if (ny < min.y) no_bounce_y_end = EINA_TRUE; if (!sd->loop_v && (ny - min.y) > max.y) no_bounce_y_end = EINA_TRUE; } if (dt >= sd->scrollto.y.dur || no_bounce_y_end) { if ((!sd->scrollto.x.animator) && (!sd->bounce.x.animator)) _efl_ui_scroll_manager_anim_stop(sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.y.animator, _efl_ui_scroll_manager_scroll_to_y_animator, sd); } } static void _efl_ui_scroll_manager_mouse_up_event_smooth(Efl_Ui_Scroll_Manager_Data *sd, double t, Evas_Coord *ox, Evas_Coord *oy, double *ot) { static const unsigned int HISTORY_MAX = 60; unsigned int i = 0; double dt = 0, at = 0; Evas_Coord ax = 0, ay = 0; for (i = 0; i < HISTORY_MAX; i++) { dt = t - sd->down.history[i].timestamp; if (dt > 0.2) break; #ifdef SCROLLDBG DBG("H: %i %i @ %1.3f\n", sd->down.history[i].x, sd->down.history[i].y, dt); #endif ax = sd->down.history[i].x; ay = sd->down.history[i].y; at = sd->down.history[i].timestamp; } if (ox) *ox = ax; if (oy) *oy = ay; if (ot) *ot = t - at; } static void _efl_ui_scroll_manager_mouse_up_event_momentum_eval(Efl_Ui_Scroll_Manager_Data *sd, Evas_Event_Mouse_Up *ev) { double t, at; Evas_Coord dx, dy, ax, ay, vel; signed char sdx, sdy; t = ev->timestamp / 1000.0; 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 _efl_ui_scroll_manager_mouse_up_event_smooth(sd, t, &ax, &ay, &at); dx = ev->canvas.x - ax; dy = ev->canvas.y - ay; sdx = (dx > 0) - (dx < 0); sdy = (dy > 0) - (dy < 0); dx = abs(dx); dy = abs(dy); if (at > 0) { vel = (Evas_Coord)(sqrt((dx * dx) + (dy * dy)) / at); if ((_elm_config->thumbscroll_friction > 0.0) && (vel > _elm_config->thumbscroll_momentum_threshold)) { _scroll_manager_momentum_animator_add(sd, -sdx*dx/at, -sdy*dy/at); } else if (!sd->bouncemex && !sd->bouncemey) { _efl_ui_scroll_manager_scroll_stop(sd); } } } static void _efl_ui_scroll_manager_mouse_up_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Event_Mouse_Up *ev; if (!sd->pan_obj) return; if (!_scroll_manager_thumb_scrollable_get(sd)) return; ev = event_info; if (ev->button == 1) { _scroll_manager_on_hold_animator_del(sd); if (sd->down.dragged) { _efl_ui_scroll_manager_drag_stop(sd); if ((!sd->hold) && (!sd->freeze)) { _efl_ui_scroll_manager_mouse_up_event_momentum_eval(sd, ev); } evas_event_feed_hold(e, 0, ev->timestamp, ev->data); } _scroll_manager_hold_animator_del(sd); if (sd->down.scroll) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL; sd->down.scroll = EINA_FALSE; } if (sd->down.hold) { ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; sd->down.hold = EINA_FALSE; } sd->down.dragged_began = EINA_FALSE; sd->down.dir_x = EINA_FALSE; sd->down.dir_y = EINA_FALSE; sd->down.dragged = EINA_FALSE; sd->down.now = EINA_FALSE; Eina_Position2D cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, cur); _efl_ui_scroll_manager_wanted_coordinates_update(sd, cur.x, cur.y); if (sd->content_info.resized) _efl_ui_scroll_manager_wanted_region_set(sd->obj); } } static void _efl_ui_scroll_manager_mouse_down_event_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Efl_Ui_Scroll_Manager_Data *sd; Evas_Event_Mouse_Down *ev; Eina_Position2D cur = {0, 0}; sd = data; ev = event_info; if (!_scroll_manager_thumb_scrollable_get(sd)) return; sd->down.hold = EINA_FALSE; if (_scroll_manager_animating_get(sd)) { ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL | EVAS_EVENT_FLAG_ON_HOLD; sd->down.scroll = EINA_TRUE; sd->down.hold = EINA_TRUE; _scroll_manager_animators_drop(sd->obj); } if (ev->button == 1) { sd->down.est_timestamp_diff = ecore_loop_time_get() - ((double)ev->timestamp / 1000.0); sd->down.now = EINA_TRUE; sd->down.dragged = EINA_FALSE; sd->down.dir_x = EINA_FALSE; sd->down.dir_y = EINA_FALSE; sd->down.x = ev->canvas.x; sd->down.y = ev->canvas.y; cur = efl_ui_scrollable_content_pos_get(sd->obj); sd->down.sx = cur.x; sd->down.sy = cur.y; memset(&(sd->down.history[0]), 0, sizeof(sd->down.history[0]) * 60); sd->down.history[0].timestamp = ev->timestamp / 1000.0; sd->down.dragged_began_timestamp = sd->down.history[0].timestamp; sd->down.history[0].x = ev->canvas.x; sd->down.history[0].y = ev->canvas.y; } sd->down.dragged_began = EINA_FALSE; if (sd->hold || sd->freeze) sd->down.want_reset = EINA_TRUE; else sd->down.want_reset = EINA_FALSE; } static Eina_Bool _efl_ui_scroll_manager_can_scroll(Efl_Ui_Scroll_Manager_Data *sd, int dir) { Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; if (!sd->pan_obj) return EINA_FALSE; max = efl_ui_pan_position_max_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); cur = efl_ui_pan_position_get(sd->pan_obj); switch (dir) { case LEFT: if (cur.x > min.x) return EINA_TRUE; break; case RIGHT: if ((cur.x - min.x) < max.x) return EINA_TRUE; break; case UP: if (cur.y > min.y) return EINA_TRUE; break; case DOWN: if ((cur.y - min.y) < max.y) return EINA_TRUE; break; default: break; } return EINA_FALSE; } static void _efl_ui_scroll_manager_bounce_weight_apply(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord *x, Evas_Coord *y) { Eina_Position2D min = {0, 0}, max = {0, 0}; min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); if (!sd->loop_h && *x < min.x) *x += (min.x - *x) * _elm_config->thumbscroll_border_friction; else if (!sd->loop_h && max.x <= 0) *x += (sd->down.sx - *x) * _elm_config->thumbscroll_border_friction; else if (!sd->loop_h && (max.x + min.x) < *x) *x += (max.x + min.x - *x) * _elm_config->thumbscroll_border_friction; if (!sd->loop_v && *y < min.y) *y += (min.y - *y) * _elm_config->thumbscroll_border_friction; else if (!sd->loop_v && max.y <= 0) *y += (sd->down.sy - *y) * _elm_config->thumbscroll_border_friction; else if (!sd->loop_v && (max.y + min.y) < *y) *y += (max.y + min.y - *y) * _elm_config->thumbscroll_border_friction; } static inline double _scroll_manager_animation_duration_get(Evas_Coord dx, Evas_Coord dy) { double dist = 0.0, vel = 0.0, dur = 0.0; uint64_t x = abs(dx), y = abs(dy); dist = sqrt(x * x + y * y); vel = _elm_config->thumbscroll_friction_standard / _elm_config->thumbscroll_friction; dur = dist / vel; dur = (dur > _elm_config->thumbscroll_friction) ? _elm_config->thumbscroll_friction : dur; return dur; } static Eina_Bool _scroll_manager_on_hold_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->down.onhold_animator) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->down.onhold_animator, _efl_ui_scroll_manager_on_hold_animator, sd); if (sd->content_info.resized) _efl_ui_scroll_manager_wanted_region_set(sd->obj); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_on_hold_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx, double vy) { sd->down.onhold_vx = vx; sd->down.onhold_vy = vy; if (!sd->down.onhold_animator) { sd->down.onhold_vxe = 0.0; sd->down.onhold_vye = 0.0; sd->down.onhold_tlast = 0.0; ELM_ANIMATOR_CONNECT(sd->event_rect, sd->down.onhold_animator, _efl_ui_scroll_manager_on_hold_animator, sd); } } static void _scroll_manager_hold_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x, Evas_Coord y) { sd->down.hold_x = x; sd->down.hold_y = y; ELM_ANIMATOR_CONNECT(sd->event_rect, sd->down.hold_animator, _efl_ui_scroll_manager_hold_animator, sd); } static Eina_Bool _scroll_manager_hold_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->down.hold_animator || sd->down.hold_enterer) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->down.hold_animator, _efl_ui_scroll_manager_hold_animator, sd); ELM_SAFE_FREE(sd->down.hold_enterer, ecore_idle_enterer_del); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_momentum_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx, double vy) { #define FRICTION 5000 #define INVERSE_MASS 1 #define ACCEL (FRICTION * INVERSE_MASS) double dur = 0.0; signed char sdx = 0, sdy = 0; Evas_Coord dstx = 0, dsty = 0; /* if (_scroll_manager_scrollto_animator_del(sd)) { restore current veolocity add to vx/vy } */ Eina_Position2D cur = efl_ui_pan_position_get(sd->pan_obj); sdx = (vx > 0) - (vx < 0); sdy = (vy > 0) - (vy < 0); dstx = cur.x + ((sdx * vx * vx) / (double)(2 * ACCEL)); dsty = cur.y + ((sdy * vy * vy) / (double)(2 * ACCEL)); dur = sqrt((vx * vx) + (vy * vy)) / (double)ACCEL; _scroll_manager_scrollto_animator_add(sd, cur.x, cur.y, dstx, dsty, dur, dur, INTERP_DECEL); } static void _efl_ui_scroll_manager_bounce_y_animator(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Coord ny = 0; Eina_Position2D cur = {0, 0}; double t = 0.0, dt = 0.0, r = 0.0; t = ecore_loop_time_get(); if (sd->bounce.y.start_t + sd->bounce.y.t01 >= t) { dt = sd->bounce.y.start_t + sd->bounce.y.t01 - t; r = 1.0 - (dt / sd->bounce.y.t01); r = _scroll_manager_decel_interp(NULL, r); ny = sd->bounce.y.p0 + (sd->bounce.y.p1 - sd->bounce.y.p0) * r; cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(cur.x, ny)); } else if (sd->bounce.y.start_t + sd->bounce.y.t01 + sd->bounce.y.t12 >= t) { dt = sd->bounce.y.start_t + sd->bounce.y.t01 + sd->bounce.y.t12 - t; r = 1.0 - (dt / sd->bounce.y.t12); r = _scroll_manager_decel_interp(NULL, r); ny = sd->bounce.y.p1 + (sd->bounce.y.p2 - sd->bounce.y.p1) * r; cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(cur.x, ny)); } else { cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(cur.x, sd->bounce.y.p2)); if ((!sd->scrollto.x.animator) && (!sd->bounce.x.animator)) _efl_ui_scroll_manager_anim_stop(sd); _scroll_manager_bounce_y_animator_del(sd); } } static void _efl_ui_scroll_manager_bounce_x_animator(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Coord nx; Eina_Position2D cur = {0, 0}; double t = 0.0, dt = 0.0, r = 0.0; t = ecore_loop_time_get(); if (sd->bounce.x.start_t + sd->bounce.x.t01 >= t) { dt = sd->bounce.x.start_t + sd->bounce.x.t01 - t; r = 1.0 - (dt / sd->bounce.x.t01); r = _scroll_manager_decel_interp(NULL, r); nx = sd->bounce.x.p0 + (sd->bounce.x.p1 - sd->bounce.x.p0) * r; cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(nx, cur.y)); } else if (sd->bounce.x.start_t + sd->bounce.x.t01 + sd->bounce.x.t12 >= t) { dt = sd->bounce.x.start_t + sd->bounce.x.t01 + sd->bounce.x.t12 - t; r = 1.0 - (dt / sd->bounce.x.t12); r = _scroll_manager_decel_interp(NULL, r); nx = sd->bounce.x.p1 + (sd->bounce.x.p2 - sd->bounce.x.p1) * r; cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(nx, cur.y)); } else { cur = efl_ui_scrollable_content_pos_get(sd->obj); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(sd->bounce.x.p2, cur.y)); if ((!sd->scrollto.y.animator) && (!sd->bounce.y.animator)) _efl_ui_scroll_manager_anim_stop(sd); _scroll_manager_bounce_x_animator_del(sd); } } static void _scroll_manager_bounce_x_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vx) { static const double spring_k = 1000; static const double mass = 1; char sign = (vx > 0) - (vx < 0); Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; _scroll_manager_bounce_x_animator_del(sd); cur = efl_ui_pan_position_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); double max_x = sqrt((mass * vx * vx) / spring_k); sd->bounce.x.start_t = ecore_loop_time_get(); sd->bounce.x.vel = vx; sd->bounce.x.p0 = cur.x; if (fabs(vx) > 0.0) sd->bounce.x.t01 = 0.2; else sd->bounce.x.t01 = 0.0; sd->bounce.x.p1 = cur.x + sign * max_x;; sd->bounce.x.t12 = 0.2; if ( cur.x < min.x ) { sd->bounce.x.p2 = min.x; } else if ( cur.x > max.x) { sd->bounce.x.p2 = max.x; } if ((!sd->bounce.y.animator) && (!sd->scrollto.y.animator)) _efl_ui_scroll_manager_anim_start(sd); ELM_ANIMATOR_CONNECT(sd->event_rect, sd->bounce.x.animator, _efl_ui_scroll_manager_bounce_x_animator, sd); } static Eina_Bool _scroll_manager_bounce_x_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->bounce.x.animator) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->bounce.x.animator, _efl_ui_scroll_manager_bounce_x_animator, sd); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_bounce_y_animator_add(Efl_Ui_Scroll_Manager_Data *sd, double vy) { static const double spring_k = 1000; static const double mass = 1; char sign = (vy > 0) - (vy < 0); Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; _scroll_manager_bounce_y_animator_del(sd); cur = efl_ui_pan_position_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); double max_y = sqrt((mass * vy * vy) / spring_k); sd->bounce.y.start_t = ecore_loop_time_get(); sd->bounce.y.vel = vy; sd->bounce.y.p0 = cur.y; if (fabs(vy) > 0.0) sd->bounce.y.t01 = 0.2; else sd->bounce.y.t01 = 0.0; sd->bounce.y.p1 = cur.y + sign * max_y; sd->bounce.y.t12 = 0.2; if ( cur.y < min.y ) { sd->bounce.y.p2 = min.y; } else if ( cur.y > max.y) { sd->bounce.y.p2 = max.y; } if ((!sd->bounce.x.animator) && (!sd->scrollto.x.animator)) _efl_ui_scroll_manager_anim_start(sd); ELM_ANIMATOR_CONNECT(sd->event_rect, sd->bounce.y.animator, _efl_ui_scroll_manager_bounce_y_animator, sd); } static Eina_Bool _scroll_manager_bounce_y_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->bounce.y.animator) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->bounce.y.animator, _efl_ui_scroll_manager_bounce_y_animator, sd); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_scrollto_x_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord sx, Evas_Coord ex, double t, InterpType interp) { sd->scrollto.x.start_t = ecore_loop_time_get(); sd->scrollto.x.dur = t; sd->scrollto.x.start = sx; sd->scrollto.x.end = ex; sd->scrollto.x.interp = _scroll_manager_interp_get(interp); if (!sd->scrollto.x.animator) { ELM_ANIMATOR_CONNECT(sd->event_rect, sd->scrollto.x.animator, _efl_ui_scroll_manager_scroll_to_x_animator, sd); if (!sd->scrollto.y.animator) _efl_ui_scroll_manager_anim_start(sd); } } static Eina_Bool _scroll_manager_scrollto_x_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->scrollto.x.animator) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.x.animator, _efl_ui_scroll_manager_scroll_to_x_animator, sd); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_scrollto_y_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord sy, Evas_Coord ey, double t, InterpType interp) { sd->scrollto.y.start_t = ecore_loop_time_get(); sd->scrollto.y.dur = t; sd->scrollto.y.start = sy; sd->scrollto.y.end = ey; sd->scrollto.y.interp = _scroll_manager_interp_get(interp); if (!sd->scrollto.y.animator) { ELM_ANIMATOR_CONNECT(sd->event_rect, sd->scrollto.y.animator, _efl_ui_scroll_manager_scroll_to_y_animator, sd); if (!sd->scrollto.x.animator) _efl_ui_scroll_manager_anim_start(sd); } } static Eina_Bool _scroll_manager_scrollto_y_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->scrollto.y.animator) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.y.animator, _efl_ui_scroll_manager_scroll_to_y_animator, sd); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_scrollto_animator_add(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord sx, Evas_Coord sy, Evas_Coord x, Evas_Coord y, double tx, double ty, InterpType interp) { if (!sd->pan_obj || sd->freeze) { _scroll_manager_scrollto_animator_del(sd); return; } _scroll_manager_scrollto_x_animator_add(sd, sx, x, tx, interp); _scroll_manager_scrollto_y_animator_add(sd, sy, y, ty, interp); } static Eina_Bool _scroll_manager_scrollto_animator_del(Efl_Ui_Scroll_Manager_Data *sd) { if ((sd->scrollto.x.animator) || (sd->scrollto.y.animator)) { ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.x.animator, _efl_ui_scroll_manager_scroll_to_x_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.y.animator, _efl_ui_scroll_manager_scroll_to_y_animator, sd); return EINA_TRUE; } return EINA_FALSE; } static void _scroll_manager_scrollto(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord x, Evas_Coord y) { double dur = 0.0; Eina_Position2D cur = efl_ui_scrollable_content_pos_get(sd->obj); dur = _scroll_manager_animation_duration_get(x - cur.x, y - cur.y); _scroll_manager_scrollto_animator_add(sd, cur.x, cur.y, x, y, dur, dur, INTERP_LINEAR); } static void _efl_ui_scroll_manager_post_event_move_on_hold_eval(Efl_Ui_Scroll_Manager_Data *sd, Evas_Event_Mouse_Move *ev) { Evas_Coord x = 0, y = 0; double vx = 0.0, vy = 0.0; char sx = 0, sy = 0; Eina_Rect r = efl_ui_scrollable_viewport_geometry_get(sd->obj); x = (r.x - ev->cur.canvas.x) > 0 ? (r.x - ev->cur.canvas.x) : 0; y = (r.y - ev->cur.canvas.y) > 0 ? (r.y - ev->cur.canvas.y) : 0; x = (ev->cur.canvas.x - (r.x + r.w)) > 0 ? (ev->cur.canvas.x - (r.x + r.w)) : x; y = (ev->cur.canvas.y - (r.y + r.h)) > 0 ? (ev->cur.canvas.y - (r.y + r.h)) : y; sx = r.x - ev->cur.canvas.x > 0 ? -1 : 1; sy = r.y - ev->cur.canvas.y > 0 ? -1 : 1; if (x > _elm_config->thumbscroll_hold_threshold) { vx = 1.0; if (_elm_config->thumbscroll_hold_threshold > 0.0) vx = (double)(x - _elm_config->thumbscroll_hold_threshold) / _elm_config->thumbscroll_hold_threshold; } if (y > _elm_config->thumbscroll_hold_threshold) { vy = 1.0; if (_elm_config->thumbscroll_hold_threshold > 0.0) vy = (double)(y - _elm_config->thumbscroll_hold_threshold) / _elm_config->thumbscroll_hold_threshold; } if ((vx != 0.0) || (vy != 0.0)) _scroll_manager_on_hold_animator_add(sd, vx*sx, vy*sy); else _scroll_manager_on_hold_animator_del(sd); } static Eina_Bool _efl_ui_scroll_manager_post_event_move_direction_restrict_eval(Efl_Ui_Scroll_Manager_Data *sd, Evas_Event_Mouse_Move *ev EINA_UNUSED, Evas_Coord dx, Evas_Coord dy) { if (sd->down.dragged) return EINA_FALSE; sd->down.hdir = -1; sd->down.vdir = -1; if (dx > 0) sd->down.hdir = LEFT; else if (dx < 0) sd->down.hdir = RIGHT; if (dy > 0) sd->down.vdir = UP; else if (dy < 0) sd->down.vdir = DOWN; if (!(sd->block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL)) sd->down.dir_x = EINA_TRUE; if (!(sd->block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL)) sd->down.dir_y = EINA_TRUE; return EINA_TRUE; } static Eina_Bool _efl_ui_scroll_manager_post_event_move_hold_eval(Efl_Ui_Scroll_Manager_Data *sd, Evas_Event_Mouse_Move *ev, Evas_Coord dx, Evas_Coord dy) { if (!sd->down.dragged) { if(((dx * dx) + (dy * dy)) > (_elm_config->thumbscroll_threshold * _elm_config->thumbscroll_threshold)) { if (_elm_config->scroll_smooth_start_enable) { sd->down.x = ev->cur.canvas.x; sd->down.y = ev->cur.canvas.y; sd->down.dragged_began_timestamp = ev->timestamp / 1000.0; } // TODO 다른조건들도 can_scroll 안쪽으로 넣는다? if ((((_efl_ui_scroll_manager_can_scroll(sd, sd->down.hdir) || sd->bounce_horiz) && sd->down.dir_x) || ((_efl_ui_scroll_manager_can_scroll(sd, sd->down.vdir) || sd->bounce_vert) && sd->down.dir_y))) { _efl_ui_scroll_manager_drag_start(sd); sd->down.dragged = EINA_TRUE; ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; } else if (!sd->down.dragged) return EINA_FALSE; } return EINA_TRUE; } if (sd->down.want_reset) { sd->down.x = ev->cur.canvas.x; sd->down.y = ev->cur.canvas.y; sd->down.want_reset = EINA_FALSE; } if (sd->down.dir_x) dx = sd->down.sx - (ev->cur.canvas.x - sd->down.x); else dx = sd->down.sx; if (sd->down.dir_y) dy = sd->down.sy - (ev->cur.canvas.y - sd->down.y); else dy = sd->down.sy; _efl_ui_scroll_manager_bounce_weight_apply(sd, &dx, &dy); _scroll_manager_hold_animator_add(sd, dx, dy); return EINA_TRUE; } static Eina_Bool _efl_ui_scroll_manager_post_event_move(void *data, Evas *e EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Event_Mouse_Move *ev = sd->event_info; sd->event_info = NULL; Evas_Coord dx, dy; dx = ev->cur.canvas.x - sd->down.x; dy = ev->cur.canvas.y - sd->down.y; _efl_ui_scroll_manager_post_event_move_direction_restrict_eval(sd, ev, dx, dy); if (!sd->freeze) { if (!sd->hold) { if (!_efl_ui_scroll_manager_post_event_move_hold_eval(sd, ev, dx, dy)) return EINA_TRUE; } else { _efl_ui_scroll_manager_post_event_move_on_hold_eval(sd, ev); } } return EINA_FALSE; } static void _efl_ui_scroll_manager_down_coord_eval(Efl_Ui_Scroll_Manager_Data *sd, Evas_Coord *x, Evas_Coord *y) { if (!sd->pan_obj) return; if (sd->down.dir_x) *x = sd->down.sx - (*x - sd->down.x); else *x = sd->down.sx; if (sd->down.dir_y) *y = sd->down.sy - (*y - sd->down.y); else *y = sd->down.sy; _efl_ui_scroll_manager_bounce_weight_apply(sd, x, y); } static Eina_Bool _efl_ui_scroll_manager_hold_enterer(void *data) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Coord fx = 0, fy = 0; sd->down.hold_enterer = NULL; fx = sd->down.hold_x; fy = sd->down.hold_y; if ((_elm_config->scroll_smooth_amount > 0.0) && (_elm_config->scroll_smooth_time_window > 0.0)) { int i, count = 0; Evas_Coord basex = 0, basey = 0, x, y; double dt, tdiff, tnow, twin, ttot; double xx, yy, tot; struct { Evas_Coord x, y; double t; } pos[100]; tdiff = sd->down.est_timestamp_diff; tnow = ecore_loop_time_get(); twin = _elm_config->scroll_smooth_time_window; for (i = 0; i < 60; i++) { if ((sd->down.history[i].timestamp - tdiff) > tnow) continue; if ((sd->down.history[i].timestamp > sd->down.dragged_began_timestamp) || (count == 0)) { x = sd->down.history[i].x; y = sd->down.history[i].y; _efl_ui_scroll_manager_down_coord_eval(sd, &x, &y); if (count == 0) { basex = x; basey = y; } dt = (tnow + tdiff) - sd->down.history[i].timestamp; if ((dt > twin) && (count > 0)) break; if ((dt > 0.0) && (count == 0)) { pos[count].x = x - basex; pos[count].y = y - basey; pos[count].t = 0.0; count++; } pos[count].x = x - basex; pos[count].y = y - basey; pos[count].t = dt; count++; } } if (count > 0) { xx = 0.0; yy = 0.0; tot = 0.0; ttot = pos[count - 1].t; for (i = 0; i < count; i++) { double wt; if (ttot > 0.0) { if (i < (count - 1)) wt = (ttot - pos[i].t) * (pos[i + 1].t - pos[i].t); else wt = 0.0; } else wt = 1.0; xx += ((double)(pos[i].x)) * wt; yy += ((double)(pos[i].y)) * wt; tot += wt; } if (tot > 0.0) { xx = basex + (xx / tot); yy = basey + (yy / tot); fx = (_elm_config->scroll_smooth_amount * xx) + ((1.0 - _elm_config->scroll_smooth_amount) * fx); fy = (_elm_config->scroll_smooth_amount * yy) + ((1.0 - _elm_config->scroll_smooth_amount) * fy); } } } Eina_Position2D cur = efl_ui_scrollable_content_pos_get(sd->obj); if (sd->down.dir_x) cur.x = fx; if (sd->down.dir_y) cur.y = fy; efl_ui_scrollable_content_pos_set(sd->obj, cur); return EINA_FALSE; } static void _efl_ui_scroll_manager_hold_animator(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; ecore_idle_enterer_del(sd->down.hold_enterer); sd->down.hold_enterer = ecore_idle_enterer_before_add(_efl_ui_scroll_manager_hold_enterer, sd); } static void _efl_ui_scroll_manager_on_hold_animator(void *data, const Efl_Event *event EINA_UNUSED) { double t, td; double vx, vy; Evas_Coord x, y; Eina_Position2D cur = {0, 0}; Efl_Ui_Scroll_Manager_Data *sd; sd = data; t = ecore_loop_time_get(); if (sd->down.onhold_tlast > 0.0) { td = t - sd->down.onhold_tlast; vx = sd->down.onhold_vx * td * (double)_elm_config->thumbscroll_hold_threshold * 2.0; vy = sd->down.onhold_vy * td * (double)_elm_config->thumbscroll_hold_threshold * 2.0; cur = efl_ui_scrollable_content_pos_get(sd->obj); x = cur.x; y = cur.y; if (sd->down.dir_x) { sd->down.onhold_vxe += vx; x = cur.x + (int)sd->down.onhold_vxe; sd->down.onhold_vxe -= (int)sd->down.onhold_vxe; } if (sd->down.dir_y) { sd->down.onhold_vye += vy; y = cur.y + (int)sd->down.onhold_vye; sd->down.onhold_vye -= (int)sd->down.onhold_vye; } efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(x, y)); } sd->down.onhold_tlast = t; } static void _efl_ui_scroll_manager_mouse_move_event_cb(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info) { Efl_Ui_Scroll_Manager_Data *sd = data; Evas_Event_Mouse_Move *ev; Eina_Position2D cur = {0, 0}; if (!sd->pan_obj) return; if (!_scroll_manager_thumb_scrollable_get(sd)) return; if (!sd->down.now) return; ev = event_info; if ((!sd->hold) && (!sd->freeze)) { if (_scroll_manager_animating_get(sd)) { _scroll_manager_animators_drop(sd->obj); cur = efl_ui_pan_position_get(sd->pan_obj); sd->down.sx = cur.x; sd->down.sy = cur.y; sd->down.x = sd->down.history[0].x; sd->down.y = sd->down.history[0].y; } } #ifdef SCROLLDBG DBG("::: %i %i\n", ev->cur.canvas.x, ev->cur.canvas.y); #endif memmove(&(sd->down.history[1]), &(sd->down.history[0]), sizeof(sd->down.history[0]) * (60 - 1)); sd->down.history[0].timestamp = ev->timestamp / 1000.0; sd->down.history[0].x = ev->cur.canvas.x; sd->down.history[0].y = ev->cur.canvas.y; sd->event_info = event_info; if (!(ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)) evas_post_event_callback_push(e, _efl_ui_scroll_manager_post_event_move, sd); if (sd->down.dragged) ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; } static void _scroll_event_object_attach(Evas_Object *obj) { EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); evas_object_event_callback_add (sd->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _efl_ui_scroll_manager_wheel_event_cb, sd); evas_object_event_callback_add (sd->event_rect, EVAS_CALLBACK_MOUSE_DOWN, _efl_ui_scroll_manager_mouse_down_event_cb, sd); evas_object_event_callback_add (sd->event_rect, EVAS_CALLBACK_MOUSE_UP, _efl_ui_scroll_manager_mouse_up_event_cb, sd); evas_object_event_callback_add (sd->event_rect, EVAS_CALLBACK_MOUSE_MOVE, _efl_ui_scroll_manager_mouse_move_event_cb, sd); } static void _efl_ui_scroll_manager_content_resized(Efl_Ui_Scroll_Manager_Data *sd, Eina_Size2D content) { sd->content_info.w = content.w; sd->content_info.h = content.h; sd->content_info.resized = EINA_TRUE; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED, NULL); efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED, NULL); efl_ui_scrollbar_bar_visibility_update(sd->obj); _efl_ui_scroll_manager_wanted_region_set(sd->obj); } static void _efl_ui_scroll_manager_pan_content_size_changed_cb(void *data, const Efl_Event *event) { Efl_Ui_Scroll_Manager_Data *sd = data; Eina_Size2D *content = event->info; _efl_ui_scroll_manager_content_resized(sd, *content); } static void _efl_ui_scroll_manager_pan_content_changed(Efl_Ui_Scroll_Manager_Data *sd) { Eina_Size2D sz = {0, 0}; if (sd->pan_obj) sz = efl_ui_pan_content_size_get(sd->pan_obj); _efl_ui_scroll_manager_content_resized(sd, sz); } static void _efl_ui_scroll_manager_pan_content_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { _efl_ui_scroll_manager_pan_content_changed(data); } static void _efl_ui_scroll_manager_pan_viewport_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; if (!sd->pan_obj) return; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED, NULL); efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED, NULL); efl_ui_scrollbar_bar_visibility_update(sd->obj); _efl_ui_scroll_manager_wanted_region_set(sd->obj); } static void _efl_ui_scroll_manager_pan_position_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Scroll_Manager_Data *sd = data; if (!sd->pan_obj) return; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED, NULL); efl_ui_scrollbar_bar_visibility_update(sd->obj); } static void _efl_ui_scroll_manager_pan_resized_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *manager = data; EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(manager, sd); efl_gfx_entity_size_set(sd->event_rect, efl_gfx_entity_size_get(obj)); } static void _efl_ui_scroll_manager_pan_moved_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *manager = data; EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(manager, sd); efl_gfx_entity_position_set(sd->event_rect, efl_gfx_entity_position_get(obj)); } static void _efl_ui_scroll_manager_scrollbar_h_visibility_apply(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->hbar_visible) { Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, &type); } else { Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE, &type); } } static void _efl_ui_scroll_manager_scrollbar_v_visibility_apply(Efl_Ui_Scroll_Manager_Data *sd) { if (sd->vbar_visible) { Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, &type); } else { Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE, &type); } } static void _efl_ui_scrollbar_h_visibility_adjust(Eo *obj) { EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); int scroll_h_vis_change = 0; Evas_Coord w; w = sd->content_info.w; Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->obj); switch (sd->hbar_mode) { case EFL_UI_SCROLLBAR_MODE_AUTO: if (sd->hbar_visible) { if (w <= view.w) { scroll_h_vis_change = 1; sd->hbar_visible = EINA_FALSE; } } else { if (w > view.w) { scroll_h_vis_change = 1; sd->hbar_visible = EINA_TRUE; } } break; case EFL_UI_SCROLLBAR_MODE_ON: if (!sd->hbar_visible) { scroll_h_vis_change = 1; sd->hbar_visible = EINA_TRUE; } break; case EFL_UI_SCROLLBAR_MODE_OFF: if (sd->hbar_visible) { scroll_h_vis_change = 1; sd->hbar_visible = EINA_FALSE; } break; default: break; } if (scroll_h_vis_change) _efl_ui_scroll_manager_scrollbar_h_visibility_apply(sd); } static void _efl_ui_scrollbar_v_visibility_adjust(Eo *obj) { EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd); int scroll_v_vis_change = 0; Evas_Coord h; h = sd->content_info.h; Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->obj); switch (sd->vbar_mode) { case EFL_UI_SCROLLBAR_MODE_AUTO: if (sd->vbar_visible) { if (h <= view.h) { scroll_v_vis_change = 1; sd->vbar_visible = EINA_FALSE; } } else { if (h > view.h) { scroll_v_vis_change = 1; sd->vbar_visible = EINA_TRUE; } } break; case EFL_UI_SCROLLBAR_MODE_ON: if (!sd->vbar_visible) { scroll_v_vis_change = 1; sd->vbar_visible = EINA_TRUE; } break; case EFL_UI_SCROLLBAR_MODE_OFF: if (sd->vbar_visible) { scroll_v_vis_change = 1; sd->vbar_visible = EINA_FALSE; } break; default: break; } if (scroll_v_vis_change) _efl_ui_scroll_manager_scrollbar_v_visibility_apply(sd); } +EOLIAN static void +_efl_ui_scroll_manager_efl_ui_scrollbar_bar_visibility_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool *hbar, Eina_Bool *vbar) +{ + if (hbar) *hbar = sd->hbar_visible; + if (vbar) *vbar = sd->vbar_visible; +} + EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_visibility_update(Eo *obj, Efl_Ui_Scroll_Manager_Data *sd EINA_UNUSED) { _efl_ui_scrollbar_h_visibility_adjust(obj); _efl_ui_scrollbar_v_visibility_adjust(obj); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_position_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, double posx, double posy) { Evas_Coord x, y; Eina_Position2D min = {0, 0}, max = {0, 0}; if (sd->down.dragged || _scroll_manager_animating_get(sd)) return; max = efl_ui_pan_position_max_get(sd->pan_obj); min = efl_ui_pan_position_min_get(sd->pan_obj); x = _round(posx * (double)max.x + min.x, 1); y = _round(posy * (double)max.y + min.y, 1); efl_ui_scrollable_content_pos_set(sd->obj, EINA_POSITION2D(x, y)); _efl_ui_scroll_manager_wanted_coordinates_update(sd, x, y); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_position_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, double *posx, double *posy) { if (!sd->pan_obj) return; double vx = 0, vy = 0; Eina_Position2D min = {0, 0}, max = {0, 0}, cur = {0, 0}; min = efl_ui_pan_position_min_get(sd->pan_obj); max = efl_ui_pan_position_max_get(sd->pan_obj); cur = efl_ui_pan_position_get(sd->pan_obj); if (max.x > 0) vx = (double)(cur.x - min.x) / (double)max.x; else vx = 0.0; if (vx < 0.0) vx = 0.0; else if (vx > 1.0) vx = 1.0; if (posx) *posx = vx; if (max.y > 0) vy = (double)(cur.y - min.y) / (double)max.y; else vy = 0.0; if (vy < 0.0) vy = 0.0; else if (vy > 1.0) vy = 1.0; if (posy) *posy = vy; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollbar_bar_size_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, double *width, double *height) { if (!sd->pan_obj) return; Evas_Coord w = 0, h = 0, vw = 0, vh = 0; double size = 0.0; Eina_Rect r = efl_ui_scrollable_viewport_geometry_get(sd->obj); vw = r.w; vh = r.h; w = sd->content_info.w; if (w < 1) w = 1; size = (double)vw / (double)w; if (size > 1.0) size = 1.0; if (width) *width = size; h = sd->content_info.h; if (h < 1) h = 1; size = (double)vh / (double)h; if (size > 1.0) size = 1.0; if (height) *height = size; } EOLIAN static void _efl_ui_scroll_manager_pan_set(Eo *obj, Efl_Ui_Scroll_Manager_Data *sd, Eo *pan) { if (sd->pan_obj == pan) return; if (sd->pan_obj) { efl_event_callback_del (sd->pan_obj, EFL_CONTENT_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_content_size_changed_cb, sd); } sd->pan_obj = pan; _efl_ui_scroll_manager_pan_content_changed(sd); if (!pan) return; efl_event_callback_add (sd->pan_obj, EFL_CONTENT_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd); efl_event_callback_add (sd->pan_obj, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_add (sd->pan_obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_add (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_changed_cb, sd); efl_event_callback_add (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_content_size_changed_cb, sd); evas_object_event_callback_add(sd->pan_obj, EVAS_CALLBACK_RESIZE, _efl_ui_scroll_manager_pan_resized_cb, obj); evas_object_event_callback_add(sd->pan_obj, EVAS_CALLBACK_MOVE, _efl_ui_scroll_manager_pan_moved_cb, obj); } EOLIAN static Eina_Bool _efl_ui_scroll_manager_efl_ui_scrollable_scroll_hold_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { return sd->hold; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_scroll_hold_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool hold) { sd->hold = hold; } EOLIAN static Eina_Bool _efl_ui_scroll_manager_efl_ui_scrollable_scroll_freeze_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { return sd->freeze; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_scroll_freeze_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool freeze) { sd->freeze = freeze; if (sd->freeze) { _scroll_manager_on_hold_animator_del(sd); } else _efl_ui_scroll_manager_bounce_eval(sd); } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_bounce_enabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool horiz, Eina_Bool vert) { sd->bounce_horiz = !!horiz; sd->bounce_vert = !!vert; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_bounce_enabled_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool *horiz, Eina_Bool *vert) { if (horiz) *horiz = sd->bounce_horiz; if (vert) *vert = sd->bounce_vert; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_scroll(Eo *obj, Efl_Ui_Scroll_Manager_Data *sd, Eina_Rect rect, Eina_Bool animation) { _scroll_manager_animators_drop(obj); if (animation) { if (_efl_ui_scroll_manager_content_region_show_internal(obj, &(rect.x), &(rect.y), rect.w, rect.h)) { _scroll_manager_scrollto(sd, rect.x, rect.y); } } else { sd->wx = (sd->is_mirrored ? _efl_ui_scroll_manager_x_mirrored_get(sd->obj, rect.x) : rect.x); sd->wy = rect.y; sd->ww = rect.w; sd->wh = rect.h; if (_efl_ui_scroll_manager_content_region_show_internal(obj, &(rect.x), &(rect.y), rect.w, rect.h)) { efl_ui_scrollable_content_pos_set(obj, EINA_POSITION2D(rect.x, rect.y)); sd->down.sx = rect.x; sd->down.sy = rect.y; sd->down.x = sd->down.history[0].x; sd->down.y = sd->down.history[0].y; } } } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_gravity_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, double x, double y) { sd->gravity_x = x; sd->gravity_y = y; Eina_Position2D max = efl_ui_pan_position_max_get(sd->pan_obj); sd->prev_cw = max.x; sd->prev_ch = max.y; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_gravity_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, double *x, double *y) { if (x) *x = sd->gravity_x; if (y) *y = sd->gravity_y; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_movement_block_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Efl_Ui_Layout_Orientation block) { sd->block = block; } EOLIAN static Efl_Ui_Layout_Orientation _efl_ui_scroll_manager_efl_ui_scrollable_movement_block_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd) { return sd->block; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_looping_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool loop_h, Eina_Bool loop_v) { if (sd->loop_h == loop_h && sd->loop_v == loop_v) return; sd->loop_h = loop_h; sd->loop_v = loop_v; } EOLIAN static void _efl_ui_scroll_manager_efl_ui_scrollable_looping_get(const Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Eina_Bool *loop_h, Eina_Bool *loop_v) { *loop_h = sd->loop_h; *loop_v = sd->loop_v; } EOLIAN static Eo * _efl_ui_scroll_manager_efl_object_constructor(Eo *obj, Efl_Ui_Scroll_Manager_Data *sd) { obj = efl_constructor(efl_super(obj, MY_CLASS)); memset(sd, 0, sizeof(*sd)); sd->parent = efl_parent_get(obj); sd->obj = obj; sd->step.x = 32 * elm_config_scale_get(); sd->step.y = 32 * elm_config_scale_get(); sd->page.x = -50; sd->page.y = -50; sd->loop_h = EINA_FALSE; sd->loop_v = EINA_FALSE; sd->hbar_mode = EFL_UI_SCROLLBAR_MODE_AUTO; sd->vbar_mode = EFL_UI_SCROLLBAR_MODE_AUTO; sd->hbar_visible = EINA_TRUE; sd->vbar_visible = EINA_TRUE; sd->bounce_horiz = _elm_config->thumbscroll_bounce_enable; sd->bounce_vert = _elm_config->thumbscroll_bounce_enable; sd->block = EFL_UI_LAYOUT_ORIENTATION_DEFAULT; sd->scrolling = EINA_FALSE; sd->event_rect = evas_object_rectangle_add(evas_object_evas_get(sd->parent)); efl_key_data_set(sd->event_rect, "_elm_leaveme", obj); efl_canvas_group_member_add(sd->parent, sd->event_rect); efl_ui_widget_sub_object_add(sd->parent, sd->event_rect); efl_gfx_color_set(sd->event_rect, 0, 0, 0, 0); efl_gfx_entity_visible_set(sd->event_rect, EINA_TRUE); efl_canvas_object_repeat_events_set(sd->event_rect, EINA_TRUE); _scroll_event_object_attach(obj); //FIXME : mostly bar-related callback is added after scroll manager creation, // so when constructor of manager is called, callback is not registered. //efl_ui_scrollbar_bar_visibility_update(sd->obj); return obj; } EOLIAN static void _efl_ui_scroll_manager_efl_object_destructor(Eo *obj, Efl_Ui_Scroll_Manager_Data *sd) { ecore_idle_enterer_del(sd->down.hold_enterer); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->down.hold_animator, _efl_ui_scroll_manager_hold_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->down.onhold_animator, _efl_ui_scroll_manager_on_hold_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->bounce.x.animator, _efl_ui_scroll_manager_bounce_x_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->bounce.y.animator, _efl_ui_scroll_manager_bounce_y_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.x.animator, _efl_ui_scroll_manager_scroll_to_x_animator, sd); ELM_ANIMATOR_DISCONNECT(sd->event_rect, sd->scrollto.y.animator, _efl_ui_scroll_manager_scroll_to_y_animator, sd); if (!efl_invalidating_get(sd->pan_obj)) { evas_object_event_callback_del_full(sd->pan_obj, EVAS_CALLBACK_RESIZE, _efl_ui_scroll_manager_pan_resized_cb, obj); evas_object_event_callback_del_full(sd->pan_obj, EVAS_CALLBACK_MOVE, _efl_ui_scroll_manager_pan_moved_cb, obj); efl_event_callback_del (sd->pan_obj, EFL_CONTENT_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_changed_cb, sd); efl_event_callback_del (sd->pan_obj, EFL_UI_PAN_EVENT_PAN_CONTENT_SIZE_CHANGED, _efl_ui_scroll_manager_pan_content_size_changed_cb, sd); } efl_destructor(efl_super(obj, MY_CLASS)); } #include "efl_ui_scroll_manager.eo.c" diff --git a/src/lib/elementary/efl_ui_scroll_manager.eo b/src/lib/elementary/efl_ui_scroll_manager.eo index 957a4f4d4d..9dcd9f8a88 100644 --- a/src/lib/elementary/efl_ui_scroll_manager.eo +++ b/src/lib/elementary/efl_ui_scroll_manager.eo @@ -1,45 +1,46 @@ class @beta Efl.Ui.Scroll.Manager extends Efl.Object implements Efl.Ui.I18n, Efl.Ui.Scrollable, Efl.Ui.Scrollbar { [[Efl ui scroll manager class]] event_c_prefix: efl_ui; c_prefix: efl_ui_scroll_manager; methods { @property pan @protected { [[This is the internal pan object managed by scroll manager. This property is protected as it is meant for scrollable object implementations only, to set and access the internal pan object. If pan is set to $NULL, scrolling does not work. ]] set { } values { pan: Efl.Ui.Pan; [[Pan object.]] } } } implements { Efl.Object.constructor; Efl.Object.destructor; Efl.Ui.I18n.mirrored { set; } Efl.Ui.Scrollable.content_pos { set; get; } Efl.Ui.Scrollable.content_size{ get; } Efl.Ui.Scrollable.viewport_geometry{ get; } Efl.Ui.Scrollable.bounce_enabled { set; get; } Efl.Ui.Scrollable.scroll_freeze { get; set; } Efl.Ui.Scrollable.scroll_hold { get; set; } Efl.Ui.Scrollable.looping { get; set; } Efl.Ui.Scrollable.movement_block { get; set; } Efl.Ui.Scrollable.gravity { get; set; } Efl.Ui.Scrollable.match_content { set; } Efl.Ui.Scrollable.step_size { set; get; } Efl.Ui.Scrollbar.bar_mode { get; set; } Efl.Ui.Scrollbar.bar_size { get; } Efl.Ui.Scrollbar.bar_position { get; set; } + Efl.Ui.Scrollbar.bar_visibility { get; } Efl.Ui.Scrollbar.bar_visibility_update; Efl.Ui.Scrollable.scroll; } } diff --git a/src/lib/elementary/efl_ui_scroll_util.c b/src/lib/elementary/efl_ui_scroll_util.c index 673cac2eae..6a7378e3b4 100644 --- a/src/lib/elementary/efl_ui_scroll_util.c +++ b/src/lib/elementary/efl_ui_scroll_util.c @@ -1,350 +1,364 @@ #ifdef HAVE_CONFIG_H #include "elementary_config.h" #endif #define EFL_UI_SCROLL_MANAGER_PROTECTED #define EFL_UI_SCROLLBAR_PROTECTED #include #include #include "elm_priv.h" typedef struct { Eo *obj; Eo *smanager; int freeze_want; Eina_Bool scroll_count : 1; Eina_Bool need_scroll : 1; } Scroll_Connector_Context; static void _scroll_connector_bar_read_and_update(Scroll_Connector_Context *ctx) { ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); double vx = 0.0, vy = 0.0; edje_object_part_drag_value_get (wd->resize_obj, "efl.draggable.vertical_bar", NULL, &vy); edje_object_part_drag_value_get (wd->resize_obj, "efl.draggable.horizontal_bar", &vx, NULL); efl_ui_scrollbar_bar_position_set(ctx->smanager, vx, vy); } static void _scroll_connector_reload_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; + ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); + Eina_Bool hbar_visible = EINA_FALSE, vbar_visible = EINA_FALSE; + + efl_ui_scrollbar_bar_visibility_get(ctx->smanager, &hbar_visible, &vbar_visible); + + if (hbar_visible) + efl_layout_signal_emit(wd->resize_obj, "efl,horizontal_bar,visible,on", "efl"); + else + efl_layout_signal_emit(wd->resize_obj, "efl,horizontal_bar,visible,off", "efl"); + + if (vbar_visible) + efl_layout_signal_emit(wd->resize_obj, "efl,vertical_bar,visible,on", "efl"); + else + efl_layout_signal_emit(wd->resize_obj, "efl,vertical_bar,visible,off", "efl"); efl_ui_scrollbar_bar_visibility_update(ctx->smanager); } static void _scroll_connector_edje_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; _scroll_connector_bar_read_and_update(ctx); } static void _scroll(void *data, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; ctx->scroll_count = EINA_FALSE; if (!ctx->need_scroll) return; ctx->need_scroll = EINA_FALSE; efl_layout_signal_emit(obj, "efl,action,scroll", "efl"); } static void _scroll_connector_edje_drag_start_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; _scroll_connector_bar_read_and_update(ctx); ctx->freeze_want = efl_ui_scrollable_scroll_freeze_get(ctx->smanager); efl_ui_scrollable_scroll_freeze_set(ctx->smanager, EINA_TRUE); efl_event_callback_call(ctx->obj, EFL_UI_EVENT_SCROLL_DRAG_STARTED, NULL); } static void _scroll_connector_edje_drag_stop_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; _scroll_connector_bar_read_and_update(ctx); EINA_SAFETY_ON_TRUE_RETURN(ctx->freeze_want == -1); efl_ui_scrollable_scroll_freeze_set(ctx->smanager, ctx->freeze_want); ctx->freeze_want = -1; efl_event_callback_call(ctx->obj, EFL_UI_EVENT_SCROLL_DRAG_FINISHED, NULL); } static void _scroll_connector_vbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type; _scroll_connector_bar_read_and_update(ctx); type = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_DRAGGED, &type); } static void _scroll_connector_vbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_PRESSED, &type); } static void _scroll_connector_vbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_VERTICAL; efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESSED, &type); } static void _scroll_connector_hbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; _scroll_connector_bar_read_and_update(ctx); efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_DRAGGED, &type); } static void _scroll_connector_hbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_PRESSED, &type); } static void _scroll_connector_hbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Scroll_Connector_Context *ctx = data; Efl_Ui_Layout_Orientation type = EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL; efl_event_callback_call(ctx->obj, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESSED, &type); } static void _scroll_connector_bar_size_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Scroll_Connector_Context *ctx = data; ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); double width = 0.0, height = 0.0; edje_object_calc_force(wd->resize_obj); efl_ui_scrollbar_bar_size_get(ctx->smanager, &width, &height); edje_object_part_drag_size_set(wd->resize_obj, "efl.draggable.horizontal_bar", width, 1.0); edje_object_part_drag_size_set(wd->resize_obj, "efl.draggable.vertical_bar", 1.0, height); } static void _scroll_connector_bar_pos_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Scroll_Connector_Context *ctx = data; ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); double posx = 0.0, posy = 0.0; Eina_Size2D cs; Eina_Position2D step; step = efl_ui_scrollable_step_size_get(ctx->smanager); cs = efl_ui_scrollable_content_size_get(ctx->smanager); edje_object_part_drag_step_set(wd->resize_obj, "efl.draggable.horizontal_bar", (double)step.x / cs.w, 0.0); edje_object_part_drag_step_set(wd->resize_obj, "efl.draggable.vertical_bar", 0.0, (double)step.y / cs.h); efl_ui_scrollbar_bar_position_get(ctx->smanager, &posx, &posy); edje_object_part_drag_value_set(wd->resize_obj, "efl.draggable.horizontal_bar", posx, 0.0); edje_object_part_drag_value_set(wd->resize_obj, "efl.draggable.vertical_bar", 0.0, posy); if (ctx->scroll_count) ctx->need_scroll = EINA_TRUE; else { efl_layout_signal_emit(wd->resize_obj, "efl,action,scroll", "efl"); ctx->scroll_count = EINA_TRUE; } } static void _scroll_connector_bar_show_cb(void *data, const Efl_Event *event) { Scroll_Connector_Context *ctx = data; ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); Efl_Ui_Layout_Orientation type = *(Efl_Ui_Layout_Orientation *)(event->info); if (type == EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL) efl_layout_signal_emit(wd->resize_obj, "efl,horizontal_bar,visible,on", "efl"); else if (type == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) efl_layout_signal_emit(wd->resize_obj, "efl,vertical_bar,visible,on", "efl"); } static void _scroll_connector_bar_hide_cb(void *data, const Efl_Event *event) { Scroll_Connector_Context *ctx = data; ELM_WIDGET_DATA_GET_OR_RETURN(ctx->obj, wd); Efl_Ui_Layout_Orientation type = *(Efl_Ui_Layout_Orientation *)(event->info); if (type == EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL) efl_layout_signal_emit(wd->resize_obj, "efl,horizontal_bar,visible,off", "efl"); else if (type == EFL_UI_LAYOUT_ORIENTATION_VERTICAL) efl_layout_signal_emit(wd->resize_obj, "efl,vertical_bar,visible,off", "efl"); } void efl_ui_scroll_connector_bind(Eo *obj, Eo *manager) { Scroll_Connector_Context *ctx = calloc(1, sizeof(Scroll_Connector_Context)); if (!ctx) return; ctx->obj = obj; ctx->smanager = manager; efl_key_data_set(obj, "__context", ctx); //from the theme to the object efl_layout_signal_callback_add(obj, "reload", "efl", ctx, _scroll_connector_reload_cb, NULL); efl_layout_signal_callback_add(obj, "drag", "efl.draggable.vertical_bar", ctx, _scroll_connector_vbar_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,set", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,start", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_start_cb, NULL); efl_layout_signal_callback_add(obj, "drag,stop", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_stop_cb, NULL); efl_layout_signal_callback_add(obj, "drag,step", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,page", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "efl,vertical_bar,press", "efl", ctx, _scroll_connector_vbar_press_cb, NULL); efl_layout_signal_callback_add(obj, "efl,vbar,unpress", "efl", ctx, _scroll_connector_vbar_unpress_cb, NULL); efl_layout_signal_callback_add(obj, "drag", "efl.draggable.horizontal_bar", ctx, _scroll_connector_hbar_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,set", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,start", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_start_cb, NULL); efl_layout_signal_callback_add(obj, "drag,stop", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_stop_cb, NULL); efl_layout_signal_callback_add(obj, "drag,step", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "drag,page", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_add(obj, "efl,horizontal_bar,press", "efl", ctx, _scroll_connector_hbar_press_cb, NULL); efl_layout_signal_callback_add(obj, "efl,hbar,unpress", "efl", ctx, _scroll_connector_hbar_unpress_cb, NULL); efl_layout_signal_callback_add(obj, "efl,action,scroll", "efl", ctx, _scroll, NULL); //from the object to the theme efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED, _scroll_connector_bar_size_changed_cb, ctx); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED, _scroll_connector_bar_pos_changed_cb, ctx); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, _scroll_connector_bar_show_cb, ctx); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE, _scroll_connector_bar_hide_cb, ctx); } void efl_ui_scroll_connector_unbind(Eo *obj) { Scroll_Connector_Context *ctx; ctx = efl_key_data_get(obj, "__context"); EINA_SAFETY_ON_NULL_RETURN(ctx); efl_layout_signal_callback_del(obj, "reload", "efl", ctx, _scroll_connector_reload_cb, NULL); efl_layout_signal_callback_del(obj, "drag", "efl.draggable.vertical_bar", ctx, _scroll_connector_vbar_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,set", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,start", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_start_cb, NULL); efl_layout_signal_callback_del(obj, "drag,stop", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_stop_cb, NULL); efl_layout_signal_callback_del(obj, "drag,step", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,page", "efl.draggable.vertical_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "efl,vertical_bar,press", "efl", ctx, _scroll_connector_vbar_press_cb, NULL); efl_layout_signal_callback_del(obj, "efl,vbar,unpress", "efl", ctx, _scroll_connector_vbar_unpress_cb, NULL); efl_layout_signal_callback_del(obj, "drag", "efl.draggable.horizontal_bar", ctx, _scroll_connector_hbar_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,set", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,start", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_start_cb, NULL); efl_layout_signal_callback_del(obj, "drag,stop", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_stop_cb, NULL); efl_layout_signal_callback_del(obj, "drag,step", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "drag,page", "efl.draggable.horizontal_bar", ctx, _scroll_connector_edje_drag_cb, NULL); efl_layout_signal_callback_del(obj, "efl,horizontal_bar,press", "efl", ctx, _scroll_connector_hbar_press_cb, NULL); efl_layout_signal_callback_del(obj, "efl,hbar,unpress", "efl", ctx, _scroll_connector_hbar_unpress_cb, NULL); free(ctx); } diff --git a/src/tests/elementary/efl_ui_test_list_collection.c b/src/tests/elementary/efl_ui_test_list_collection.c index 1d4f913901..94bafbd010 100644 --- a/src/tests/elementary/efl_ui_test_list_collection.c +++ b/src/tests/elementary/efl_ui_test_list_collection.c @@ -1,317 +1,317 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #include #include "efl_ui_suite.h" #include "efl_ui_test_collection_common.h" static Eo *win; static void item_container_setup() { Eo * list = efl_new(EFL_UI_POSITION_MANAGER_LIST_CLASS); position_manager = efl_new(EFL_UI_POSITION_MANAGER_LIST_CLASS); win = win_add(); item_container = efl_add(EFL_UI_COLLECTION_CLASS, win, efl_ui_collection_position_manager_set(efl_added, list)); efl_content_set(win, item_container); } static void item_container_teardown() { item_container = NULL; position_manager = NULL; win = NULL; } EFL_START_TEST(placement_test_only_items) { Eo *item[3]; for (int i = 0; i < 2; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(item_container, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } for (int i = 0; i < 3; ++i) { item[i] = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(item_container, item[i]); efl_gfx_hint_size_min_set(item[i], EINA_SIZE2D(40, 40)); } //now fill up to trigger the scrollbar to be visible for (int i = 0; i < 10; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(item_container, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } efl_gfx_entity_geometry_set(win, EINA_RECT(0, 0, 200, 200)); get_me_to_those_events(item_container); for (int i = 0; i < 3; ++i) { Eina_Rect r = efl_gfx_entity_geometry_get(item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+(i+2)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 80)); for (int i = 0; i < 3; ++i) { Eina_Rect r = efl_gfx_entity_geometry_get(item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+i*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } } EFL_END_TEST EFL_START_TEST(placement_test_group) { Eo *core_item[4]; Eo *group; Eina_Rect r; for (int i = 0; i < 1; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(item_container, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } core_item[0] = group = efl_add(EFL_UI_GROUP_ITEM_CLASS, item_container); efl_pack_end(item_container, group); efl_gfx_hint_size_min_set(group, EINA_SIZE2D(40, 40)); Eo **item = core_item+1; for (int i = 0; i < 3; ++i) { item[i] = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(group, item[i]); efl_gfx_hint_size_min_set(item[i], EINA_SIZE2D(40, 40)); } //now fill up to trigger the scrollbar to be visible for (int i = 0; i < 10; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(group, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } efl_gfx_entity_geometry_set(win, EINA_RECT(0, 0, 200, 200)); get_me_to_those_events(item_container); for (int i = 0; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+(i+1)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } // test when we have scrolled to the top of the group item - just testing normal scrolling efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 40)); for (int i = 0; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+i*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } //testing the placement in the middle of the item - group must already be warped efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 60)); r = efl_gfx_entity_geometry_get(core_item[0]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); for (int i = 1; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); - ck_assert_int_eq(r.y, 21+(i - 1)*40); + ck_assert_int_eq(r.y, 22+(i - 1)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } // testing if we have scrolled into the middle of the group the group item is not even in the calculated index anymore efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 120)); r = efl_gfx_entity_geometry_get(core_item[0]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); for (int i = 2; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); - ck_assert_int_eq(r.y, 1+(i-2)*40); + ck_assert_int_eq(r.y, 2+(i-2)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); } } EFL_END_TEST EFL_START_TEST(placement_test_group_crazy) { Eo *core_item[4]; Eo *group; Eina_Rect r; for (int i = 0; i < 1; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(item_container, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } core_item[0] = group = efl_add(EFL_UI_GROUP_ITEM_CLASS, item_container); efl_pack_end(item_container, group); efl_gfx_hint_size_min_set(group, EINA_SIZE2D(40, 40)); Eo **item = core_item+1; for (int i = 0; i < 3; ++i) { item[i] = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(group, item[i]); efl_gfx_hint_size_min_set(item[i], EINA_SIZE2D(40, 40)); } //now fill up to trigger the scrollbar to be visible for (int i = 0; i < 10; ++i) { Eo *group2 = efl_add(EFL_UI_GROUP_ITEM_CLASS, item_container); efl_pack_end(item_container, group2); efl_gfx_hint_size_min_set(group2, EINA_SIZE2D(40, 40)); for (int i = 0; i < 3; ++i) { Eo *item = efl_add(EFL_UI_LIST_DEFAULT_ITEM_CLASS, item_container); efl_pack_end(group2, item); efl_gfx_hint_size_min_set(item, EINA_SIZE2D(40, 40)); } } efl_gfx_entity_geometry_set(win, EINA_RECT(0, 0, 200, 800)); get_me_to_those_events(item_container); for (int i = 0; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+(i+1)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[i]), EINA_TRUE); } // test when we have scrolled to the top of the group item - just testing normal scrolling efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 40)); for (int i = 0; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+i*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[i]), EINA_TRUE); } //testing the placement in the middle of the item - group must already be warped efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 60)); r = efl_gfx_entity_geometry_get(core_item[0]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[0]), EINA_TRUE); for (int i = 1; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 21+(i - 1)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[i]), EINA_TRUE); } // testing if we have scrolled into the middle of the group the group item is not even in the calculated index anymore efl_ui_scrollable_content_pos_set(item_container, EINA_POSITION2D(0, 120)); r = efl_gfx_entity_geometry_get(core_item[0]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[0]), EINA_TRUE); for (int i = 2; i < 4; ++i) { r = efl_gfx_entity_geometry_get(core_item[i]); ck_assert_int_eq(r.x, 1); ck_assert_int_eq(r.y, 1+(i-2)*40); ck_assert_int_eq(r.w, 183); // 200 - 2px border - X for the width of the scrollbar. ck_assert_int_eq(r.h, 40); ck_assert_int_eq(efl_gfx_entity_visible_get(core_item[i]), EINA_TRUE); } } EFL_END_TEST void efl_ui_test_list_container(TCase *tc) { tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown); tcase_add_checked_fixture(tc, item_container_setup, item_container_teardown); efl_ui_test_item_container_common_add(tc); efl_ui_test_position_manager_common_add(tc); tcase_add_test(tc, placement_test_only_items); tcase_add_test(tc, placement_test_group); tcase_add_test(tc, placement_test_group_crazy); } diff --git a/src/tests/elementary/efl_ui_test_scroller.c b/src/tests/elementary/efl_ui_test_scroller.c index e1aa4ef53b..78f8bfaf7f 100644 --- a/src/tests/elementary/efl_ui_test_scroller.c +++ b/src/tests/elementary/efl_ui_test_scroller.c @@ -1,150 +1,201 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #include #include #include "efl_ui_suite.h" static void _startfinish_event_cb(void *data, const Efl_Event *ev EINA_UNUSED) { int *called = data; (*called)++; /* start/finish always come in pairs */ if (*called == 2) ecore_main_loop_quit(); } EFL_START_TEST(efl_ui_test_scroller_events) { Eo *sc, *sc2, *sc3, *bx, *bx2, *gd, *gd2; int i, j, called = 0, called2 = 0, called3 = 0; Eo *win = win_add(); efl_gfx_entity_size_set(win, EINA_SIZE2D(500, 500)); sc = efl_add(EFL_UI_SCROLLER_CLASS, win, efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_STARTED, _startfinish_event_cb, &called), efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_FINISHED, _startfinish_event_cb, &called), efl_gfx_entity_size_set(efl_added, EINA_SIZE2D(500, 500)) ); bx = efl_add(EFL_UI_BOX_CLASS, sc, efl_ui_layout_orientation_set(efl_added, EFL_UI_LAYOUT_ORIENTATION_VERTICAL), efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0), efl_gfx_hint_align_set(efl_added, 0.5, 0), efl_gfx_hint_fill_set(efl_added, EINA_TRUE, EINA_FALSE), efl_content_set(sc, efl_added)); efl_add(EFL_UI_SLIDER_CLASS, bx, efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(160, 0)), efl_pack(bx, efl_added)); for (i = 0; i < 3; i++) { efl_add(EFL_UI_BUTTON_CLASS, bx, efl_text_set(efl_added, "Vertical"), efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0), efl_gfx_hint_fill_set(efl_added, EINA_TRUE, EINA_FALSE), //efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _bt_clicked, NULL), efl_pack(bx, efl_added)); } sc2 = efl_add(EFL_UI_SCROLLER_CLASS, bx, efl_ui_scrollable_match_content_set(efl_added, EINA_FALSE, EINA_TRUE), efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_STARTED, _startfinish_event_cb, &called2), efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_FINISHED, _startfinish_event_cb, &called2), efl_pack(bx, efl_added)); bx2 = efl_add(EFL_UI_BOX_CLASS, sc2, efl_ui_layout_orientation_set(efl_added, EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL), efl_content_set(sc2, efl_added)); for (i = 0; i < 10; i++) { efl_add(EFL_UI_BUTTON_CLASS, bx2, efl_text_set(efl_added, "... Horizontal scrolling ..."), //efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _bt_clicked, NULL), efl_pack(bx2, efl_added)); } for (i = 0; i < 3; i++) { efl_add(EFL_UI_BUTTON_CLASS, bx, efl_text_set(efl_added, "Vertical"), efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0), efl_gfx_hint_fill_set(efl_added, EINA_TRUE, EINA_FALSE), //efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _bt_clicked, NULL), efl_pack(bx, efl_added)); } gd = efl_add(EFL_UI_TABLE_CLASS, bx, efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND), efl_gfx_hint_align_set(efl_added, 0.5, 0), efl_pack(bx, efl_added)); efl_add(EFL_CANVAS_RECTANGLE_CLASS, win, efl_gfx_color_set(efl_added, 0, 0, 0, 0), efl_gfx_hint_size_min_set(efl_added, EINA_SIZE2D(200, 120)), efl_pack_table(gd, efl_added, 0, 0, 1, 1)); sc3 = efl_add(EFL_UI_SCROLLER_CLASS, win, efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND), efl_gfx_hint_fill_set(efl_added, EINA_TRUE, EINA_TRUE), efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_STARTED, _startfinish_event_cb, &called3), efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_FINISHED, _startfinish_event_cb, &called3), efl_pack_table(gd, efl_added, 0, 0, 1, 1)); gd2 = efl_add(EFL_UI_TABLE_CLASS, sc3, efl_content_set(sc3, efl_added)); for (j = 0; j < 4; j++) { for (i = 0; i < 4; i++) { efl_add(EFL_UI_BUTTON_CLASS, win, efl_text_set(efl_added, "Both"), //efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _bt_clicked, NULL), efl_pack_table(gd2, efl_added, i, j, 1, 1)); } } for (i = 0; i < 10; i++) { efl_add(EFL_UI_BUTTON_CLASS, bx, efl_text_set(efl_added, "Vertical"), efl_gfx_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0), efl_gfx_hint_fill_set(efl_added, EINA_TRUE, EINA_FALSE), //efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _bt_clicked, NULL), efl_pack(bx, efl_added)); } get_me_to_those_events(sc); /* this should only trigger the inner scroller */ wheel_object(sc3, 0, 1); ecore_main_loop_begin(); ck_assert_int_eq(called3, 2); ck_assert_int_eq(called2, 0); ck_assert_int_eq(called, 0); called3 = 0; /* this should only trigger the horizontal scroller */ wheel_object(sc2, 1, 1); ecore_main_loop_begin(); ck_assert_int_eq(called3, 0); ck_assert_int_eq(called2, 2); ck_assert_int_eq(called, 0); called2 = 0; /* this should only trigger the outer scroller */ wheel_object_at(sc, 250, 400, 0, 1); ecore_main_loop_begin(); ck_assert_int_eq(called3, 0); ck_assert_int_eq(called2, 0); ck_assert_int_eq(called, 2); } EFL_END_TEST +EFL_START_TEST(efl_ui_test_scroller_scrollbar) +{ + Eo *sc; + + Eo *win = win_add(); + Eina_Bool hbar_visible = EINA_FALSE, vbar_visible = EINA_FALSE; + efl_gfx_entity_size_set(win, EINA_SIZE2D(500, 500)); + + sc = efl_add(EFL_UI_SCROLLER_CLASS, win, + efl_ui_scrollbar_bar_mode_set(efl_added, EFL_UI_SCROLLBAR_MODE_AUTO, EFL_UI_SCROLLBAR_MODE_AUTO), + efl_content_set(win, efl_added)); + + /*Scrollbar auto mode test.*/ + efl_loop_iterate(efl_main_loop_get()); + + efl_ui_scrollbar_bar_visibility_get(sc, &hbar_visible, &vbar_visible); + ck_assert(hbar_visible == EINA_FALSE); + ck_assert(vbar_visible == EINA_FALSE); + + /*Scrollbar auto mode test.*/ + efl_add(EFL_CANVAS_RECTANGLE_CLASS, evas_object_evas_get(sc), + efl_gfx_entity_size_set(efl_added, EINA_SIZE2D(5000, 5000)), + efl_content_set(sc, efl_added)); + + efl_loop_iterate(efl_main_loop_get()); + + efl_ui_scrollbar_bar_visibility_get(sc, &hbar_visible, &vbar_visible); + ck_assert(hbar_visible == EINA_TRUE); + ck_assert(vbar_visible == EINA_TRUE); + + /*Scrollbar off mode test.*/ + efl_ui_scrollbar_bar_mode_set(sc, EFL_UI_SCROLLBAR_MODE_OFF, EFL_UI_SCROLLBAR_MODE_OFF); + + efl_loop_iterate(efl_main_loop_get()); + + efl_ui_scrollbar_bar_visibility_get(sc, &hbar_visible, &vbar_visible); + ck_assert(hbar_visible == EINA_FALSE); + ck_assert(vbar_visible == EINA_FALSE); + + /*Scrollbar on mode test.*/ + efl_ui_scrollbar_bar_mode_set(sc, EFL_UI_SCROLLBAR_MODE_ON, EFL_UI_SCROLLBAR_MODE_ON); + + efl_loop_iterate(efl_main_loop_get()); + + efl_ui_scrollbar_bar_visibility_get(sc, &hbar_visible, &vbar_visible); + ck_assert(hbar_visible == EINA_TRUE); + ck_assert(vbar_visible == EINA_TRUE); +} +EFL_END_TEST + void efl_ui_test_scroller(TCase *tc) { tcase_add_test(tc, efl_ui_test_scroller_events); + tcase_add_test(tc, efl_ui_test_scroller_scrollbar); }