scroller: Introducing Efl.Ui.Scroller
authorWonki Kim <wonki_.kim@samsung.com>
Mon, 18 Dec 2017 12:08:25 +0000 (21:08 +0900)
committerWonki Kim <wonki_.kim@samsung.com>
Wed, 10 Jan 2018 11:08:13 +0000 (20:08 +0900)
Summary:
scrollable widgets had a interface_scrollable as a mixin so that the
widgets had a 'is-a' relation with interface_scrollabe.  however, new
scroller concept don't have 'is-a' relationship, but 'has-a'
relationship.  scrollable widgets should have a scroll manager inside
them, then scroll manager handles event from user and api
implementations.  and also we cut the features such as paging because
there will be aka 'elm_pager'.

we are expecting that the new concept make us to maintain the scroller
easier.  please excuse for many unorganized code and logics. : (

[contained commit]
scrollable: add efl_ui_scroller example
scrollable: refactoring for behavior in case of multiple scroller
scrollable: remove repetitive scrollbar code.
scrollable: combine calculating bounce distance code.
scroll_manager: mouse up function refactoring
scroll_manager: mouse move function refactoring
scroll_manager: warp animator wip
scroll_manager: fix denominator value when calculating flicking behavior.
Fix to disconnect bounce animator once animation is done
gather duplicated animator drop logics
gather duplicated conditions
Rearrange prototypes and append comment
Add manipulate functions for animators
scroll_manager: change member_add function.
scroll_manger: apply mirroring logic
scroll_manager: apply scrollbar
apply API to scroller widget
scroll_manager: apply scroll event callback
Change logics for all about scroll animating
efl_ui_pan: add efl_ui_pan
scrollable:  change content_min_limit to match_content
scroll theme: apply overlapped scrollbar
+ many others!

Reviewers: akanad, woohyun, cedric, jpeg

Subscribers: jenkins, cedric, jpeg

Differential Revision: https://phab.enlightenment.org/D5222

Note by @jpeg:
Unfortunately this patch comes in a massive single blob, after too many
rebase operations. It has now come to a point where I think the API is
nice and it works as I'd expect.
Now I only wonder how applicable this will be for Efl.Ui.List. As we can
see Photocam (legacy and unified API) could be transformed to use this
new API.

25 files changed:
src/Makefile_Efl.am
src/Makefile_Elementary.am
src/bin/elementary/test.c
src/bin/elementary/test_ui_scroller.c [new file with mode: 0644]
src/examples/elementary/efl_ui_scroller_example.c [new file with mode: 0644]
src/lib/efl/Efl.h
src/lib/efl/interfaces/efl_interfaces_main.c
src/lib/efl/interfaces/efl_ui_scrollable.eo
src/lib/efl/interfaces/efl_ui_scrollable_interactive.eo [new file with mode: 0644]
src/lib/efl/interfaces/efl_ui_scrollbar.eo [new file with mode: 0644]
src/lib/elementary/Elementary.h
src/lib/elementary/efl_ui.eot
src/lib/elementary/efl_ui_image_zoomable.c
src/lib/elementary/efl_ui_image_zoomable.eo
src/lib/elementary/efl_ui_image_zoomable_pan.eo
src/lib/elementary/efl_ui_image_zoomable_private.h
src/lib/elementary/efl_ui_pan.c [new file with mode: 0644]
src/lib/elementary/efl_ui_pan.eo [new file with mode: 0644]
src/lib/elementary/efl_ui_scroll_manager.c [new file with mode: 0644]
src/lib/elementary/efl_ui_scroll_manager.eo [new file with mode: 0644]
src/lib/elementary/efl_ui_scroller.c [new file with mode: 0644]
src/lib/elementary/efl_ui_scroller.eo [new file with mode: 0644]
src/lib/elementary/efl_ui_widget_pan.h [new file with mode: 0644]
src/lib/elementary/efl_ui_widget_scroll_manager.h [new file with mode: 0644]
src/lib/elementary/efl_ui_widget_scroller.h [new file with mode: 0644]

index 243a04e..1ab8eef 100644 (file)
@@ -8,6 +8,8 @@ efl_eolian_legacy_files = \
         lib/efl/interfaces/efl_ui_draggable.eo \
         lib/efl/interfaces/efl_ui_clickable.eo \
         lib/efl/interfaces/efl_ui_scrollable.eo \
+        lib/efl/interfaces/efl_ui_scrollable_interactive.eo \
+        lib/efl/interfaces/efl_ui_scrollbar.eo \
         lib/efl/interfaces/efl_ui_selectable.eo \
         lib/efl/interfaces/efl_ui_zoom.eo \
        $(NULL)
index 111d977..255787d 100644 (file)
@@ -82,6 +82,9 @@ elm_public_eolian_files = \
        lib/elementary/efl_ui_widget_part_bg.eo \
        lib/elementary/efl_ui_widget_part_shadow.eo \
        lib/elementary/efl_ui_win_part.eo \
+       lib/elementary/efl_ui_scroller.eo \
+       lib/elementary/efl_ui_scroll_manager.eo \
+       lib/elementary/efl_ui_pan.eo \
        lib/elementary/efl_access.eo \
        lib/elementary/efl_access_action.eo \
        lib/elementary/efl_access_component.eo \
@@ -362,6 +365,9 @@ includesunstable_HEADERS = \
        lib/elementary/elm_code_parse.h \
        lib/elementary/elm_code_syntax.h \
        lib/elementary/efl_ui_multibuttonentry.h \
+       lib/elementary/efl_ui_widget_scroller.h \
+       lib/elementary/efl_ui_widget_scroll_manager.h \
+       lib/elementary/efl_ui_widget_pan.h \
        lib/elementary/Efl_Ui.h
 includesunstabledir = $(includedir)/elementary-@VMAJ@
 
@@ -756,6 +762,9 @@ lib_elementary_libelementary_la_SOURCES = \
        lib/elementary/efl_ui_list_precise_layouter.c \
        lib/elementary/efl_ui_list_segarray.c \
        lib/elementary/efl_ui_layout_factory.c \
+       lib/elementary/efl_ui_scroller.c \
+       lib/elementary/efl_ui_scroll_manager.c \
+       lib/elementary/efl_ui_pan.c \
        $(NULL)
 
 
@@ -923,6 +932,7 @@ bin/elementary/test_progressbar.c \
 bin/elementary/test_radio.c \
 bin/elementary/test_scaling.c \
 bin/elementary/test_scroller.c \
+bin/elementary/test_ui_scroller.c \
 bin/elementary/test_segment_control.c \
 bin/elementary/test_separator.c \
 bin/elementary/test_slider.c \
index 7484a25..eb55913 100644 (file)
@@ -176,6 +176,8 @@ void test_scroller2(void *data, Evas_Object *obj, void *event_info);
 void test_scroller3(void *data, Evas_Object *obj, void *event_info);
 void test_scroller4(void *data, Evas_Object *obj, void *event_info);
 void test_scroller5(void *data, Evas_Object *obj, void *event_info);
+void test_efl_ui_scroller(void *data, Evas_Object *obj, void *event_info);
+void test_efl_ui_scroller2(void *data, Evas_Object *obj, void *event_info);
 void test_spinner(void *data, Evas_Object *obj, void *event_info);
 void test_ui_spin(void *data, Evas_Object *obj, void *event_info);
 void test_ui_spin_button(void *data, Evas_Object *obj, void *event_info);
@@ -1024,6 +1026,7 @@ add_tests:
    ADD_TEST(NULL, "Scroller", "Scroller 3", test_scroller3);
    ADD_TEST(NULL, "Scroller", "Page Scroller", test_scroller4);
    ADD_TEST(NULL, "Scroller", "Scroller on Popup", test_scroller5);
+   ADD_TEST_EO(NULL, "Scroller", "Efl.Ui.Scroller", test_efl_ui_scroller);
 
    //------------------------------//
    // FIXME: add frame test
diff --git a/src/bin/elementary/test_ui_scroller.c b/src/bin/elementary/test_ui_scroller.c
new file mode 100644 (file)
index 0000000..460ba41
--- /dev/null
@@ -0,0 +1,124 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+
+static void
+_bt_clicked(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   printf("click went through on %p\n", ev->object);
+}
+
+static void
+_scroll_start_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   printf("scroll start: %p\n", ev->object);
+}
+
+static void
+_scroll_stop_cb(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   printf("scroll stop: %p\n", ev->object);
+}
+
+void
+test_efl_ui_scroller(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Eo *win, *sc, *sc2, *sc3, *bx, *bx2, *gd, *gd2;
+   int i, j;
+
+   win = efl_add(EFL_UI_WIN_CLASS, NULL,
+                 efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC),
+                 efl_text_set(efl_added, "Efl Ui Scroller"),
+                 efl_ui_win_autodel_set(efl_added, EINA_TRUE));
+   efl_gfx_size_set(win, EINA_SIZE2D(320, 400));
+
+   sc = efl_add(EFL_UI_SCROLLER_CLASS, win,
+                efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND),
+                efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_START, _scroll_start_cb, NULL),
+                efl_event_callback_add(efl_added, EFL_UI_EVENT_SCROLL_STOP, _scroll_stop_cb, NULL),
+                efl_content_set(win, efl_added));
+
+   bx = efl_add(EFL_UI_BOX_CLASS, sc,
+                efl_ui_direction_set(efl_added, EFL_UI_DIR_DOWN),
+                efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0),
+                efl_gfx_size_hint_align_set(efl_added, EVAS_HINT_FILL, 0),
+                efl_content_set(sc, efl_added));
+
+
+   for (i = 0; i < 3; i++)
+      {
+        efl_add(EFL_UI_BUTTON_CLASS, bx,
+                efl_text_set(efl_added, "Vertical"),
+                efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0),
+                efl_gfx_size_hint_align_set(efl_added, EVAS_HINT_FILL, 0.5),
+                efl_event_callback_add(efl_added, EFL_UI_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_pack(bx, efl_added));
+
+   bx2 = efl_add(EFL_UI_BOX_CLASS, sc2,
+                 efl_ui_direction_set(efl_added, EFL_UI_DIR_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_UI_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_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0),
+                efl_gfx_size_hint_align_set(efl_added, EVAS_HINT_FILL, 0.5),
+                efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED, _bt_clicked, NULL),
+                efl_pack(bx, efl_added));
+      }
+
+   gd = efl_add(EFL_UI_GRID_CLASS, bx,
+                efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND),
+                efl_gfx_size_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_size_hint_min_set(efl_added, EINA_SIZE2D(200, 120)),
+           efl_pack_grid(gd, efl_added, 0, 0, 1, 1));
+
+   sc3 = efl_add(EFL_UI_SCROLLER_CLASS, win,
+                 efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND),
+                 efl_gfx_size_hint_align_set(efl_added, EVAS_HINT_FILL, EVAS_HINT_FILL),
+                 efl_pack_grid(gd, efl_added, 0, 0, 1, 1));
+
+   gd2 = efl_add(EFL_UI_GRID_CLASS, sc3,
+                 efl_content_set(sc3, efl_added));
+
+   for (j = 0; j < 16; j++)
+     {
+        for (i = 0; i < 16; i++)
+          {
+             efl_add(EFL_UI_BUTTON_CLASS, win,
+                     efl_text_set(efl_added, "Both"),
+                     efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED, _bt_clicked, NULL),
+                     efl_pack_grid(gd2, efl_added, i, j, 1, 1));
+          }
+     }
+
+   for (i = 0; i < 200; i++)
+      {
+        efl_add(EFL_UI_BUTTON_CLASS, bx,
+                efl_text_set(efl_added, "Vertical"),
+                efl_gfx_size_hint_weight_set(efl_added, EVAS_HINT_EXPAND, 0.0),
+                efl_gfx_size_hint_align_set(efl_added, EVAS_HINT_FILL, 0.5),
+                efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED, _bt_clicked, NULL),
+                efl_pack(bx, efl_added));
+      }
+}
diff --git a/src/examples/elementary/efl_ui_scroller_example.c b/src/examples/elementary/efl_ui_scroller_example.c
new file mode 100644 (file)
index 0000000..b09756c
--- /dev/null
@@ -0,0 +1,31 @@
+//Compile with:
+//gcc -g efl_ui_scroller_example.c -o efl_ui_scroller_example `pkg-config --cflags --libs elementary`
+
+#define EFL_BETA_API_SUPPORT
+#define EFL_EO_API_SUPPORT
+
+#include <Elementary.h>
+
+EAPI_MAIN void
+efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   Eo *win, *scroller, *content;
+   char buf[64];
+
+   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+   elm_app_info_set(efl_main, "elementary", "images/plant_01.jpg");
+
+   win = efl_add(EFL_UI_WIN_CLASS, NULL, "TEST", ELM_WIN_BASIC,
+                        efl_ui_win_autodel_set(efl_added, EINA_TRUE));
+   efl_gfx_size_set(win, EINA_SIZE2D(300, 400));
+
+   scroller = efl_add(EFL_UI_SCROLLER_CLASS, win);
+   efl_content_set(win, scroller);
+
+   content = efl_add(EFL_UI_IMAGE_CLASS, scroller);
+   snprintf(buf, sizeof(buf), "%s/images/plant_01.jpg", elm_app_data_dir_get());
+   efl_file_set(content, buf, NULL);
+   efl_gfx_size_set(content, EINA_SIZE2D(5000, 5000));
+   efl_content_set(scroller, content);
+}
+EFL_MAIN()
index eb9e5f1..263e991 100644 (file)
@@ -98,6 +98,8 @@ typedef Efl_Gfx_Path_Command_Type Efl_Gfx_Path_Command;
 #include "interfaces/efl_ui_draggable.eo.h"
 #include "interfaces/efl_ui_clickable.eo.h"
 #include "interfaces/efl_ui_scrollable.eo.h"
+#include "interfaces/efl_ui_scrollbar.eo.h"
+#include "interfaces/efl_ui_scrollable_interactive.eo.h"
 #include "interfaces/efl_ui_selectable.eo.h"
 #include "interfaces/efl_ui_zoom.eo.h"
 
index 3f19f05..ec7366c 100644 (file)
@@ -3,6 +3,8 @@
 #endif
 
 #define EFL_CANVAS_BETA
+#define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_SCROLLBAR_BETA
 
 #include <Efl.h>
 
@@ -75,6 +77,8 @@
 #include "interfaces/efl_ui_draggable.eo.c"
 #include "interfaces/efl_ui_clickable.eo.c"
 #include "interfaces/efl_ui_scrollable.eo.c"
+#include "interfaces/efl_ui_scrollable_interactive.eo.c"
+#include "interfaces/efl_ui_scrollbar.eo.c"
 #include "interfaces/efl_ui_selectable.eo.c"
 #include "interfaces/efl_ui_zoom.eo.c"
 
