diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am index 243a04eac1..1ab8eef152 100644 --- a/src/Makefile_Efl.am +++ b/src/Makefile_Efl.am @@ -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) diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index 5a1aaa379c..c54181e049 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -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 \ @@ -358,6 +361,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@ @@ -748,6 +754,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) @@ -915,6 +924,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 \ diff --git a/src/bin/elementary/test.c b/src/bin/elementary/test.c index 7484a2545d..eb5591300f 100644 --- a/src/bin/elementary/test.c +++ b/src/bin/elementary/test.c @@ -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 index 0000000000..460ba41d05 --- /dev/null +++ b/src/bin/elementary/test_ui_scroller.c @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include + +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 index 0000000000..b09756c272 --- /dev/null +++ b/src/examples/elementary/efl_ui_scroller_example.c @@ -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 + +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() diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h index eb9e5f17ec..263e991bef 100644 --- a/src/lib/efl/Efl.h +++ b/src/lib/efl/Efl.h @@ -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" diff --git a/src/lib/efl/interfaces/efl_interfaces_main.c b/src/lib/efl/interfaces/efl_interfaces_main.c index 3f19f054f8..ec7366c1ee 100644 --- a/src/lib/efl/interfaces/efl_interfaces_main.c +++ b/src/lib/efl/interfaces/efl_interfaces_main.c @@ -3,6 +3,8 @@ #endif #define EFL_CANVAS_BETA +#define EFL_UI_SCROLLBAR_PROTECTED +#define EFL_UI_SCROLLBAR_BETA #include @@ -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" diff --git a/src/lib/efl/interfaces/efl_ui_scrollable.eo b/src/lib/efl/interfaces/efl_ui_scrollable.eo index 366e2c02f9..203120613d 100644 --- a/src/lib/efl/interfaces/efl_ui_scrollable.eo +++ b/src/lib/efl/interfaces/efl_ui_scrollable.eo @@ -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 index 0000000000..f25192e374 --- /dev/null +++ b/src/lib/efl/interfaces/efl_ui_scrollable_interactive.eo @@ -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 index 0000000000..a539e82d24 --- /dev/null +++ b/src/lib/efl/interfaces/efl_ui_scrollbar.eo @@ -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]] + } +} diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h index 1fc4f5419a..3ff72cbbee 100644 --- a/src/lib/elementary/Elementary.h +++ b/src/lib/elementary/Elementary.h @@ -324,6 +324,9 @@ EAPI extern Elm_Version *elm_version; # include # include # include +# include +# include +# include #endif /* include deprecated calls last of all */ diff --git a/src/lib/elementary/efl_ui.eot b/src/lib/elementary/efl_ui.eot index f3efc840aa..e89104009a 100644 --- a/src/lib/elementary/efl_ui.eot +++ b/src/lib/elementary/efl_ui.eot @@ -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 { diff --git a/src/lib/elementary/efl_ui_image_zoomable.c b/src/lib/elementary/efl_ui_image_zoomable.c index 09321aab70..5592fcfa6b 100644 --- a/src/lib/elementary/efl_ui_image_zoomable.c +++ b/src/lib/elementary/efl_ui_image_zoomable.c @@ -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 #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; @@ -200,11 +202,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); } } @@ -381,23 +383,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; @@ -406,28 +409,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)); } @@ -725,28 +727,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) { @@ -886,7 +888,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; @@ -897,47 +899,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) @@ -948,63 +921,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; } @@ -1076,7 +1042,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); } @@ -1112,18 +1078,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; @@ -1139,30 +1103,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); } @@ -1175,22 +1139,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; @@ -1220,10 +1183,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) @@ -1236,13 +1197,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; @@ -1250,15 +1211,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; @@ -1269,8 +1230,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 { @@ -1282,18 +1243,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; @@ -1304,7 +1265,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; } @@ -1399,6 +1360,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) { @@ -1417,34 +1669,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; @@ -1471,8 +1713,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 @@ -1500,38 +1751,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 * @@ -1569,6 +1820,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, @@ -1960,9 +2233,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; @@ -1970,10 +2245,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) { @@ -1989,15 +2263,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; @@ -2019,15 +2293,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; @@ -2047,7 +2321,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; @@ -2056,14 +2330,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 @@ -2125,16 +2399,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; @@ -2212,20 +2486,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; @@ -2233,9 +2505,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; @@ -2262,19 +2534,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; @@ -2283,7 +2555,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 @@ -2915,7 +3187,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 @@ -2925,7 +3198,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 @@ -2935,7 +3208,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 diff --git a/src/lib/elementary/efl_ui_image_zoomable.eo b/src/lib/elementary/efl_ui_image_zoomable.eo index 62dc1b15e7..6aa65eaf64 100644 --- a/src/lib/elementary/efl_ui_image_zoomable.eo +++ b/src/lib/elementary/efl_ui_image_zoomable.eo @@ -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 { diff --git a/src/lib/elementary/efl_ui_image_zoomable_pan.eo b/src/lib/elementary/efl_ui_image_zoomable_pan.eo index e448a144bf..35f62a5622 100644 --- a/src/lib/elementary/efl_ui_image_zoomable_pan.eo +++ b/src/lib/elementary/efl_ui_image_zoomable_pan.eo @@ -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]] diff --git a/src/lib/elementary/efl_ui_image_zoomable_private.h b/src/lib/elementary/efl_ui_image_zoomable_private.h index bfe58ef93d..1f867a9a4c 100644 --- a/src/lib/elementary/efl_ui_image_zoomable_private.h +++ b/src/lib/elementary/efl_ui_image_zoomable_private.h @@ -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 index 0000000000..f6c065a41a --- /dev/null +++ b/src/lib/elementary/efl_ui_pan.c @@ -0,0 +1,197 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include +#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 index 0000000000..5648a85160 --- /dev/null +++ b/src/lib/elementary/efl_ui_pan.eo @@ -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 index 0000000000..a25e3db763 --- /dev/null +++ b/src/lib/elementary/efl_ui_scroll_manager.c @@ -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 +#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 index 0000000000..462b964511 --- /dev/null +++ b/src/lib/elementary/efl_ui_scroll_manager.eo @@ -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 index 0000000000..e10d5b6cce --- /dev/null +++ b/src/lib/elementary/efl_ui_scroller.c @@ -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 +#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 index 0000000000..805e0de207 --- /dev/null +++ b/src/lib/elementary/efl_ui_scroller.eo @@ -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 index 0000000000..03275dcec8 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_pan.h @@ -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 index 0000000000..07311910e0 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_scroll_manager.h @@ -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 index 0000000000..4fdfa32989 --- /dev/null +++ b/src/lib/elementary/efl_ui_widget_scroller.h @@ -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