index 366e2c0..2031206 100644 (file)
@@ -1,9 +1,33 @@
+enum Efl.Ui.Scroll_Block
+{
+   [[Direction in which a scroller should be blocked.
+
+     Note: These options may be effective only in case of thumbscroll (i.e.
+     when scrolling by dragging).
+
+     @since 1.21
+   ]]
+   none = 0,      [[Don't block any movement.]]
+   vertical = 1,  [[Block vertical movement.]]
+   horizontal = 2 [[Block horizontal movement.]]
+}
+
 interface Efl.Ui.Scrollable ()
 {
    [[Efl UI scrollable interface]]
    event_prefix: efl_ui;
    events {
-      scroll; [[Called when scroll operation started]]
+      scroll,start; [[Called when scroll operation started]]
+      scroll; [[Called when scroll operation]]
+      scroll,stop; [[Called when scroll operation stopped]]
+      scroll,up; [[Called when scrolling to upwards]]
+      scroll,down; [[Called when scrolling to downwards]]
+      scroll,left; [[Called when scrolling to left]]
+      scroll,right; [[Called when scrolling to right]]
+      edge,up; [[Called when hitting the top edge]]
+      edge,down; [[Called when hitting the bottom edge]]
+      edge,left; [[Called when hitting the left edge]]
+      edge,right; [[Called when hitting the right edge]]
       scroll,anim,start; [[Called when scroll animation started]]
       scroll,anim,stop; [[Called when scroll animation stopped]]
       scroll,drag,start; [[Called when scroll drag started]]
diff --git a/src/lib/efl/interfaces/efl_ui_scrollable_interactive.eo b/src/lib/efl/interfaces/efl_ui_scrollable_interactive.eo
new file mode 100644 (file)
index 0000000..f25192e
--- /dev/null
@@ -0,0 +1,156 @@
+import eina_types;
+
+interface Efl.Ui.Scrollable.Interactive (Efl.Ui.Scrollable)
+{
+   eo_prefix: efl_ui_scrollable;
+   methods {
+      @property content_pos {
+         [[The content position]]
+         set {
+         }
+         get {
+         }
+         values {
+            pos: Eina.Position2D; [[The position is virtual value, (0, 0) starting at the top-left.]]
+         }
+      }
+      @property content_size {
+         [[The content size]]
+         get {
+         }
+         values {
+            size: Eina.Size2D; [[The content size in pixels.]]
+         }
+      }
+      @property viewport_geometry {
+         [[The viewport geometry]]
+         get {
+         }
+         values {
+            rect: Eina.Rect; [[It is absolute geometry.]]
+         }
+      }
+      @property bounce_enabled {
+         [[Bouncing behavior
+
+           When scrolling, the scroller may "bounce" when reaching an edge of the
+           content object. This is a visual way to indicate the end has been reached.
+           This is enabled by default for both axis. This API will set if it is enabled
+           for the given axis with the boolean parameters for each axis.]]
+         set {
+         }
+         get {
+         }
+         values {
+            horiz: bool; [[Horizontal bounce policy.]]
+            vert: bool; [[Vertical bounce policy.]]
+         }
+      }
+      @property scroll_freeze {
+         [[Freeze property
+           This function will freeze scrolling movement (by input of a user).
+           Unlike efl_ui_scrollable_movement_block_set, this function freezes bidirectional.
+           If you want to freeze only one direction,
+           See @.movement_block.set.
+         ]]
+         get {
+         }
+         set {
+         }
+         values {
+            freeze: bool; [[$true if freeze, $false otherwise]]
+         }
+      }
+      @property scroll_hold {
+         [[Hold property
+           When hold turns on, it only scrolls by holding action.
+         ]]
+         get {
+         }
+         set {
+         }
+         values {
+            hold: bool; [[$true if hold, $false otherwise]]
+         }
+      }
+      @property looping {
+         [[Controls an infinite loop for a scroller.]]
+         set {
+         }
+         get {
+         }
+         values {
+            loop_h: bool; [[The scrolling horizontal loop]]
+            loop_v: bool; [[The Scrolling vertical loop]]
+         }
+      }
+      @property movement_block {
+         [[Blocking of scrolling (per axis)
+
+           This function will block scrolling movement (by input of a user) in
+           a given direction. One can disable movements in the X axis, the Y
+           axis or both. The default value is $none, where movements are
+           allowed in both directions.
+         ]]
+         set {
+         }
+         get {
+         }
+         values {
+            block: Efl.Ui.Scroll_Block(Efl.Ui.Scroll_Block.none); [[Which axis (or axes) to block]]
+         }
+      }
+      @property gravity {
+         [[Control scrolling gravity on the scrollable
+
+           The gravity defines how the scroller will adjust its view
+           when the size of the scroller contents increases.
+
+           The scroller will adjust the view to glue itself as follows.
+
+            x=0.0, for staying where it is relative to the left edge of the content
+            x=1.0, for staying where it is relative to the rigth edge of the content
+            y=0.0, for staying where it is relative to the top edge of the content
+            y=1.0, for staying where it is relative to the bottom edge of the content
+
+           Default values for x and y are 0.0]]
+         set {
+         }
+         get {
+         }
+         values {
+            x: double; [[Horizontal scrolling gravity]]
+            y: double; [[Vertical scrolling gravity]]
+         }
+      }
+      @property match_content {
+         [[Prevent the scrollable from being smaller than the minimum size of the content.
+
+           By default the scroller will be as small as its design allows,
+           irrespective of its content. This will make the scroller minimum size the
+           right size horizontally and/or vertically to perfectly fit its content in
+           that direction.]]
+         set {
+         }
+         values {
+            w: bool; [[Whether to limit the minimum horizontal size]]
+            h: bool; [[Whether to limit the minimum vertical size]]
+         }
+      }
+      scroll {
+         [[Show a specific virtual region within the scroller content object.
+
+           This will ensure all (or part if it does not fit) of the designated
+           region in the virtual content object (0, 0 starting at the top-left of the
+           virtual content object) is shown within the scroller. This allows the scroller to "smoothly slide"
+           to this location (if configuration in general calls for transitions). It
+           may not jump immediately to the new location and make take a while and
+           show other content along the way.
+           ]]
+         params {
+            @in rect: Eina.Rect; [[The position where to scroll. and The size user want to see]]
+            @in animation: bool; [[Whether to scroll with animation or not]]
+         }
+      }
+   }
+}
diff --git a/src/lib/efl/interfaces/efl_ui_scrollbar.eo b/src/lib/efl/interfaces/efl_ui_scrollbar.eo
new file mode 100644 (file)
index 0000000..a539e82
--- /dev/null
@@ -0,0 +1,70 @@
+enum Efl.Ui.Scrollbar_Mode
+{
+   auto = 0, [[Visible if necessary]]
+   on, [[Always visible]]
+   off, [[Always invisible]]
+   last [[]]
+}
+
+enum Efl.Ui.Scrollbar_Direction
+{
+   horizontal = 0,
+   vertical,
+   last
+}
+interface Efl.Ui.Scrollbar ()
+{
+   methods {
+      @property bar_mode {
+         [[Scrollbar visibility policy]]
+         set {
+         }
+         get {
+         }
+         values {
+            hbar: Efl.Ui.Scrollbar_Mode; [[Horizontal scrollbar]]
+            vbar: Efl.Ui.Scrollbar_Mode; [[Vertical scrollbar]]
+         }
+      }
+      @property bar_size {
+         [[Scrollbar size.
+           It is calculated based on viewport size-content sizes.
+         ]]
+         get {
+         }
+         values {
+            width: double; [[Value between 0.0 and 1.0]]
+            height: double; [[Value between 0.0 and 1.0]]
+         }
+      }
+      @property bar_position {
+         [[Scrollbar position.
+           It is calculated based on current position-maximum positions.
+         ]]
+         set {
+         }
+         get {
+         }
+         values {
+            posx: double; [[Value between 0.0 and 1.0]]
+            posy: double; [[Value between 0.0 and 1.0]]
+         }
+      }
+      bar_visibility_update @protected @beta{
+         [[ Update bar visibility.
+
+            The object will call this function whenever the bar
+            need to be shown or hidden.
+         ]]
+      }
+   }
+   events {
+      bar,press; [[Called when bar is pressed]]
+      bar,unpress; [[Called when bar is unpressed]]
+      bar,drag; [[Called when bar is dragged]]
+      bar,size,changed; [[Called when bar size is changed]]
+      bar,pos,changed; [[Called when bar position is changed]]
+      bar,show; [[Callend when bar is shown]]
+      bar,hide; [[Called when bar is hidden]]
+   }
+}
index 361b2cd..d99fe21 100644 (file)
@@ -328,6 +328,9 @@ EAPI extern Elm_Version *elm_version;
 # include <efl_ui_list_relayout.eo.h>
 # include <efl_ui_list.eo.h>
 # include <efl_ui_list_pan.eo.h>
+# include <efl_ui_scroll_manager.eo.h>
+# include <efl_ui_scroller.eo.h>
+# include <efl_ui_pan.eo.h>
 #endif
 
 /* include deprecated calls last of all */
index f3efc84..e891040 100644 (file)
@@ -76,20 +76,6 @@ enum Efl.Ui.Softcursor_Mode
    off   [[Never use a softcursor.]]
 }
 
-enum Efl.Ui.Scroll_Block
-{
-   [[Direction in which a scroller should be blocked.
-
-     Note: These options may be effective only in case of thumbscroll (i.e.
-     when scrolling by dragging).
-
-     @since 1.21
-   ]]
-   none = 0,      [[Don't block any movement.]]
-   vertical = 1,  [[Block vertical movement.]]
-   horizontal = 2 [[Block horizontal movement.]]
-}
-
 /* 'on_access_activate' is beta API in the Widget class */
 enum Efl.Ui.Activate
 {
index 6f758b5..d1c7c11 100644 (file)
@@ -4,12 +4,14 @@
 
 #define EFL_ACCESS_PROTECTED
 #define EFL_ACCESS_WIDGET_ACTION_PROTECTED
+#define EFL_UI_SCROLL_MANAGER_PROTECTED
+#define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_SCROLLBAR_BETA
 
 #include <Elementary.h>
 
 #include "elm_priv.h"
 #include "efl_ui_image_zoomable_private.h"
-#include "elm_interface_scrollable.h"
 
 #define MY_PAN_CLASS EFL_UI_IMAGE_ZOOMABLE_PAN_CLASS
 
@@ -143,7 +145,7 @@ _calc_job_cb(void *data)
         sd->minw = minw;
         sd->minh = minh;
 
-        efl_event_callback_legacy_call(sd->pan_obj, ELM_PAN_EVENT_CHANGED, NULL);
+        efl_event_callback_call(sd->pan_obj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, NULL);
         _sizing_eval(obj);
      }
    sd->calc_job = NULL;
@@ -210,11 +212,11 @@ _image_place(Evas_Object *obj,
    evas_object_move(sd->img, ox + 0 - px + ax, oy + 0 - py + ay);
    evas_object_resize(sd->img, gw, gh);
 
-   if (sd->show.show)
+   if (sd->show_item)
      {
-        sd->show.show = EINA_FALSE;
-        elm_interface_scrollable_content_region_show
-              (obj, sd->show.x, sd->show.y, sd->show.w, sd->show.h);
+        sd->show_item = EINA_FALSE;
+        efl_ui_scrollable_scroll
+              (sd->smanager, sd->show, EINA_FALSE);
      }
 }
 
@@ -391,23 +393,24 @@ _efl_ui_image_zoomable_pan_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Imag
 }
 
 EOLIAN static void
-_efl_ui_image_zoomable_pan_elm_pan_pos_set(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Evas_Coord x, Evas_Coord y)
+_efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Eina_Position2D pos)
 {
-   if ((x == psd->wsd->pan_x) && (y == psd->wsd->pan_y)) return;
-   psd->wsd->pan_x = x;
-   psd->wsd->pan_y = y;
+   if ((pos.x == psd->wsd->pan_x) && (pos.y == psd->wsd->pan_y)) return;
+   psd->wsd->pan_x = pos.x;
+   psd->wsd->pan_y = pos.y;
    evas_object_smart_changed(obj);
+
+   efl_event_callback_call(obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, NULL);
 }
 
-EOLIAN static void
-_efl_ui_image_zoomable_pan_elm_pan_pos_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
+EOLIAN static Eina_Position2D
+_efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd)
 {
-   if (x) *x = psd->wsd->pan_x;
-   if (y) *y = psd->wsd->pan_y;
+   return EINA_POSITION2D(psd->wsd->pan_x, psd->wsd->pan_y);
 }
 
-EOLIAN static void
-_efl_ui_image_zoomable_pan_elm_pan_pos_max_get(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
+EOLIAN static Eina_Position2D
+_efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_max_get(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd)
 {
    Evas_Coord ow, oh;
 
@@ -416,28 +419,27 @@ _efl_ui_image_zoomable_pan_elm_pan_pos_max_get(Eo *obj, Efl_Ui_Image_Zoomable_Pa
    if (ow < 0) ow = 0;
    oh = psd->wsd->minh - oh;
    if (oh < 0) oh = 0;
-   if (x) *x = ow;
-   if (y) *y = oh;
+
+   return EINA_POSITION2D(ow, oh);
 }
 
-EOLIAN static void
-_efl_ui_image_zoomable_pan_elm_pan_pos_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *_pd EINA_UNUSED, Evas_Coord *x, Evas_Coord *y)
+EOLIAN static Eina_Position2D
+_efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *_pd EINA_UNUSED)
 {
-   if (x) *x = 0;
-   if (y) *y = 0;
+   return EINA_POSITION2D(0, 0);
 }
 
-EOLIAN static void
-_efl_ui_image_zoomable_pan_elm_pan_content_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd, Evas_Coord *w, Evas_Coord *h)
+EOLIAN static Eina_Size2D
+_efl_ui_image_zoomable_pan_efl_ui_pan_content_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd)
 {
-   if (w) *w = psd->wsd->minw;
-   if (h) *h = psd->wsd->minh;
+   return EINA_SIZE2D(psd->wsd->minw, psd->wsd->minh);
 }
 
 EOLIAN static void
 _efl_ui_image_zoomable_pan_efl_object_destructor(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd)
 {
    efl_data_unref(psd->wobj, psd->wsd);
+
    efl_destructor(efl_super(obj, MY_PAN_CLASS));
 }
 
@@ -738,28 +740,28 @@ static Eina_Bool
 _zoom_do(Evas_Object *obj,
          double t)
 {
-   Evas_Coord xx, yy, ow = 0, oh = 0;
+   Evas_Coord xx, yy;
+   Eina_Rect view = {};
 
    EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
 
    sd->size.w = (sd->size.ow * (1.0 - t)) + (sd->size.nw * t);
    sd->size.h = (sd->size.oh * (1.0 - t)) + (sd->size.nh * t);
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, &ow, &oh);
-   xx = (sd->size.spos.x * sd->size.w) - (ow / 2);
-   yy = (sd->size.spos.y * sd->size.h) - (oh / 2);
+   view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
+   xx = (sd->size.spos.x * sd->size.w) - (view.w / 2);
+   yy = (sd->size.spos.y * sd->size.h) - (view.h / 2);
    if (xx < 0) xx = 0;
-   else if (xx > (sd->size.w - ow))
-     xx = sd->size.w - ow;
+   else if (xx > (sd->size.w - view.w))
+     xx = sd->size.w - view.w;
    if (yy < 0) yy = 0;
-   else if (yy > (sd->size.h - oh))
-     yy = sd->size.h - oh;
+   else if (yy > (sd->size.h - view.h))
+     yy = sd->size.h - view.h;
 
-   sd->show.show = EINA_TRUE;
+   sd->show_item = EINA_TRUE;
    sd->show.x = xx;
    sd->show.y = yy;
-   sd->show.w = ow;
-   sd->show.h = oh;
+   sd->show.w = view.w;
+   sd->show.h = view.h;
 
    if (sd->orientation_changed)
      {
@@ -899,7 +901,7 @@ _efl_ui_image_zoomable_elm_widget_on_focus_update(Eo *obj, Efl_Ui_Image_Zoomable
 }
 
 EOLIAN static Efl_Ui_Theme_Apply
-_efl_ui_image_zoomable_elm_widget_theme_apply(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED)
+_efl_ui_image_zoomable_elm_widget_theme_apply(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd)
 {
    Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED;
    Eina_Bool fdo = EINA_FALSE;
@@ -910,47 +912,18 @@ _efl_ui_image_zoomable_elm_widget_theme_apply(Eo *obj, Efl_Ui_Image_Zoomable_Dat
    int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
    if (!int_ret) return EFL_UI_THEME_APPLY_FAILED;
 
+   efl_ui_mirrored_set(sd->smanager, efl_ui_mirrored_get(obj));
+
    _sizing_eval(obj);
 
    return int_ret;
 }
 
 static void
-_scroll_animate_start_cb(Evas_Object *obj,
-                         void *data EINA_UNUSED)
-{
-   efl_event_callback_legacy_call
-     (obj, EFL_UI_EVENT_SCROLL_ANIM_START, NULL);
-}
-
-static void
-_scroll_animate_stop_cb(Evas_Object *obj,
-                        void *data EINA_UNUSED)
-{
-   efl_event_callback_legacy_call
-     (obj, EFL_UI_EVENT_SCROLL_ANIM_STOP, NULL);
-}
-
-static void
-_scroll_drag_start_cb(Evas_Object *obj,
-                      void *data EINA_UNUSED)
-{
-   efl_event_callback_legacy_call
-     (obj, EFL_UI_EVENT_SCROLL_DRAG_START, NULL);
-}
-
-static void
-_scroll_drag_stop_cb(Evas_Object *obj,
-                     void *data EINA_UNUSED)
-{
-   efl_event_callback_legacy_call
-     (obj, EFL_UI_EVENT_SCROLL_DRAG_STOP, NULL);
-}
-
-static void
-_scroll_cb(Evas_Object *obj,
-           void *data EINA_UNUSED)
+_scroll_cb(void * data,
+           const Efl_Event *event EINA_UNUSED)
 {
+   Evas_Object *obj = data;
    EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
 
    if (!sd->scr_timer)
@@ -961,63 +934,56 @@ _scroll_cb(Evas_Object *obj,
 
    ecore_timer_del(sd->scr_timer);
    sd->scr_timer = ecore_timer_add(0.5, _scroll_timeout_cb, obj);
-
-   efl_event_callback_legacy_call
-     (obj, EFL_UI_EVENT_SCROLL, NULL);
 }
 
 static Eina_Bool
 _key_action_move(Evas_Object *obj, const char *params)
 {
+   Eina_Rect view = {};
+   Eina_Position2D pos = {};
    const char *dir = params;
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
 
-   Evas_Coord x = 0;
-   Evas_Coord y = 0;
-   Evas_Coord v_h = 0;
    Evas_Coord step_x = 0;
    Evas_Coord step_y = 0;
-   Evas_Coord page_x = 0;
    Evas_Coord page_y = 0;
 
-   elm_interface_scrollable_content_pos_get(obj, &x, &y);
-   elm_interface_scrollable_step_size_get(obj, &step_x, &step_y);
-   elm_interface_scrollable_page_size_get(obj, &page_x, &page_y);
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, NULL, &v_h);
+   pos = efl_ui_scrollable_content_pos_get(sd->smanager);
+   view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
 
    if (!strcmp(dir, "left"))
      {
-        x -= step_x;
+        pos.x -= step_x;
      }
    else if (!strcmp(dir, "right"))
      {
-        x += step_x;
+        pos.x += step_x;
      }
    else if (!strcmp(dir, "up"))
      {
-        y -= step_y;
+        pos.y -= step_y;
      }
    else if (!strcmp(dir, "down"))
      {
-        y += step_y;
+        pos.y += step_y;
      }
    else if (!strcmp(dir, "prior"))
      {
         if (page_y < 0)
-          y -= -(page_y * v_h) / 100;
+          pos.y -= -(page_y * view.h) / 100;
         else
-          y -= page_y;
+          pos.y -= page_y;
      }
    else if (!strcmp(dir, "next"))
      {
         if (page_y < 0)
-          y += -(page_y * v_h) / 100;
+          pos.y += -(page_y * view.h) / 100;
         else
-          y += page_y;
+          pos.y += page_y;
      }
    else return EINA_FALSE;
 
-   elm_interface_scrollable_content_pos_set(obj, x, y, EINA_TRUE);
+   efl_ui_scrollable_content_pos_set(sd->smanager, pos);
    return EINA_TRUE;
 }
 
@@ -1089,7 +1055,7 @@ _bounce_eval(void *data, const Efl_Event *event EINA_UNUSED)
    sd->g_layer_zoom.imy = 0;
    sd->zoom_g_layer = EINA_FALSE;
 
-   elm_interface_scrollable_freeze_set(obj, EINA_FALSE);
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_FALSE);
 
    efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj);
 }
@@ -1125,18 +1091,16 @@ _g_layer_zoom_do(Evas_Object *obj,
                  Elm_Gesture_Zoom_Info *g_layer)
 {
    int regx, regy, regw, regh, ix, iy, iw, ih;
-   Evas_Coord rx, ry, rw = 0, rh = 0;
    int xx, yy;
+   Eina_Rect view = {};
 
    EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
    sd->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL;
    sd->zoom = sd->g_layer_start / g_layer->zoom;
    sd->size.ow = sd->size.w;
    sd->size.oh = sd->size.h;
-   elm_interface_scrollable_content_pos_get(obj, &rx, &ry);
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, &rw, &rh);
-   if ((rw <= 0) || (rh <= 0)) return;
+   view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
+   if ((view.w <= 0) || (view.h <= 0)) return;
 
    sd->size.nw = (double)sd->size.imw / sd->zoom;
    sd->size.nh = (double)sd->size.imh / sd->zoom;
@@ -1152,30 +1116,30 @@ _g_layer_zoom_do(Evas_Object *obj,
    sd->g_layer_zoom.imx = 0;
    sd->g_layer_zoom.imy = 0;
 
-   if ((xx < 0) || (rw > sd->size.nw))
+   if ((xx < 0) || (view.w > sd->size.nw))
      {
         sd->g_layer_zoom.imx = xx;
         xx = 0;
      }
-   else if ((xx + rw) > sd->size.nw)
+   else if ((xx + view.w) > sd->size.nw)
      {
-        sd->g_layer_zoom.imx = xx + rw - sd->size.nw;
-        xx = sd->size.nw - rw;
+        sd->g_layer_zoom.imx = xx + view.w - sd->size.nw;
+        xx = sd->size.nw - view.w;
      }
 
-   if ((yy < 0) || (rh > sd->size.nh))
+   if ((yy < 0) || (view.h > sd->size.nh))
      {
         sd->g_layer_zoom.imy = yy;
         yy = 0;
      }
-   else if ((yy + rh) > sd->size.nh)
+   else if ((yy + view.h) > sd->size.nh)
      {
-        sd->g_layer_zoom.imy = yy + rh - sd->size.nh;
-        yy = sd->size.nh - rh;
+        sd->g_layer_zoom.imy = yy + view.h - sd->size.nh;
+        yy = sd->size.nh - view.h;
      }
 
-   sd->size.spos.x = (double)(xx + (rw / 2)) / (double)(sd->size.nw);
-   sd->size.spos.y = (double)(yy + (rh / 2)) / (double)(sd->size.nh);
+   sd->size.spos.x = (double)(xx + (view.w / 2)) / (double)(sd->size.nw);
+   sd->size.spos.y = (double)(yy + (view.h / 2)) / (double)(sd->size.nh);
 
    _zoom_do(obj, 1.0);
 }
@@ -1188,22 +1152,21 @@ _g_layer_zoom_start_cb(void *data,
    Elm_Gesture_Zoom_Info *p = event_info;
    EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
    double marginx = 0, marginy = 0;
-   Evas_Coord rw = 0, rh = 0;
    int x, y, w, h;
+   Eina_Rect view = {};
 
    _efl_ui_image_zoomable_bounce_reset(obj, sd);
    sd->zoom_g_layer = EINA_TRUE;
 
-   elm_interface_scrollable_freeze_set(obj, EINA_TRUE);
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_TRUE);
 
    elm_photocam_image_region_get(obj, &x, &y, &w, &h);
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, &rw, &rh);
+   view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
 
-   if (rw > sd->size.nw)
-     marginx = (rw - sd->size.nw) / 2;
-   if (rh > sd->size.nh)
-     marginy = (rh - sd->size.nh) / 2;
+   if (view.w > sd->size.nw)
+     marginx = (view.w - sd->size.nw) / 2;
+   if (view.h > sd->size.nh)
+     marginy = (view.h - sd->size.nh) / 2;
 
    sd->g_layer_start = sd->zoom;
 
@@ -1233,10 +1196,8 @@ _g_layer_zoom_end_cb(void *data,
 {
    Evas_Object *obj = data;
    EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
-   Evas_Coord rw, rh;
 
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, &rw, &rh);
+   Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
    sd->g_layer_start = 1.0;
 
    if (sd->g_layer_zoom.imx || sd->g_layer_zoom.imy)
@@ -1249,13 +1210,13 @@ _g_layer_zoom_end_cb(void *data,
         sd->g_layer_zoom.bounce.x_end = 0;
         sd->g_layer_zoom.bounce.y_end = 0;
 
-        if (rw > sd->size.nw &&
-            rh > sd->size.nh)
+        if (view.w > sd->size.nw &&
+            view.h > sd->size.nh)
           {
              Evas_Coord pw, ph;
              double z;
 
-             if ((sd->size.imw < rw) && (sd->size.imh < rh))
+             if ((sd->size.imw < view.w) && (sd->size.imh < view.h))
                {
                   sd->zoom = 1;
                   sd->size.nw = sd->size.imw;
@@ -1263,15 +1224,15 @@ _g_layer_zoom_end_cb(void *data,
                }
              else
                {
-                  ph = (sd->size.imh * rw) / sd->size.imw;
-                  if (ph > rh)
+                  ph = (sd->size.imh * view.w) / sd->size.imw;
+                  if (ph > view.h)
                     {
-                       pw = (sd->size.imw * rh) / sd->size.imh;
-                       ph = rh;
+                       pw = (sd->size.imw * view.h) / sd->size.imh;
+                       ph = view.h;
                     }
                   else
                     {
-                       pw = rw;
+                       pw = view.w;
                     }
                   if (sd->size.imw > sd->size.imh)
                     z = (double)sd->size.imw / pw;
@@ -1282,8 +1243,8 @@ _g_layer_zoom_end_cb(void *data,
                   sd->size.nw = pw;
                   sd->size.nh = ph;
                }
-             sd->g_layer_zoom.bounce.x_end = (sd->size.nw - rw) / 2;
-             sd->g_layer_zoom.bounce.y_end = (sd->size.nh - rh) / 2;
+             sd->g_layer_zoom.bounce.x_end = (sd->size.nw - view.w) / 2;
+             sd->g_layer_zoom.bounce.y_end = (sd->size.nh - view.h) / 2;
           }
         else
           {
@@ -1295,18 +1256,18 @@ _g_layer_zoom_end_cb(void *data,
              if (xx < 0) xx = 0;
              if (yy < 0) yy = 0;
 
-             if (rw > sd->size.nw)
-               sd->g_layer_zoom.bounce.x_end = (sd->size.nw - rw) / 2;
-             if ((xx + rw) > sd->size.nw)
-               xx = sd->size.nw - rw;
+             if (view.w > sd->size.nw)
+               sd->g_layer_zoom.bounce.x_end = (sd->size.nw - view.w) / 2;
+             if ((xx + view.w) > sd->size.nw)
+               xx = sd->size.nw - view.w;
 
-             if (rh > sd->size.nh)
-               sd->g_layer_zoom.bounce.y_end = (sd->size.nh - rh) / 2;
-             if ((yy + rh) > sd->size.nh)
-               yy = sd->size.nh - rh;
+             if (view.h > sd->size.nh)
+               sd->g_layer_zoom.bounce.y_end = (sd->size.nh - view.h) / 2;
+             if ((yy + view.h) > sd->size.nh)
+               yy = sd->size.nh - view.h;
 
-             sd->size.spos.x = (double)(xx + (rw / 2)) / (double)(sd->size.nw);
-             sd->size.spos.y = (double)(yy + (rh / 2)) / (double)(sd->size.nh);
+             sd->size.spos.x = (double)(xx + (view.w / 2)) / (double)(sd->size.nw);
+             sd->size.spos.y = (double)(yy + (view.h / 2)) / (double)(sd->size.nh);
           }
 
         sd->g_layer_zoom.bounce.t_start = t;
@@ -1317,7 +1278,7 @@ _g_layer_zoom_end_cb(void *data,
      }
    else
      {
-        elm_interface_scrollable_freeze_set(obj, EINA_FALSE);
+        efl_ui_scrollable_scroll_freeze_set(obj, EINA_FALSE);
         sd->zoom_g_layer = EINA_FALSE;
      }
 
@@ -1412,6 +1373,297 @@ _efl_ui_image_zoomable_efl_flipable_flip_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Z
    return sd->flip;
 }
 
+static void
+_efl_ui_image_zoomable_bar_read_and_update(Eo *obj)
+{
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   double vx, vy;
+
+   edje_object_part_drag_value_get
+      (wd->resize_obj, "elm.dragable.vbar", NULL, &vy);
+   edje_object_part_drag_value_get
+      (wd->resize_obj, "elm.dragable.hbar", &vx, NULL);
+   efl_ui_scrollbar_bar_position_set(sd->smanager, vx, vy);
+}
+
+static void
+_efl_ui_image_zoomable_reload_cb(void *data,
+                                 Evas_Object *obj EINA_UNUSED,
+                                 const char *emission EINA_UNUSED,
+                                 const char *source EINA_UNUSED)
+{
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd);
+
+   efl_ui_scrollbar_bar_visibility_update(sd->smanager);
+}
+
+static void
+_efl_ui_image_zoomable_vbar_drag_cb(void *data,
+                                    Evas_Object *obj EINA_UNUSED,
+                                    const char *emission EINA_UNUSED,
+                                    const char *source EINA_UNUSED)
+{
+   _efl_ui_image_zoomable_bar_read_and_update(data);
+
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type);
+}
+
+static void
+_efl_ui_image_zoomable_vbar_press_cb(void *data,
+                                     Evas_Object *obj EINA_UNUSED,
+                                     const char *emission EINA_UNUSED,
+                                     const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type);
+}
+
+static void
+_efl_ui_image_zoomable_vbar_unpress_cb(void *data,
+                                       Evas_Object *obj EINA_UNUSED,
+                                       const char *emission EINA_UNUSED,
+                                       const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type);
+}
+
+static void
+_efl_ui_image_zoomable_edje_drag_start_cb(void *data,
+                                          Evas_Object *obj EINA_UNUSED,
+                                          const char *emission EINA_UNUSED,
+                                          const char *source EINA_UNUSED)
+{
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd);
+
+   _efl_ui_image_zoomable_bar_read_and_update(data);
+
+   sd->freeze_want = efl_ui_scrollable_scroll_freeze_get(sd->smanager);
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_TRUE);
+   efl_event_callback_call(data, EFL_UI_EVENT_SCROLL_DRAG_START, NULL);
+}
+
+static void
+_efl_ui_image_zoomable_edje_drag_stop_cb(void *data,
+                                         Evas_Object *obj EINA_UNUSED,
+                                         const char *emission EINA_UNUSED,
+                                         const char *source EINA_UNUSED)
+{
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd);
+
+   _efl_ui_image_zoomable_bar_read_and_update(data);
+
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, sd->freeze_want);
+   efl_event_callback_call(data, EFL_UI_EVENT_SCROLL_DRAG_STOP, NULL);
+}
+
+static void
+_efl_ui_image_zoomable_edje_drag_cb(void *data,
+                                    Evas_Object *obj EINA_UNUSED,
+                                    const char *emission EINA_UNUSED,
+                                    const char *source EINA_UNUSED)
+{
+   _efl_ui_image_zoomable_bar_read_and_update(data);
+}
+
+static void
+_efl_ui_image_zoomable_hbar_drag_cb(void *data,
+                                    Evas_Object *obj EINA_UNUSED,
+                                    const char *emission EINA_UNUSED,
+                                    const char *source EINA_UNUSED)
+{
+   _efl_ui_image_zoomable_bar_read_and_update(data);
+
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type);
+}
+
+static void
+_efl_ui_image_zoomable_hbar_press_cb(void *data,
+                                     Evas_Object *obj EINA_UNUSED,
+                                     const char *emission EINA_UNUSED,
+                                     const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type);
+}
+
+static void
+_efl_ui_image_zoomable_hbar_unpress_cb(void *data,
+                                       Evas_Object *obj EINA_UNUSED,
+                                       const char *emission EINA_UNUSED,
+                                       const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type);
+}
+
+static void
+_efl_ui_image_zoomable_bar_size_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *obj = data;
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+
+   double width = 0.0, height = 0.0;
+
+   efl_ui_scrollbar_bar_size_get(sd->smanager, &width, &height);
+   edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.hbar", width, 1.0);
+   edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.vbar", 1.0, height);
+}
+
+static void
+_efl_ui_image_zoomable_bar_pos_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *obj = data;
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+
+   double posx = 0.0, posy = 0.0;
+
+   efl_ui_scrollbar_bar_position_get(sd->smanager, &posx, &posy);
+   edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.hbar", posx, 0.0);
+   edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.vbar", 0.0, posy);
+}
+
+static void
+_efl_ui_image_zoomable_bar_show_cb(void *data, const Efl_Event *event)
+{
+   Eo *obj = data;
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info);
+
+   if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,show,hbar", "elm");
+   else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,show,vbar", "elm");
+}
+
+static void
+_efl_ui_image_zoomable_bar_hide_cb(void *data, const Efl_Event *event)
+{
+   Eo *obj = data;
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info);
+
+   if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,hide,hbar", "elm");
+   else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,hide,vbar", "elm");
+}
+
+static void
+_efl_ui_image_zoomable_edje_object_attach(Eo *obj)
+{
+   efl_layout_signal_callback_add
+     (obj, "reload", "elm", _efl_ui_image_zoomable_reload_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag", "elm.dragable.vbar", _efl_ui_image_zoomable_vbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,set", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,start", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,stop", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,step", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,page", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,vbar,press", "elm",
+     _efl_ui_image_zoomable_vbar_press_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,vbar,unpress", "elm",
+     _efl_ui_image_zoomable_vbar_unpress_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag", "elm.dragable.hbar", _efl_ui_image_zoomable_hbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,set", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,start", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,stop", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,step", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,page", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,hbar,press", "elm",
+     _efl_ui_image_zoomable_hbar_press_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,hbar,unpress", "elm",
+     _efl_ui_image_zoomable_hbar_unpress_cb, obj);
+}
+
+static void
+_efl_ui_image_zoomable_edje_object_detach(Evas_Object *obj)
+{
+   efl_layout_signal_callback_del
+     (obj, "reload", "elm", _efl_ui_image_zoomable_reload_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag", "elm.dragable.vbar", _efl_ui_image_zoomable_vbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,set", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,start", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,stop", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,step", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,page", "elm.dragable.vbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,vbar,press", "elm",
+     _efl_ui_image_zoomable_vbar_press_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,vbar,unpress", "elm",
+     _efl_ui_image_zoomable_vbar_unpress_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag", "elm.dragable.hbar", _efl_ui_image_zoomable_hbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,set", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,start", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,stop", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,step", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,page", "elm.dragable.hbar",
+     _efl_ui_image_zoomable_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,hbar,press", "elm",
+     _efl_ui_image_zoomable_hbar_press_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,hbar,unpress", "elm",
+     _efl_ui_image_zoomable_hbar_unpress_cb, obj);
+}
+
 EOLIAN static void
 _efl_ui_image_zoomable_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Image_Zoomable_Data *priv)
 {
@@ -1430,34 +1682,24 @@ _efl_ui_image_zoomable_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Image_Zoomable
    elm_widget_theme_object_set
       (obj, edje, "photocam", "base", elm_widget_style_get(obj));
 
-   priv->hit_rect = evas_object_rectangle_add(evas_object_evas_get(obj));
-   evas_object_smart_member_add(priv->hit_rect, obj);
-   elm_widget_sub_object_add(obj, priv->hit_rect);
-
-   /* common scroller hit rectangle setup */
-   evas_object_color_set(priv->hit_rect, 0, 0, 0, 0);
-   evas_object_show(priv->hit_rect);
-   evas_object_repeat_events_set(priv->hit_rect, EINA_TRUE);
-
    elm_widget_can_focus_set(obj, EINA_TRUE);
 
-   elm_interface_scrollable_objects_set(obj, edje, priv->hit_rect);
+   priv->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj);
 
-   elm_interface_scrollable_animate_start_cb_set(obj, _scroll_animate_start_cb);
-   elm_interface_scrollable_animate_stop_cb_set(obj, _scroll_animate_stop_cb);
-   elm_interface_scrollable_drag_start_cb_set(obj, _scroll_drag_start_cb);
-   elm_interface_scrollable_drag_stop_cb_set(obj, _scroll_drag_stop_cb);
-   elm_interface_scrollable_scroll_cb_set(obj, _scroll_cb);
+   efl_ui_mirrored_set(priv->smanager, efl_ui_mirrored_get(obj));
+   efl_ui_scrollable_bounce_enabled_set(priv->smanager, bounce, bounce);
 
-   elm_interface_scrollable_bounce_allow_set(obj, bounce, bounce);
+   priv->pan_obj = efl_add(MY_PAN_CLASS, obj);
+
+   efl_ui_scroll_manager_pan_set(priv->smanager, priv->pan_obj);
+   edje_object_part_swallow(edje, "elm.swallow.content", priv->pan_obj);
 
-   priv->pan_obj = efl_add(MY_PAN_CLASS, evas_object_evas_get(obj));
    pan_data = efl_data_scope_get(priv->pan_obj, MY_PAN_CLASS);
    efl_data_ref(obj, MY_CLASS);
    pan_data->wobj = obj;
    pan_data->wsd = priv;
 
-   elm_interface_scrollable_extern_pan_set(obj, priv->pan_obj);
+   efl_event_callback_add(obj, EFL_UI_EVENT_SCROLL, _scroll_cb, obj);
 
    priv->g_layer_start = 1.0;
    priv->zoom = 1;
@@ -1484,8 +1726,17 @@ _efl_ui_image_zoomable_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Image_Zoomable
    edje_object_size_min_calc(edje, &minw, &minh);
    evas_object_size_hint_min_set(obj, minw, minh);
 
-   _sizing_eval(obj);
+   _efl_ui_image_zoomable_edje_object_attach(obj);
 
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED,
+                          _efl_ui_image_zoomable_bar_size_changed_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED,
+                          _efl_ui_image_zoomable_bar_pos_changed_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW,
+                          _efl_ui_image_zoomable_bar_show_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE,
+                          _efl_ui_image_zoomable_bar_hide_cb, obj);
+   _sizing_eval(obj);
 }
 
 EOLIAN static void
@@ -1513,38 +1764,38 @@ _efl_ui_image_zoomable_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Image_Zoomable
    ecore_timer_del(sd->long_timer);
    efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj);
    efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj);
+   efl_event_callback_del(obj, EFL_UI_EVENT_SCROLL, _scroll_cb, obj);
 
+   _efl_ui_image_zoomable_edje_object_detach(obj);
+   efl_del(sd->pan_obj);
+   sd->pan_obj = NULL;
+   efl_del(sd->smanager);
+   sd->smanager = NULL;
    efl_canvas_group_del(efl_super(obj, MY_CLASS));
 }
 
 EOLIAN static void
-_efl_ui_image_zoomable_efl_gfx_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Position2D pos)
+_efl_ui_image_zoomable_efl_gfx_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sdi EINA_UNUSED, Eina_Position2D pos)
 {
    if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y))
      return;
 
    efl_gfx_position_set(efl_super(obj, MY_CLASS), pos);
-   efl_gfx_position_set(sd->hit_rect, pos);
 }
 
 EOLIAN static void
-_efl_ui_image_zoomable_efl_gfx_size_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Size2D sz)
+_efl_ui_image_zoomable_efl_gfx_size_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, Eina_Size2D sz)
 {
    if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h))
      return;
 
    efl_gfx_size_set(efl_super(obj, MY_CLASS), sz);
-   efl_gfx_size_set(sd->hit_rect, sz);
 }
 
 EOLIAN static void
-_efl_ui_image_zoomable_efl_canvas_group_group_member_add(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Evas_Object *member)
+_efl_ui_image_zoomable_efl_canvas_group_group_member_add(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, Evas_Object *member)
 {
-
    efl_canvas_group_member_add(efl_super(obj, MY_CLASS), member);
-
-   if (sd->hit_rect)
-     evas_object_raise(sd->hit_rect);
 }
 
 EOLIAN static Eo *
@@ -1582,6 +1833,28 @@ _efl_ui_image_zoomable_efl_layout_group_group_size_max_get(Eo *obj EINA_UNUSED,
      return EINA_SIZE2D(0, 0);
 }
 
+EOLIAN static Eina_Bool
+_efl_ui_image_zoomable_efl_layout_signal_signal_callback_add(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
+{
+   Eina_Bool ok;
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE);
+
+   ok = efl_layout_signal_callback_add(wd->resize_obj, emission, source, func_cb, data);
+
+   return ok;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_image_zoomable_efl_layout_signal_signal_callback_del(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
+{
+   Eina_Bool ok;
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE);
+
+   ok = efl_layout_signal_callback_del(wd->resize_obj, emission, source, func_cb, data);
+
+   return ok;
+}
+
 static Eina_Bool
 _img_proxy_set(Evas_Object *obj, Efl_Ui_Image_Zoomable_Data *sd,
                const char *file, const Eina_File *f, const char *group,
@@ -1973,9 +2246,11 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
    double z;
    Eina_List *l;
    Efl_Ui_Image_Zoomable_Grid *g, *g_zoom = NULL;
-   Evas_Coord pw, ph, rx, ry, rw, rh;
+   Evas_Coord pw, ph;
    int zoom_changed = 0, started = 0;
    Eina_Bool an = EINA_FALSE;
+   Eina_Rect view = {};
+   Eina_Position2D pos = {};
 
    if (zoom <= (1.0 / 256.0)) zoom = (1.0 / 256.0);
    if (EINA_DBL_EQ(zoom, sd->zoom)) return;
@@ -1983,10 +2258,9 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
    sd->zoom = zoom;
    sd->size.ow = sd->size.w;
    sd->size.oh = sd->size.h;
-   elm_interface_scrollable_content_pos_get(obj, &rx, &ry);
-   elm_interface_scrollable_content_viewport_geometry_get
-         (obj, NULL, NULL, &rw, &rh);
-   if ((rw <= 0) || (rh <= 0)) return;
+   pos = efl_ui_scrollable_content_pos_get(sd->smanager);
+   view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
+   if ((view.w <= 0) || (view.h <= 0)) return;
 
    if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_MANUAL)
      {
@@ -2002,15 +2276,15 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
           }
         else
           {
-             ph = (sd->size.imh * rw) / sd->size.imw;
-             if (ph > rh)
+             ph = (sd->size.imh * view.w) / sd->size.imw;
+             if (ph > view.h)
                {
-                  pw = (sd->size.imw * rh) / sd->size.imh;
-                  ph = rh;
+                  pw = (sd->size.imw * view.h) / sd->size.imh;
+                  ph = view.h;
                }
              else
                {
-                  pw = rw;
+                  pw = view.w;
                }
              if (sd->size.imw > sd->size.imh)
                z = (double)sd->size.imw / pw;
@@ -2032,15 +2306,15 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
           }
         else
           {
-             ph = (sd->size.imh * rw) / sd->size.imw;
-             if (ph < rh)
+             ph = (sd->size.imh * view.w) / sd->size.imw;
+             if (ph < view.h)
                {
-                  pw = (sd->size.imw * rh) / sd->size.imh;
-                  ph = rh;
+                  pw = (sd->size.imw * view.h) / sd->size.imh;
+                  ph = view.h;
                }
              else
                {
-                  pw = rw;
+                  pw = view.w;
                }
              if (sd->size.imw > sd->size.imh)
                z = (double)sd->size.imw / pw;
@@ -2060,7 +2334,7 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
              sd->size.nw = 0;
              sd->size.nh = 0;
           }
-        else if ((sd->size.imw < rw) && (sd->size.imh < rh))
+        else if ((sd->size.imw < view.w) && (sd->size.imh < view.h))
           {
              if (!EINA_DBL_EQ(sd->zoom, 1)) zoom_changed = 1;
              sd->zoom = 1;
@@ -2069,14 +2343,14 @@ _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data
           }
         else
           {
-             ph = (sd->size.imh * rw) / sd->size.imw;
-             if (ph > rh)
+             ph = (sd->size.imh * view.w) / sd->size.imw;
+             if (ph > view.h)
                {
-                  pw = (sd->size.imw * rh) / sd->size.imh;
-                  ph = rh;
+                  pw = (sd->size.imw * view.h) / sd->size.imh;
+                  ph = view.h;
                }
              else
-               pw = rw;
+               pw = view.w;
              if (sd->size.imw > sd->size.imh)
                z = (double)sd->size.imw / pw;
              else
@@ -2138,16 +2412,16 @@ done:
    sd->t_end = sd->t_start + _elm_config->zoom_friction;
    if ((sd->size.w > 0) && (sd->size.h > 0))
      {
-        sd->size.spos.x = (double)(rx + (rw / 2)) / (double)sd->size.w;
-        sd->size.spos.y = (double)(ry + (rh / 2)) / (double)sd->size.h;
+        sd->size.spos.x = (double)(pos.x + (view.w / 2)) / (double)sd->size.w;
+        sd->size.spos.y = (double)(pos.y + (view.h / 2)) / (double)sd->size.h;
      }
    else
      {
         sd->size.spos.x = 0.5;
         sd->size.spos.y = 0.5;
      }
-   if (rw > sd->size.w) sd->size.spos.x = 0.5;
-   if (rh > sd->size.h) sd->size.spos.y = 0.5;
+   if (view.w > sd->size.w) sd->size.spos.x = 0.5;
+   if (view.h > sd->size.h) sd->size.spos.y = 0.5;
    if (sd->size.spos.x > 1.0) sd->size.spos.x = 1.0;
    if (sd->size.spos.y > 1.0) sd->size.spos.y = 1.0;
 
@@ -2225,20 +2499,18 @@ _efl_ui_image_zoomable_efl_gfx_view_view_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Im
 }
 
 EOLIAN static Eina_Rect
-_efl_ui_image_zoomable_image_region_get(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd)
+_efl_ui_image_zoomable_image_region_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd)
 {
-   Evas_Coord sx, sy, sw, sh;
    Eina_Rect region = {};
 
-   elm_interface_scrollable_content_pos_get((Eo *)obj, &sx, &sy);
-   elm_interface_scrollable_content_viewport_geometry_get
-         ((Eo *)obj, NULL, NULL, &sw, &sh);
+   Eina_Position2D pos = efl_ui_scrollable_content_pos_get(sd->smanager);
+   Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
 
    if (sd->size.w > 0)
      {
-        region.x = (sd->size.imw * sx) / sd->size.w;
+        region.x = (sd->size.imw * pos.x) / sd->size.w;
         if (region.x > sd->size.imw) region.x = sd->size.imw;
-        region.w = (sd->size.imw * sw) / sd->size.w;
+        region.w = (sd->size.imw * view.w) / sd->size.w;
         if (region.w > sd->size.imw) region.w = sd->size.imw;
         else if (region.w < 0)
           region.w = 0;
@@ -2246,9 +2518,9 @@ _efl_ui_image_zoomable_image_region_get(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd)
 
    if (sd->size.h > 0)
      {
-        region.y = (sd->size.imh * sy) / sd->size.h;
+        region.y = (sd->size.imh * pos.y) / sd->size.h;
         if (region.y > sd->size.imh) region.y = sd->size.imh;
-        region.h = (sd->size.imh * sh) / sd->size.h;
+        region.h = (sd->size.imh * view.h) / sd->size.h;
         if (region.h > sd->size.imh) region.h = sd->size.imh;
         else if (region.h < 0)
           region.h = 0;
@@ -2275,19 +2547,19 @@ _efl_ui_image_zoomable_image_region_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd,
    _efl_ui_image_zoomable_bounce_reset(obj, sd);
    _efl_ui_image_zoomable_zoom_reset(obj, sd);
 
-   elm_interface_scrollable_content_region_show(obj, rx, ry, rw, rh);
+   efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(rx, ry, rw, rh), EINA_FALSE);
 }
 
 EOLIAN static void
-_efl_ui_image_zoomable_elm_interface_scrollable_region_bring_in(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
+_efl_ui_image_zoomable_efl_ui_scrollable_interactive_scroll(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Rect rc, Eina_Bool animation)
 {
    int rx, ry, rw, rh;
 
    if ((sd->size.imw < 1) || (sd->size.imh < 1)) return;
-   rx = (x * sd->size.w) / sd->size.imw;
-   ry = (y * sd->size.h) / sd->size.imh;
-   rw = (w * sd->size.w) / sd->size.imw;
-   rh = (h * sd->size.h) / sd->size.imh;
+   rx = (rc.x * sd->size.w) / sd->size.imw;
+   ry = (rc.y * sd->size.h) / sd->size.imh;
+   rw = (rc.w * sd->size.w) / sd->size.imw;
+   rh = (rc.h * sd->size.h) / sd->size.imh;
    if (rw < 1) rw = 1;
    if (rh < 1) rh = 1;
    if ((rx + rw) > sd->size.w) rx = sd->size.w - rw;
@@ -2296,7 +2568,7 @@ _efl_ui_image_zoomable_elm_interface_scrollable_region_bring_in(Eo *obj, Efl_Ui_
    _efl_ui_image_zoomable_bounce_reset(obj, sd);
    _efl_ui_image_zoomable_zoom_reset(obj, sd);
 
-   elm_interface_scrollable_region_bring_in(efl_super(obj, MY_CLASS), rx, ry, rw, rh);
+   efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(rx, ry, rw, rh), animation);
 }
 
 EOLIAN static void
@@ -2928,7 +3200,8 @@ elm_photocam_image_region_bring_in(Evas_Object *obj,
                                    int h EINA_UNUSED)
 {
    ELM_PHOTOCAM_CHECK(obj);
-   elm_interface_scrollable_region_bring_in(obj, x, y, w, h);
+   EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd);
+   efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(x, y, w, h), EINA_TRUE);
 }
 
 EAPI void
@@ -2938,7 +3211,7 @@ elm_photocam_bounce_set(Evas_Object *obj,
 {
    ELM_PHOTOCAM_CHECK(obj);
 
-   elm_interface_scrollable_bounce_allow_set(obj, h_bounce, v_bounce);
+   efl_ui_scrollable_bounce_enabled_set(obj, h_bounce, v_bounce);
 }
 
 EAPI void
@@ -2948,7 +3221,7 @@ elm_photocam_bounce_get(const Evas_Object *obj,
 {
    ELM_PHOTOCAM_CHECK(obj);
 
-   elm_interface_scrollable_bounce_allow_get((Eo *)obj, h_bounce, v_bounce);
+   efl_ui_scrollable_bounce_enabled_get((Eo *)obj, h_bounce, v_bounce);
 }
 
 EAPI void
index 62dc1b1..6aa65ea 100644 (file)
@@ -1,6 +1,6 @@
 class Efl.Ui.Image_Zoomable (Elm.Widget, Efl.Ui.Image, Efl.Ui.Zoom,
-                             Elm.Interface_Scrollable,
-                             Efl.Ui.Scrollable)
+                             Efl.Ui.Scrollable.Interactive,
+                             Efl.Ui.Scrollbar)
 {
    [[Elementary Image Zoomable class]]
    legacy_prefix: elm_photocam;
@@ -61,13 +61,15 @@ class Efl.Ui.Image_Zoomable (Elm.Widget, Efl.Ui.Image, Efl.Ui.Zoom,
       Elm.Widget.theme_apply;
       Elm.Widget.on_focus_update;
       Elm.Widget.widget_event;
-      Elm.Interface_Scrollable.region_bring_in;
+      Efl.Ui.Scrollable.Interactive.scroll;
       Efl.Access.Widget.Action.elm_actions { get; }
       Efl.File.file { get; set; }
       Efl.Orientation.orientation { get; set; }
       Efl.Flipable.flip { get; set; }
       Efl.Layout.Group.group_size_min { get; }
       Efl.Layout.Group.group_size_max { get; }
+         Efl.Layout.Signal.signal_callback_add;
+         Efl.Layout.Signal.signal_callback_del;
       //Efl.Canvas.Layout_Group.group_data { get; }
    }
    events {
index e448a14..35f62a5 100644 (file)
@@ -1,4 +1,4 @@
-class Efl.Ui.Image_Zoomable_Pan (Elm.Pan)
+class Efl.Ui.Image_Zoomable_Pan (Efl.Ui.Pan)
 {
    [[Elementary photocom pan class]]
    legacy_prefix: elm_photocam_pan;
@@ -8,10 +8,10 @@ class Efl.Ui.Image_Zoomable_Pan (Elm.Pan)
       Efl.Gfx.position { set; }
       Efl.Gfx.size { set; }
       Efl.Canvas.Group.group_calculate;
-      Elm.Pan.content_size { get; }
-      Elm.Pan.pos { get; set; }
-      Elm.Pan.pos_min { get; }
-      Elm.Pan.pos_max { get; }
+      Efl.Ui.Pan.content_size { get; }
+      Efl.Ui.Pan.pan_position { get; set; }
+      Efl.Ui.Pan.pan_position_min { get; }
+      Efl.Ui.Pan.pan_position_max { get; }
    }
    events {
       load; [[Called when load started]]
index bfe58ef..1f867a9 100644 (file)
@@ -59,10 +59,10 @@ struct _Efl_Ui_Image_Zoomable_Grid
 
 struct _Efl_Ui_Image_Zoomable_Data
 {
-   Evas_Object                          *hit_rect;
+   Eo                                   *smanager;
+   Eo                                   *pan_obj;
    Evas_Object                          *g_layer;
 
-   Evas_Object                          *pan_obj;
 
    Evas_Coord                            pan_x, pan_y, minw, minh;
 
@@ -109,11 +109,7 @@ struct _Efl_Ui_Image_Zoomable_Data
       } spos;
    } size;
 
-   struct
-   {
-      Eina_Bool  show : 1;
-      Evas_Coord x, y, w, h;
-   } show;
+   Eina_Rect show;
 
    int          tsize;
    Evas_Object *img;  /* low res version of image (scale down == 8) */
@@ -147,11 +143,13 @@ struct _Efl_Ui_Image_Zoomable_Data
    Eina_Bool    orientation_changed : 1;
    Eina_Bool    play : 1;
    Eina_Bool    anim : 1;
+   Eina_Bool    freeze_want : 1;
+   Eina_Bool    show_item: 1;
 };
 
 struct _Efl_Ui_Image_Zoomable_Pan_Data
 {
-   Evas_Object            *wobj;
+   Eo                              *wobj;
    Efl_Ui_Image_Zoomable_Data      *wsd;
 };
 
diff --git a/src/lib/elementary/efl_ui_pan.c b/src/lib/elementary/efl_ui_pan.c
new file mode 100644 (file)
index 0000000..f6c065a
--- /dev/null
@@ -0,0 +1,197 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+#include "efl_ui_widget_pan.h"
+
+#define MY_CLASS EFL_UI_PAN_CLASS
+#define MY_CLASS_NAME "Efl_Ui_Pan"
+
+#define EFL_UI_PAN_DATA_GET(o, sd) \
+  Efl_Ui_Pan_Data *sd = efl_data_scope_safe_get(o, MY_CLASS)
+
+#define EFL_UI_PAN_DATA_GET_OR_RETURN(o, ptr, ...)                      \
+  EFL_UI_PAN_DATA_GET(o, ptr);                                     \
+  if (EINA_UNLIKELY(!ptr))                                         \
+    {                                                              \
+      CRI("No widget data for object %p (%s)",                     \
+          o, evas_object_type_get(o));                             \
+      return __VA_ARGS__;                                                      \
+    }
+
+EOLIAN static void
+_efl_ui_pan_efl_gfx_position_set(Eo *obj, Efl_Ui_Pan_Data *psd, Eina_Position2D pos)
+{
+   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y))
+     return;
+
+   efl_gfx_position_set(efl_super(obj, MY_CLASS), pos);
+
+   psd->x = pos.x;
+   psd->y = pos.y;
+
+   evas_object_smart_changed(obj);
+}
+
+EOLIAN static void
+_efl_ui_pan_efl_gfx_size_set(Eo *obj, Efl_Ui_Pan_Data *psd, Eina_Size2D sz)
+{
+   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h))
+     return;
+
+   efl_gfx_size_set(efl_super(obj, MY_CLASS), sz);
+
+   psd->w = sz.w;
+   psd->h = sz.h;
+
+   evas_object_smart_changed(obj);
+   efl_event_callback_call(obj, EFL_UI_PAN_EVENT_VIEWPORT_CHANGED, NULL);
+}
+
+EOLIAN static void
+_efl_ui_pan_efl_gfx_visible_set(Eo *obj, Efl_Ui_Pan_Data *psd, Eina_Bool vis)
+{
+   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis))
+     return;
+
+   efl_gfx_visible_set(efl_super(obj, MY_CLASS), vis);
+   if (psd->content) efl_gfx_visible_set(psd->content, vis);
+}
+
+EOLIAN static void
+_efl_ui_pan_pan_position_set(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *psd, Eina_Position2D pos)
+{
+   if ((pos.x == psd->px) && (pos.y == psd->py)) return;
+   psd->px = pos.x;
+   psd->py = pos.y;
+
+   evas_object_smart_changed(obj);
+   efl_event_callback_call(obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Position2D
+_efl_ui_pan_pan_position_get(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *psd)
+{
+   return EINA_POSITION2D(psd->px, psd->py);
+}
+
+EOLIAN static Eina_Position2D
+_efl_ui_pan_pan_position_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *psd)
+{
+   Eina_Position2D pos = { 0, 0};
+   if (psd->w < psd->content_w) pos.x = psd->content_w - psd->w;
+   if (psd->h < psd->content_h) pos.y = psd->content_h - psd->h;
+
+   return pos;
+}
+
+EOLIAN static Eina_Position2D
+_efl_ui_pan_pan_position_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *_pd EINA_UNUSED)
+{
+   return EINA_POSITION2D(0 ,0);
+}
+
+EOLIAN static Eina_Size2D
+_efl_ui_pan_content_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *psd)
+{
+   return EINA_SIZE2D(psd->content_w, psd->content_h);
+}
+
+EOLIAN static Eo *
+_efl_ui_pan_efl_object_constructor(Eo *obj, Efl_Ui_Pan_Data *_pd EINA_UNUSED)
+{
+   efl_canvas_group_clipped_set(obj, EINA_TRUE);
+   obj = efl_constructor(efl_super(obj, MY_CLASS));
+
+   return obj;
+}
+
+EOLIAN static void
+_efl_ui_pan_efl_object_destructor(Eo *obj, Efl_Ui_Pan_Data *sd EINA_UNUSED)
+{
+   efl_content_set(obj, NULL);
+   efl_destructor(efl_super(obj, MY_CLASS));
+}
+
+static void
+_efl_ui_pan_content_del_cb(void *data,
+                        Evas *e EINA_UNUSED,
+                        Evas_Object *obj EINA_UNUSED,
+                        void *event_info EINA_UNUSED)
+{
+   Evas_Object *pobj = data;
+   EFL_UI_PAN_DATA_GET_OR_RETURN(pobj, psd);
+
+   psd->content = NULL;
+   psd->content_w = psd->content_h = psd->px = psd->py = 0;
+   efl_event_callback_call(pobj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, NULL);
+}
+
+static void
+_efl_ui_pan_content_resize_cb(void *data,
+                           Evas *e EINA_UNUSED,
+                           Evas_Object *obj EINA_UNUSED,
+                           void *event_info EINA_UNUSED)
+{
+   Evas_Object *pobj = data;
+   EFL_UI_PAN_DATA_GET_OR_RETURN(pobj, psd);
+
+   Eina_Size2D sz = efl_gfx_size_get(psd->content);
+   if ((sz.w != psd->content_w) || (sz.h != psd->content_h))
+     {
+        psd->content_w = sz.w;
+        psd->content_h = sz.h;
+        evas_object_smart_changed(pobj);
+     }
+   efl_event_callback_call(pobj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_pan_efl_content_content_set(Evas_Object *obj, Efl_Ui_Pan_Data *psd, Evas_Object *content)
+{
+   Eina_Size2D sz;
+
+   if (content == psd->content) return EINA_TRUE;
+   if (psd->content)
+     {
+        efl_canvas_group_member_del(obj, psd->content);
+        evas_object_event_callback_del_full
+          (psd->content, EVAS_CALLBACK_DEL, _efl_ui_pan_content_del_cb, obj);
+        evas_object_event_callback_del_full
+          (psd->content, EVAS_CALLBACK_RESIZE, _efl_ui_pan_content_resize_cb,
+          obj);
+        psd->content = NULL;
+        psd->content_w = psd->content_h = psd->px = psd->py = 0;
+     }
+   if (!content) goto end;
+
+   psd->content = content;
+   efl_canvas_group_member_add(obj, content);
+   sz = efl_gfx_size_get(psd->content);
+   psd->content_w = sz.w;
+   psd->content_h = sz.h;
+   evas_object_event_callback_add
+     (content, EVAS_CALLBACK_DEL, _efl_ui_pan_content_del_cb, obj);
+   evas_object_event_callback_add
+     (content, EVAS_CALLBACK_RESIZE, _efl_ui_pan_content_resize_cb, obj);
+
+   if (evas_object_visible_get(obj))
+     evas_object_show(psd->content);
+   else
+     evas_object_hide(psd->content);
+
+   evas_object_smart_changed(obj);
+
+end:
+   efl_event_callback_call(obj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, NULL);
+   return EINA_TRUE;
+}
+
+EOLIAN static void
+_efl_ui_pan_efl_canvas_group_group_calculate(Eo *obj EINA_UNUSED, Efl_Ui_Pan_Data *psd)
+{
+   efl_gfx_position_set(psd->content, EINA_POSITION2D(psd->x - psd->px, psd->y - psd->py));
+}
+#include "efl_ui_pan.eo.c"
diff --git a/src/lib/elementary/efl_ui_pan.eo b/src/lib/elementary/efl_ui_pan.eo
new file mode 100644 (file)
index 0000000..5648a85
--- /dev/null
@@ -0,0 +1,55 @@
+class Efl.Ui.Pan (Efl.Canvas.Group,
+                  Efl.Content)
+{
+   [[Elementary pan class]]
+   methods {
+      @property pan_position {
+        [[Position]]
+         set {
+         }
+         get {
+         }
+         values {
+            position: Eina.Position2D;
+         }
+      }
+      @property content_size {
+        [[Content size]]
+         get {
+         }
+         values {
+           size: Eina.Size2D;
+         }
+      }
+      @property pan_position_min {
+        [[The minimal position to scroll]]
+         get {
+         }
+         values {
+           pos: Eina.Position2D;
+         }
+      }
+      @property pan_position_max {
+        [[The maximal position to scroll]]
+         get {
+         }
+         values {
+           pos: Eina.Position2D;
+         }
+      }
+   }
+   implements {
+      Efl.Object.constructor;
+      Efl.Object.destructor;
+      Efl.Gfx.visible { set; }
+      Efl.Gfx.position { set; }
+      Efl.Gfx.size { set; }
+      Efl.Content.content { set; }
+      Efl.Canvas.Group.group_calculate;
+   }
+   events {
+      content,changed; [[Called when pan content changed]]
+      viewport,changed; [[Called when pan viewport changed]]
+      position,changed; [[Called when pan position changed]]
+   }
+}
diff --git a/src/lib/elementary/efl_ui_scroll_manager.c b/src/lib/elementary/efl_ui_scroll_manager.c
new file mode 100644 (file)
index 0000000..a25e3db
--- /dev/null
@@ -0,0 +1,2495 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#define EFL_UI_SCROLL_MANAGER_PROTECTED
+#define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_SCROLLBAR_BETA
+
+#include <Elementary.h>
+#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_EVENT_ANIMATOR_TICK, Callback, Data); \
+  efl_event_callback_add(Obj, EFL_EVENT_ANIMATOR_TICK, Callback, Data); \
+  Bool = 1;
+
+#define ELM_ANIMATOR_DISCONNECT(Obj, Bool, Callback, Data)              \
+  efl_event_callback_del(Obj, EFL_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 == ACCEL)
+     return _scroll_manager_accel_interp;
+   else if (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 EVTIME             1
+//#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_SCROLL_BLOCK_VERTICAL) &&
+       (sd->block & EFL_UI_SCROLL_BLOCK_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_START, 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_STOP, 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_START, 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_STOP, 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_START, 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_STOP, 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, 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_interactive_content_size_get(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_interactive_viewport_geometry_get(Eo *obj EINA_UNUSED,
+                                                                       Efl_Ui_Scroll_Manager_Data *sd)
+{
+   if (!sd->pan_obj) return EINA_RECT(0, 0, 0, 0);
+
+   return efl_gfx_geometry_get(sd->pan_obj);
+}
+
+EOLIAN static void
+_efl_ui_scroll_manager_efl_ui_scrollable_interactive_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;
+}
+
+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_interactive_content_pos_get(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_interactive_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_base_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(Eo *obj EINA_UNUSED,
+                                                     Efl_Ui_Scroll_Manager_Data *sd,
+                                                     Efl_Ui_Scrollbar_Mode *hmode,
+                                                     Efl_Ui_Scrollbar_Mode *vmode)
+{
+    *hmode = sd->hbar_mode;
+    *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_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);
+        efl_future_Eina_FutureXXX_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, 0.5, 0.5, LINEAR);
+     }
+}
+
+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) x = sd->scrollto.x.end;
+   if (sd->scrollto.y.animator) 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_SCROLL_BLOCK_HORIZONTAL) return;
+     }
+   else
+     {
+        if (sd->block & EFL_UI_SCROLL_BLOCK_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(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(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;
+
+   return;
+   if (ox) *ox = (Evas_Coord)(ax / (i + 1));
+   if (oy) *oy = (Evas_Coord)(ay / (i + 1));
+   if (ot) *ot = (double)(at / (i + 1));
+}
+
+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;
+   char sdx, sdy;
+
+#ifdef EVTIME
+   t = ev->timestamp / 1000.0;
+#else
+   t = ecore_loop_time_get();
+#endif
+
+   ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+   ax = ev->canvas.x;
+   ay = ev->canvas.y;
+   at = 0.0;
+#ifdef SCROLLDBG
+   DBG("------ %i %i\n", ev->canvas.x, ev->canvas.y);
+#endif
+   _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.hist.est_timestamp_diff =
+          ecore_loop_time_get() - ((double)ev->timestamp / 1000.0);
+        sd->down.hist.tadd = 0.0;
+        sd->down.hist.dxsum = 0.0;
+        sd->down.hist.dysum = 0.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);
+#ifdef EVTIME
+        sd->down.history[0].timestamp = ev->timestamp / 1000.0;
+        sd->down.history[0].localtimestamp = ecore_loop_time_get();
+#else
+        sd->down.history[0].timestamp = ecore_loop_time_get();
+#endif
+        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;
+  dist = sqrt(dx * dx + dy *dy);
+  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)
+{
+  static const double friction = 5000;
+  static const double inverse_mass = 1;
+  static const double accel = friction * inverse_mass;
+  double dur = 0.0, vel = 0.0;
+  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) / (2 * accel);
+   dsty = cur.y + sdy * (vy * vy) / (2 * accel);
+
+   vel = sqrt(vx*vx + vy*vy);
+   dur = vel / accel;
+
+   _scroll_manager_scrollto_animator_add(sd, cur.x, cur.y, dstx, dsty, dur, dur, 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)
+{
+   _scroll_manager_scrollto_animator_del(sd);
+
+   if (!sd->pan_obj) return;
+   if (sd->freeze) 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, 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_SCROLL_BLOCK_HORIZONTAL))
+     sd->down.dir_x = EINA_TRUE;
+
+   if (!(sd->block & EFL_UI_SCROLL_BLOCK_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;
+#ifdef EVTIME
+                 sd->down.dragged_began_timestamp = ev->timestamp / 1000.0;
+#else
+                 sd->down.dragged_began_timestamp = ecore_loop_time_get();
+#endif
+              }
+            // 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.hist.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));
+#ifdef EVTIME
+   sd->down.history[0].timestamp = ev->timestamp / 1000.0;
+   sd->down.history[0].localtimestamp = ecore_loop_time_get();
+#else
+   sd->down.history[0].timestamp = ecore_loop_time_get();
+#endif
+   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
+_scroll_event_object_detach(Evas_Object *obj)
+{
+   EFL_UI_SCROLL_MANAGER_DATA_GET_OR_RETURN(obj, sd);
+
+   evas_object_event_callback_del_full
+     (sd->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _efl_ui_scroll_manager_wheel_event_cb,
+     sd);
+   evas_object_event_callback_del_full
+     (sd->event_rect, EVAS_CALLBACK_MOUSE_DOWN,
+     _efl_ui_scroll_manager_mouse_down_event_cb, sd);
+   evas_object_event_callback_del_full
+     (sd->event_rect, EVAS_CALLBACK_MOUSE_UP,
+     _efl_ui_scroll_manager_mouse_up_event_cb, sd);
+   evas_object_event_callback_del_full
+     (sd->event_rect, EVAS_CALLBACK_MOUSE_MOVE,
+     _efl_ui_scroll_manager_mouse_move_event_cb, sd);
+}
+
+static void
+_efl_ui_scroll_manager_pan_content_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Efl_Ui_Scroll_Manager_Data *sd = data;
+   Eina_Size2D content = {0, 0};
+
+   if (!sd->pan_obj) return;
+
+   content = efl_ui_pan_content_size_get(sd->pan_obj);
+   if ((content.w != sd->content_info.w) || (content.h != sd->content_info.h))
+     {
+        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_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_size_set(sd->event_rect, efl_gfx_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_position_set(sd->event_rect, efl_gfx_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_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+        efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, &type);
+     }
+   else
+     {
+        Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_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_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+        efl_event_callback_call(sd->parent, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, &type);
+     }
+   else
+     {
+        Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_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_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(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(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)
+     {
+        efl_event_callback_del
+           (sd->pan_obj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd);
+        efl_event_callback_del
+           (sd->pan_obj, EFL_UI_PAN_EVENT_VIEWPORT_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd);
+        efl_event_callback_del
+           (sd->pan_obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_changed_cb, sd);
+     }
+
+   if (!pan)
+     return;
+
+   sd->pan_obj = pan;
+
+   efl_event_callback_add
+     (sd->pan_obj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd);
+   efl_event_callback_add
+     (sd->pan_obj, EFL_UI_PAN_EVENT_VIEWPORT_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd);
+   efl_event_callback_add
+     (sd->pan_obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_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_interactive_scroll_hold_get(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd)
+{
+   return sd->hold;
+}
+
+EOLIAN static void
+_efl_ui_scroll_manager_efl_ui_scrollable_interactive_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_interactive_scroll_freeze_get(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd)
+{
+   return sd->freeze;
+}
+
+EOLIAN static void
+_efl_ui_scroll_manager_efl_ui_scrollable_interactive_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_interactive_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_interactive_bounce_enabled_get(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_interactive_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_interactive_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_interactive_gravity_get(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_interactive_movement_block_set(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd, Efl_Ui_Scroll_Block block)
+{
+   sd->block = block;
+}
+
+EOLIAN static Efl_Ui_Scroll_Block
+_efl_ui_scroll_manager_efl_ui_scrollable_interactive_movement_block_get(Eo *obj EINA_UNUSED, Efl_Ui_Scroll_Manager_Data *sd)
+{
+   return sd->block;
+}
+
+EOLIAN static void
+_efl_ui_scroll_manager_efl_ui_scrollable_interactive_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_interactive_looping_get(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_SCROLL_BLOCK_NONE;
+   sd->scrolling = EINA_FALSE;
+
+   sd->event_rect = evas_object_rectangle_add(evas_object_evas_get(sd->parent));
+   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_visible_set(sd->event_rect, EINA_TRUE);
+   efl_canvas_object_repeat_events_set(sd->event_rect, EINA_TRUE);
+
+   _scroll_event_object_attach(obj);
+
+   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);
+
+   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_UI_PAN_EVENT_CONTENT_CHANGED, _efl_ui_scroll_manager_pan_content_changed_cb, sd);
+   efl_event_callback_del
+      (sd->pan_obj, EFL_UI_PAN_EVENT_VIEWPORT_CHANGED, _efl_ui_scroll_manager_pan_viewport_changed_cb, sd);
+   efl_event_callback_del
+      (sd->pan_obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, _efl_ui_scroll_manager_pan_position_changed_cb, sd);
+
+   _scroll_event_object_detach(obj);
+
+   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
new file mode 100644 (file)
index 0000000..462b964
--- /dev/null
@@ -0,0 +1,44 @@
+class Efl.Ui.Scroll.Manager (Efl.Object,
+                             Efl.Ui.Base,
+                             Efl.Ui.Scrollable.Interactive,
+                             Efl.Ui.Scrollbar)
+{
+   [[Efl ui scroll manager class]]
+   event_prefix: efl_ui;
+   eo_prefix: efl_ui_scroll_manager;
+   methods {
+      @property pan @protected {
+        [[This is the internal canvas object managed by scroll manager.
+                
+          This property is protected as it is meant for scrollable object
+          implementations only, to set and access the internal canvas object.
+          If pan is set to NULL, scrolling does not work.
+        ]]
+         set {
+         }
+         values {
+            pan: Efl.Canvas.Object @nullable; [[Pan object]]
+         }
+      }
+   }
+   implements {
+      Efl.Object.constructor;
+      Efl.Object.destructor;
+      Efl.Ui.Base.mirrored { set; }
+      Efl.Ui.Scrollable.Interactive.content_pos { set; get; }
+      Efl.Ui.Scrollable.Interactive.content_size{ get; }
+      Efl.Ui.Scrollable.Interactive.viewport_geometry{ get; }
+      Efl.Ui.Scrollable.Interactive.bounce_enabled { set; get; }
+      Efl.Ui.Scrollable.Interactive.scroll_freeze { get; set; }
+      Efl.Ui.Scrollable.Interactive.scroll_hold { get; set; }
+      Efl.Ui.Scrollable.Interactive.looping { get; set; }
+      Efl.Ui.Scrollable.Interactive.movement_block { get; set; }
+      Efl.Ui.Scrollable.Interactive.gravity { get; set; }
+      Efl.Ui.Scrollable.Interactive.match_content { set; }
+      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_update;
+      Efl.Ui.Scrollable.Interactive.scroll;
+   }
+}
diff --git a/src/lib/elementary/efl_ui_scroller.c b/src/lib/elementary/efl_ui_scroller.c
new file mode 100644 (file)
index 0000000..e10d5b6
--- /dev/null
@@ -0,0 +1,638 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#define ELM_LAYOUT_PROTECTED
+#define EFL_UI_SCROLL_MANAGER_PROTECTED
+#define EFL_UI_SCROLLBAR_PROTECTED
+#define EFL_UI_SCROLLBAR_BETA
+
+#include <Elementary.h>
+#include "elm_priv.h"
+#include "efl_ui_widget_scroller.h"
+#include "elm_widget_layout.h"
+
+#define MY_CLASS EFL_UI_SCROLLER_CLASS
+#define MY_CLASS_PFX efl_ui_scroller
+
+#define MY_CLASS_NAME "Efl.Ui.Scroller"
+
+#define EFL_UI_SCROLLER_DATA_GET(o, sd) \
+  Efl_Ui_Scroller_Data * sd = efl_data_scope_safe_get(o, EFL_UI_SCROLLER_CLASS)
+
+#define EFL_UI_SCROLLER_DATA_GET_OR_RETURN(o, ptr, ...) \
+  EFL_UI_SCROLLER_DATA_GET(o, ptr);                         \
+  if (EINA_UNLIKELY(!ptr))                            \
+    {                                                 \
+       CRI("No widget data for object %p (%s)",       \
+           o, evas_object_type_get(o));               \
+       return __VA_ARGS__;                                    \
+    }
+static void
+_efl_ui_scroller_content_del_cb(void *data,
+                                const Efl_Event *event EINA_UNUSED)
+{
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(data, sd);
+
+   sd->content = NULL;
+   if (!sd->smanager) return;
+   efl_ui_scrollbar_bar_visibility_update(sd->smanager);
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_scroller_efl_content_content_set(Eo *obj,
+                                           Efl_Ui_Scroller_Data *sd,
+                                           Evas_Object *content)
+{
+   if (sd->content)
+     {
+        efl_content_set(sd->pan_obj, NULL);
+        efl_event_callback_del(sd->content, EFL_EVENT_DEL,
+                               _efl_ui_scroller_content_del_cb, obj);
+     }
+
+   sd->content = content;
+   if (!content) return EINA_TRUE;
+
+   efl_event_callback_add(sd->content, EFL_EVENT_DEL,
+                          _efl_ui_scroller_content_del_cb, obj);
+
+   efl_content_set(sd->pan_obj, content);
+
+   elm_layout_sizing_eval(obj);
+
+   return EINA_TRUE;
+}
+
+static void
+_efl_ui_scroller_bar_read_and_update(Eo *obj)
+{
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   double vx, vy;
+
+   edje_object_part_drag_value_get
+      (wd->resize_obj, "elm.dragable.vbar", NULL, &vy);
+   edje_object_part_drag_value_get
+      (wd->resize_obj, "elm.dragable.hbar", &vx, NULL);
+
+   efl_ui_scrollbar_bar_position_set(sd->smanager, vx, vy);
+}
+
+static void
+_efl_ui_scroller_reload_cb(void *data,
+                           Evas_Object *obj EINA_UNUSED,
+                           const char *emission EINA_UNUSED,
+                           const char *source EINA_UNUSED)
+{
+   Eo *scroller = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(scroller, sd);
+
+   efl_ui_scrollbar_bar_visibility_update(sd->smanager);
+}
+
+static void
+_efl_ui_scroller_vbar_drag_cb(void *data,
+                              Evas_Object *obj EINA_UNUSED,
+                              const char *emission EINA_UNUSED,
+                              const char *source EINA_UNUSED)
+{
+   _efl_ui_scroller_bar_read_and_update(data);
+
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type);
+}
+
+static void
+_efl_ui_scroller_vbar_press_cb(void *data,
+                               Evas_Object *obj EINA_UNUSED,
+                               const char *emission EINA_UNUSED,
+                               const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type);
+}
+
+static void
+_efl_ui_scroller_vbar_unpress_cb(void *data,
+                                 Evas_Object *obj EINA_UNUSED,
+                                 const char *emission EINA_UNUSED,
+                                 const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type);
+}
+
+static void
+_efl_ui_scroller_edje_drag_start_cb(void *data,
+                                 Evas_Object *obj EINA_UNUSED,
+                                 const char *emission EINA_UNUSED,
+                                 const char *source EINA_UNUSED)
+{
+   Eo *scroller = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(scroller, sd);
+
+   _efl_ui_scroller_bar_read_and_update(scroller);
+
+   sd->freeze_want = efl_ui_scrollable_scroll_freeze_get(sd->smanager);
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_TRUE);
+   efl_event_callback_call(scroller, EFL_UI_EVENT_SCROLL_DRAG_START, NULL);
+}
+
+static void
+_efl_ui_scroller_edje_drag_stop_cb(void *data,
+                                Evas_Object *obj EINA_UNUSED,
+                                const char *emission EINA_UNUSED,
+                                const char *source EINA_UNUSED)
+{
+   Eo *scroller = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(scroller, sd);
+
+   _efl_ui_scroller_bar_read_and_update(scroller);
+
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, sd->freeze_want);
+   efl_event_callback_call(scroller, EFL_UI_EVENT_SCROLL_DRAG_STOP, NULL);
+}
+
+static void
+_efl_ui_scroller_edje_drag_cb(void *data,
+                           Evas_Object *obj EINA_UNUSED,
+                           const char *emission EINA_UNUSED,
+                           const char *source EINA_UNUSED)
+{
+   _efl_ui_scroller_bar_read_and_update(data);
+}
+
+static void
+_efl_ui_scroller_hbar_drag_cb(void *data,
+                         Evas_Object *obj EINA_UNUSED,
+                         const char *emission EINA_UNUSED,
+                         const char *source EINA_UNUSED)
+{
+   _efl_ui_scroller_bar_read_and_update(data);
+
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type);
+}
+
+static void
+_efl_ui_scroller_hbar_press_cb(void *data,
+                          Evas_Object *obj EINA_UNUSED,
+                          const char *emission EINA_UNUSED,
+                          const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type);
+}
+
+static void
+_efl_ui_scroller_hbar_unpress_cb(void *data,
+                            Evas_Object *obj EINA_UNUSED,
+                            const char *emission EINA_UNUSED,
+                            const char *source EINA_UNUSED)
+{
+   Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL;
+   efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type);
+}
+
+static void
+_efl_ui_scroller_bar_size_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *obj = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+
+   double width = 0.0, height = 0.0;
+
+   edje_object_calc_force(wd->resize_obj);
+   efl_ui_scrollbar_bar_size_get(sd->smanager, &width, &height);
+   edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.hbar", width, 1.0);
+   edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.vbar", 1.0, height);
+}
+
+static void
+_efl_ui_scroller_bar_pos_changed_cb(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *obj = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+
+   double posx = 0.0, posy = 0.0;
+
+   efl_ui_scrollbar_bar_position_get(sd->smanager, &posx, &posy);
+   edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.hbar", posx, 0.0);
+   edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.vbar", 0.0, posy);
+}
+
+static void
+_efl_ui_scroller_bar_show_cb(void *data, const Efl_Event *event)
+{
+   Eo *obj = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info);
+
+   if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,show,hbar", "elm");
+   else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,show,vbar", "elm");
+}
+
+static void
+_efl_ui_scroller_bar_hide_cb(void *data, const Efl_Event *event)
+{
+   Eo *obj = data;
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+   Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info);
+
+   if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,hide,hbar", "elm");
+   else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL)
+     edje_object_signal_emit(wd->resize_obj, "elm,action,hide,vbar", "elm");
+}
+
+static void
+_scroll_edje_object_attach(Eo *obj)
+{
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+
+   efl_layout_signal_callback_add
+     (obj, "reload", "elm", _efl_ui_scroller_reload_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag", "elm.dragable.vbar", _efl_ui_scroller_vbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,set", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,start", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,stop", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,step", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,page", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,vbar,press", "elm",
+     _efl_ui_scroller_vbar_press_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,vbar,unpress", "elm",
+     _efl_ui_scroller_vbar_unpress_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag", "elm.dragable.hbar", _efl_ui_scroller_hbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,set", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,start", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,stop", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,step", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "drag,page", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,hbar,press", "elm",
+     _efl_ui_scroller_hbar_press_cb, obj);
+   efl_layout_signal_callback_add
+     (obj, "elm,hbar,unpress", "elm",
+     _efl_ui_scroller_hbar_unpress_cb, obj);
+}
+
+static void
+_scroll_edje_object_detach(Evas_Object *obj)
+{
+   EFL_UI_SCROLLER_DATA_GET_OR_RETURN(obj, sd);
+
+   efl_layout_signal_callback_del
+     (obj, "reload", "elm", _efl_ui_scroller_reload_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag", "elm.dragable.vbar", _efl_ui_scroller_vbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,set", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,start", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,stop", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,step", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,page", "elm.dragable.vbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,vbar,press", "elm",
+     _efl_ui_scroller_vbar_press_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,vbar,unpress", "elm",
+     _efl_ui_scroller_vbar_unpress_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag", "elm.dragable.hbar", _efl_ui_scroller_hbar_drag_cb,
+     obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,set", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,start", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_start_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,stop", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_stop_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,step", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "drag,page", "elm.dragable.hbar",
+     _efl_ui_scroller_edje_drag_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,hbar,press", "elm",
+     _efl_ui_scroller_hbar_press_cb, obj);
+   efl_layout_signal_callback_del
+     (obj, "elm,hbar,unpress", "elm",
+     _efl_ui_scroller_hbar_unpress_cb, obj);
+}
+
+static void
+_efl_ui_scroller_pan_resized_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   elm_layout_sizing_eval(data);
+}
+
+static void
+_efl_ui_scroller_resized_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   elm_layout_sizing_eval(data);
+}
+
+static void
+_efl_ui_scroller_size_hint_changed_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   elm_layout_sizing_eval(data);
+}
+
+EOLIAN static Eo *
+_efl_ui_scroller_efl_object_constructor(Eo *obj,
+                                        Efl_Ui_Scroller_Data *sd EINA_UNUSED)
+{
+   obj = efl_constructor(efl_super(obj, MY_CLASS));
+
+   return obj;
+}
+
+EOLIAN static Eo *
+_efl_ui_scroller_efl_object_finalize(Eo *obj,
+                                     Efl_Ui_Scroller_Data *sd EINA_UNUSED)
+{
+   obj = efl_finalize(efl_super(obj, MY_CLASS));
+
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL);
+
+   efl_ui_layout_theme_set(obj, "scroller", "base", efl_ui_widget_style_get(obj));
+
+   sd->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj);
+   efl_ui_mirrored_set(sd->smanager, efl_ui_mirrored_get(obj));
+
+   sd->pan_obj = efl_add(EFL_UI_PAN_CLASS, obj);
+
+   efl_ui_scroll_manager_pan_set(sd->smanager, sd->pan_obj);
+   edje_object_part_swallow(wd->resize_obj, "elm.swallow.content", sd->pan_obj);
+
+   _scroll_edje_object_attach(obj);
+
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED,
+                          _efl_ui_scroller_bar_size_changed_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED,
+                          _efl_ui_scroller_bar_pos_changed_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW,
+                          _efl_ui_scroller_bar_show_cb, obj);
+   efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE,
+                          _efl_ui_scroller_bar_hide_cb, obj);
+   efl_event_callback_add(obj, EFL_GFX_EVENT_RESIZE,
+                          _efl_ui_scroller_resized_cb, obj);
+   efl_event_callback_add(obj, EFL_GFX_EVENT_CHANGE_SIZE_HINTS,
+                          _efl_ui_scroller_size_hint_changed_cb, obj);
+   efl_event_callback_add(sd->pan_obj, EFL_GFX_EVENT_RESIZE,
+                          _efl_ui_scroller_pan_resized_cb, obj);
+
+   return obj;
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_object_destructor(Eo *obj,
+                                       Efl_Ui_Scroller_Data *sd EINA_UNUSED)
+{
+   _scroll_edje_object_detach(obj);
+
+   efl_event_callback_del(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED,
+                          _efl_ui_scroller_bar_size_changed_cb, obj);
+   efl_event_callback_del(obj, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED,
+                          _efl_ui_scroller_bar_pos_changed_cb, obj);
+   efl_event_callback_del(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW,
+                          _efl_ui_scroller_bar_show_cb, obj);
+   efl_event_callback_del(obj, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE,
+                          _efl_ui_scroller_bar_hide_cb, obj);
+   efl_event_callback_del(obj, EFL_GFX_EVENT_RESIZE,
+                          _efl_ui_scroller_resized_cb, obj);
+   efl_event_callback_del(obj, EFL_GFX_EVENT_CHANGE_SIZE_HINTS,
+                          _efl_ui_scroller_size_hint_changed_cb, obj);
+   efl_event_callback_del(sd->pan_obj, EFL_GFX_EVENT_RESIZE,
+                          _efl_ui_scroller_pan_resized_cb, obj);
+   efl_del(sd->pan_obj);
+   sd->pan_obj = NULL;
+   efl_del(sd->smanager);
+   sd->smanager = NULL;
+
+   efl_destructor(efl_super(obj, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_ui_scroller_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Scroller_Data *sd)
+{
+   Eina_Size2D min = {0, 0}, max = {0, 0}, size = {-1, -1};
+   Eina_Rect view = {};
+   Evas_Coord vmw = 0, vmh = 0;
+   double xw = 0.0, yw = 0.0;
+
+   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
+
+   if (sd->content)
+     {
+        min = efl_gfx_size_hint_combined_min_get(sd->content);
+        max = efl_gfx_size_hint_max_get(sd->content);
+        efl_gfx_size_hint_weight_get(sd->content, &xw, &yw);
+     }
+
+   if (sd->smanager)
+     view = efl_ui_scrollable_viewport_geometry_get(sd->smanager);
+
+   if (xw > 0.0)
+     {
+        if ((min.w > 0) && (view.w < min.w))
+          view.w = min.w;
+        else if ((max.w > 0) && (view.w > max.w))
+          view.w = max.w;
+     }
+   else if (min.w > 0)
+     view.w = min.w;
+
+   if (yw > 0.0)
+     {
+        if ((min.h > 0) && (view.h < min.h))
+          view.h = min.h;
+        else if ((max.h > 0) && (view.h > max.h))
+          view.h = max.h;
+     }
+   else if (min.h > 0)
+     view.h = min.h;
+
+   if (sd->content) efl_gfx_size_set(sd->content, EINA_SIZE2D(view.w, view.h));
+
+   edje_object_size_min_calc(wd->resize_obj, &vmw, &vmh);
+
+   if (sd->match_content_w) size.w = vmw + min.w;
+   if (sd->match_content_h) size.h = vmh + min.h;
+
+   max = efl_gfx_size_hint_max_get(obj);
+   if ((max.w > 0) && (size.w > max.w)) size.w = max.w;
+   if ((max.h > 0) && (size.h > max.h)) size.h = max.h;
+
+   efl_gfx_size_hint_min_set(obj, size);
+}
+
+EOLIAN static Efl_Ui_Theme_Apply
+_efl_ui_scroller_elm_widget_theme_apply(Eo *obj, Efl_Ui_Scroller_Data *sd)
+{
+   Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED;
+   int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
+   if (!int_ret) return EFL_UI_THEME_APPLY_FAILED;
+
+   efl_ui_mirrored_set(sd->smanager, efl_ui_mirrored_get(obj));
+
+   elm_layout_sizing_eval(obj);
+
+   return int_ret;
+}
+
+EOLIAN static Eina_Size2D
+_efl_ui_scroller_efl_ui_scrollable_interactive_content_size_get(Eo *obj EINA_UNUSED,
+                                                            Efl_Ui_Scroller_Data *sd)
+{
+   return efl_ui_scrollable_content_size_get(sd->smanager);
+}
+
+EOLIAN static Eina_Rect
+_efl_ui_scroller_efl_ui_scrollable_interactive_viewport_geometry_get(Eo *obj EINA_UNUSED,
+                                                                 Efl_Ui_Scroller_Data *sd)
+{
+   return efl_ui_scrollable_viewport_geometry_get(sd->smanager);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_bounce_enabled_set(Eo *obj EINA_UNUSED,
+                                                              Efl_Ui_Scroller_Data *sd,
+                                                              Eina_Bool horiz,
+                                                              Eina_Bool vert)
+{
+   efl_ui_scrollable_bounce_enabled_set(sd->smanager, horiz, vert);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_bounce_enabled_get(Eo *obj EINA_UNUSED,
+                                                                  Efl_Ui_Scroller_Data *sd,
+                                                                  Eina_Bool *horiz,
+                                                                  Eina_Bool *vert)
+{
+   efl_ui_scrollable_bounce_enabled_get(sd->smanager, horiz, vert);
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_scroller_efl_ui_scrollable_interactive_scroll_hold_get(Eo *obj EINA_UNUSED,
+                                                               Efl_Ui_Scroller_Data *sd)
+{
+   return efl_ui_scrollable_scroll_hold_get(sd->smanager);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_scroll_hold_set(Eo *obj EINA_UNUSED,
+                                                               Efl_Ui_Scroller_Data *sd,
+                                                               Eina_Bool hold)
+{
+   efl_ui_scrollable_scroll_hold_set(sd->smanager, hold);
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_scroller_efl_ui_scrollable_interactive_scroll_freeze_get(Eo *obj EINA_UNUSED,
+                                                                 Efl_Ui_Scroller_Data *sd)
+{
+   return efl_ui_scrollable_scroll_freeze_get(sd->smanager);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_scroll_freeze_set(Eo *obj EINA_UNUSED,
+                                                                 Efl_Ui_Scroller_Data *sd,
+                                                                 Eina_Bool freeze)
+{
+   efl_ui_scrollable_scroll_freeze_set(sd->smanager, freeze);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_match_content_set(Eo *obj EINA_UNUSED,
+                                                                 Efl_Ui_Scroller_Data *sd,
+                                                                 Eina_Bool match_content_w,
+                                                                 Eina_Bool match_content_h)
+{
+   sd->match_content_w = !!match_content_w;
+   sd->match_content_h = !!match_content_h;
+
+   efl_ui_scrollable_match_content_set(sd->smanager, match_content_w, match_content_h);
+
+   elm_layout_sizing_eval(obj);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollbar_bar_mode_set(Eo *obj EINA_UNUSED,
+                                           Efl_Ui_Scroller_Data *sd,
+                                           Efl_Ui_Scrollbar_Mode hmode,
+                                           Efl_Ui_Scrollbar_Mode vmode)
+{
+   efl_ui_scrollbar_bar_mode_set(sd->smanager, hmode, vmode);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollbar_bar_mode_get(Eo *obj EINA_UNUSED,
+                                           Efl_Ui_Scroller_Data *sd,
+                                           Efl_Ui_Scrollbar_Mode *hmode,
+                                           Efl_Ui_Scrollbar_Mode *vmode)
+{
+   efl_ui_scrollbar_bar_mode_get(sd->smanager, hmode, vmode);
+}
+
+EOLIAN static void
+_efl_ui_scroller_efl_ui_scrollable_interactive_scroll(Eo *obj EINA_UNUSED,
+                                                      Efl_Ui_Scroller_Data *sd,
+                                                      Eina_Rect rc,
+                                                      Eina_Bool animation)
+{
+   efl_ui_scrollable_scroll(sd->smanager, rc, animation);
+}
+
+/* Internal EO APIs and hidden overrides */
+
+#define EFL_UI_SCROLLER_EXTRA_OPS \
+   ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_scroller)
+
+#include "efl_ui_scroller.eo.c"
diff --git a/src/lib/elementary/efl_ui_scroller.eo b/src/lib/elementary/efl_ui_scroller.eo
new file mode 100644 (file)
index 0000000..805e0de
--- /dev/null
@@ -0,0 +1,23 @@
+class Efl.Ui.Scroller (Efl.Ui.Layout,
+                       Efl.Ui.Scrollable.Interactive,
+                       Efl.Ui.Scrollbar,
+                       Efl.Content,
+                       Efl.Ui.Drag)
+{
+   [[Efl ui scroller class]]
+   implements {
+      Efl.Object.constructor;
+      Efl.Object.finalize;
+      Efl.Object.destructor;
+      Efl.Content.content { set; }
+      Elm.Widget.theme_apply;
+      Efl.Ui.Scrollable.Interactive.content_size{ get; }
+      Efl.Ui.Scrollable.Interactive.viewport_geometry{ get; }
+      Efl.Ui.Scrollable.Interactive.bounce_enabled { set; get; }
+      Efl.Ui.Scrollable.Interactive.scroll_freeze { get; set; }
+      Efl.Ui.Scrollable.Interactive.scroll_hold { get; set; }
+      Efl.Ui.Scrollable.Interactive.match_content { set; }
+      Efl.Ui.Scrollbar.bar_mode { get; set; }
+      Efl.Ui.Scrollable.Interactive.scroll;
+   }
+}
diff --git a/src/lib/elementary/efl_ui_widget_pan.h b/src/lib/elementary/efl_ui_widget_pan.h
new file mode 100644 (file)
index 0000000..03275dc
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef EFL_UI_WIDGET_PAN_H
+#define EFL_UI_WIDGET_PAN_H
+
+#include "Elementary.h"
+
+typedef struct _Efl_Ui_Pan_Data Efl_Ui_Pan_Data;
+struct _Efl_Ui_Pan_Data
+{
+   Evas_Object                   *content;
+   Evas_Coord                     x, y, w, h;
+   Evas_Coord                     content_w, content_h, px, py;
+};
+
+#endif
diff --git a/src/lib/elementary/efl_ui_widget_scroll_manager.h b/src/lib/elementary/efl_ui_widget_scroll_manager.h
new file mode 100644 (file)
index 0000000..0731191
--- /dev/null
@@ -0,0 +1,135 @@
+#ifndef EFL_UI_WIDGET_SCROLL_MANAGER_H
+#define EFL_UI_WIDGET_SCROLL_MANAGER_H
+
+#include "Elementary.h"
+
+typedef double (*Interpolator)(void *data, double progress);
+
+typedef enum {
+  LINEAR,
+  ACCEL,
+  DECEL,
+} InterpType;
+
+typedef struct _Efl_Ui_Scroll_Manager_Data
+{
+   Evas_Coord                    x, y, w, h;
+   Evas_Coord                    wx, wy, ww, wh; /**< Last "wanted" geometry */
+
+   Evas_Object                  *obj;
+   Evas_Object                  *pan_obj;
+   Evas_Object                  *event_rect;
+
+   Evas_Object                  *parent;
+
+   Efl_Ui_Scrollbar_Mode         hbar_mode, vbar_mode;
+
+   Ecore_Timer                  *hbar_timer, *vbar_timer;
+   Efl_Ui_Scroll_Block block;
+
+   struct
+   {
+      Evas_Coord x, y;
+      Evas_Coord sx, sy;
+
+      struct
+      {
+         Evas_Coord x, y;
+         double     timestamp, localtimestamp;
+      } history[60];
+
+      struct
+      {
+         double tadd, dxsum, dysum;
+         double est_timestamp_diff;
+      } hist;
+
+      double          onhold_vx, onhold_vy;
+      double          onhold_vxe, onhold_vye;
+      double          onhold_tlast;
+
+      double          last_time_x_wheel;
+      double          last_time_y_wheel;
+
+      int             hdir, vdir;
+
+      Evas_Coord      hold_x, hold_y;
+      Ecore_Idle_Enterer *hold_enterer;
+
+      double          dragged_began_timestamp;
+      Eina_Bool       dragged : 1;
+      Eina_Bool       dragged_began : 1;
+      Eina_Bool       hold_animator : 1;
+      Eina_Bool       onhold_animator : 1;
+      Eina_Bool       last_hold_x_wheel : 1;
+      Eina_Bool       last_hold_y_wheel : 1;
+      Eina_Bool       dir_x : 1;
+      Eina_Bool       dir_y : 1;
+
+      Eina_Bool       scroll : 1;
+      Eina_Bool       hold : 1;
+
+      Eina_Bool       now : 1;
+      Eina_Bool       want_reset : 1;
+   } down;
+
+   struct
+   {
+      Evas_Coord w, h;
+      Eina_Bool  resized : 1;
+   } content_info;
+
+   struct
+   {
+      Evas_Coord x, y;
+   } step, page;
+
+   struct
+   {
+      struct
+      {
+         Evas_Coord      start, end;
+         Eina_Bool       animator;
+         Interpolator interp;
+         double start_t, dur;
+      } x, y;
+   } scrollto;
+
+   struct
+   {
+      struct
+      {
+         Evas_Coord      p0, p1, p2;
+         double          vel;
+         Eina_Bool       animator;
+         double start_t;
+         double t01, t12;
+      } x, y;
+   } bounce;
+
+   double       last_wheel_mul;
+   unsigned int last_wheel;
+
+   void         *event_info;
+
+   double                         gravity_x, gravity_y;
+   Evas_Coord                     prev_cw, prev_ch;
+
+   Eina_Bool  hbar_visible : 1;
+   Eina_Bool  vbar_visible : 1;
+   Eina_Bool  bounce_horiz : 1;
+   Eina_Bool  bounce_vert : 1;
+   Eina_Bool  is_mirrored : 1;
+   Eina_Bool  bouncemey : 1;
+   Eina_Bool  bouncemex : 1;
+   Eina_Bool  freeze : 1;
+   Eina_Bool  freeze_want : 1;
+   Eina_Bool  hold : 1;
+   Eina_Bool  match_content_w : 1;
+   Eina_Bool  match_content_h : 1;
+   Eina_Bool  loop_h : 1;
+   Eina_Bool  loop_v : 1;
+   Eina_Bool  scrolling : 1;
+} Efl_Ui_Scroll_Manager_Data;
+
+#endif
diff --git a/src/lib/elementary/efl_ui_widget_scroller.h b/src/lib/elementary/efl_ui_widget_scroller.h
new file mode 100644 (file)
index 0000000..4fdfa32
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef EFL_UI_WIDGET_SCROLLER_H
+#define EFL_UI_WIDGET_SCROLLER_H
+
+#include "Elementary.h"
+
+typedef struct _Efl_Ui_Scroller_Data
+{
+   Eo *content;
+   Eo *pan_obj;
+   Eo *smanager;
+
+   Eina_Bool  freeze_want : 1;
+   Eina_Bool  match_content_w: 1;
+   Eina_Bool  match_content_h: 1;
+} Efl_Ui_Scroller_Data;
+
+#endif