Add scrollable-like functions to at-spi component object 80/157880/8
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Thu, 26 Oct 2017 10:51:50 +0000 (12:51 +0200)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 5 Dec 2017 12:55:12 +0000 (12:55 +0000)
Extends scrollable interface and at-spi protocol with scroll
functionality. Used in Universal Switch to implement scroll up / down a page,
scroll to end and fluid scrolling over time.

Change-Id: Ib2ac1fb8e85817f6e817e30a858886749e6bb8a6

src/lib/elm_atspi_bridge.c
src/lib/elm_interface_scrollable.c
src/lib/elm_interface_scrollable.eo

index fbb154947a26ddbeb9c1e4e1c98ca27b2d74c6a2..50a6a8e43518cfe451f48d59da7203465a6659ab 100644 (file)
@@ -1441,11 +1441,45 @@ _accessible_default_label_info_get(const Eldbus_Service_Interface *iface, const
 }
 //
 
+//TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+static Eldbus_Message *
+_accessible_get_scrollable_element(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
+{
+   const char *obj_path = eldbus_message_path_get(msg);
+   Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
+   Eo *obj = _bridge_object_from_path(bridge, obj_path);
+
+   ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN, msg);
+
+   while (obj)
+     {
+       if (eo_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN)) break;
+       Eo *parent;
+       eo_do(obj, parent = elm_interface_atspi_accessible_parent_get());
+       obj = parent;
+     }
+
+   Eldbus_Message *ret = eldbus_message_method_return_new(msg);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ret, NULL);
+
+   Eldbus_Message_Iter *iter = eldbus_message_iter_get(ret);
+   _bridge_iter_object_reference_append(bridge, iter, obj);
+   _bridge_object_register(bridge, obj);
+
+   return ret;
+}
+//
+
 static const Eldbus_Method accessible_methods[] = {
 // TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility
    { "GetNavigableAtPoint", ELDBUS_ARGS({"i", "x"}, {"i", "y"}, {"u", "coord_type"}), ELDBUS_ARGS({"(so)y", "accessible"}, {"(so)", "deputy"}), _accessible_get_navigable_at_point, 0 },
    { "GetNeighbor", ELDBUS_ARGS({"s", "current"}, {"i", "direction"}, {"i", "force_next"}), ELDBUS_ARGS({"(so)y", "accessible"}), _accessible_get_neighbor, 0 },
 //
+
+   //TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+   { "GetScrollableElement", NULL, ELDBUS_ARGS({"(so)", "scrollable"}), _accessible_get_scrollable_element, 0 },
+   //
+
    { "GetChildAtIndex", ELDBUS_ARGS({"i", "index"}), ELDBUS_ARGS({"(so)", "Accessible"}), _accessible_child_at_index, 0 },
    { "GetChildren", NULL, ELDBUS_ARGS({"a(so)", "children"}), _accessible_get_children, 0 },
    { "GetIndexInParent", NULL, ELDBUS_ARGS({"i", "index"}), _accessible_get_index_in_parent, 0 },
@@ -5237,6 +5271,86 @@ _component_set_size(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eld
    return ret;
 }
 
+// //TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+
+static Eldbus_Message *
+_component_execute_scroll_auto(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
+{
+   const char *obj_path = eldbus_message_path_get(msg);
+   Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
+   Eo *obj = _bridge_object_from_path(bridge, obj_path);
+   Eldbus_Message *ret;
+
+   ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_COMPONENT_MIXIN, msg);
+   if (!eo_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN)) return _dbus_invalid_ref_error_new(msg);
+
+   unsigned int ustep;
+   int direction, step;
+   double remainingTime = 0;
+   if (!eldbus_message_arguments_get(msg, "iu", &direction, &ustep))
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid argument types.");
+
+   if (direction != ELM_SCROLL_DIRECTION_UP && direction != ELM_SCROLL_DIRECTION_DOWN &&
+       direction != ELM_SCROLL_DIRECTION_LEFT && direction != ELM_SCROLL_DIRECTION_RIGHT)
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid direction value.");
+
+   eo_do(obj, remainingTime = elm_interface_scrollable_content_scroll_auto(direction, ustep));
+
+   ret = eldbus_message_method_return_new(msg);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ret, NULL);
+
+   eldbus_message_arguments_append(ret, "d", remainingTime);
+
+   return ret;
+}
+
+static Eldbus_Message *
+_component_execute_scroll_page(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
+{
+   const char *obj_path = eldbus_message_path_get(msg);
+   Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
+   Eo *obj = _bridge_object_from_path(bridge, obj_path);
+
+   ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_COMPONENT_MIXIN, msg);
+   if (!eo_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN)) return _dbus_invalid_ref_error_new(msg);
+
+   int direction;
+   if (!eldbus_message_arguments_get(msg, "i", &direction))
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid argument type.");
+
+   if (direction != ELM_SCROLL_DIRECTION_UP && direction != ELM_SCROLL_DIRECTION_DOWN &&
+      direction != ELM_SCROLL_DIRECTION_LEFT && direction != ELM_SCROLL_DIRECTION_RIGHT)
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid direction value.");
+
+   eo_do(obj, elm_interface_scrollable_content_scroll_page(direction));
+
+   return eldbus_message_method_return_new(msg);
+}
+
+static Eldbus_Message *
+_component_execute_scroll_to_end(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
+{
+   const char *obj_path = eldbus_message_path_get(msg);
+   Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
+   Eo *obj = _bridge_object_from_path(bridge, obj_path);
+
+   ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_COMPONENT_MIXIN, msg);
+   if (!eo_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN)) return _dbus_invalid_ref_error_new(msg);
+
+   int direction;
+   if (!eldbus_message_arguments_get(msg, "i", &direction))
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid argument type.");
+
+   if (direction != ELM_SCROLL_DIRECTION_UP && direction != ELM_SCROLL_DIRECTION_DOWN &&
+      direction != ELM_SCROLL_DIRECTION_LEFT && direction != ELM_SCROLL_DIRECTION_RIGHT)
+     return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid direction value.");
+
+   eo_do(obj, elm_interface_scrollable_content_scroll_to_end(direction));
+
+   return eldbus_message_method_return_new(msg);
+}
+//
+
 static const Eldbus_Method component_methods[] = {
    { "Contains", ELDBUS_ARGS({"i", "x"}, {"i", "y"}, {"u", "coord_type"}), ELDBUS_ARGS({"b", "contains"}), _component_contains, 0 },
    { "GetAccessibleAtPoint", ELDBUS_ARGS({"i", "x"}, {"i", "y"}, {"u", "coord_type"}), ELDBUS_ARGS({"(so)", "accessible"}), _component_get_accessible_at_point, 0 },
@@ -5251,6 +5365,12 @@ static const Eldbus_Method component_methods[] = {
    { "SetPosition", ELDBUS_ARGS({"i", "x"}, {"i", "y"}, {"u", "coord_type"}), ELDBUS_ARGS({"b", "result"}), _component_set_position, 0 },
    { "SetSize", ELDBUS_ARGS({"i", "width"}, {"i", "height"}), ELDBUS_ARGS({"b", "result"}), _component_set_size, 0 },
 
+   //TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+   { "ScrollAuto", ELDBUS_ARGS({"i", "direction"}, {"u", "step"}), ELDBUS_ARGS({"d", "remainingTime"}), _component_execute_scroll_auto, 0 },
+   { "ScrollPage", ELDBUS_ARGS({"i", "direction"}), NULL, _component_execute_scroll_page, 0 },
+   { "ScrollToEnd", ELDBUS_ARGS({"i", "direction"}), NULL, _component_execute_scroll_to_end, 0 },
+   //
+
    //TIZEN_ONLY(20160329): atspi: implement HighlightGrab and HighlightClear methods (29e253e2f7ef3c632ac3a64c489bf569df407f30)
    { "GrabHighlight", NULL, ELDBUS_ARGS({"b", "result"}), _component_grab_highlight, 0 },
    { "ClearHighlight", NULL, ELDBUS_ARGS({"b", "result"}), _component_clear_highlight, 0 },
index fac63494f79d87e5d4b93fde6dd801c2e547e181..3f71c9c4055acef561ff8ca4f98c6538bbd8d0fb 100644 (file)
@@ -1671,6 +1671,206 @@ _elm_scroll_bounce_eval(Elm_Scrollable_Smart_Interface_Data *sid)
      }
 }
 
+//TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+typedef struct scroll_timer_data {
+  Ecore_Timer *timer;
+  Eo *obj;
+  int step, start_pos, vertical;
+  double step_calc_time;
+  double end_time;
+} scroll_timer_data;
+static scroll_timer_data scroll_info;
+
+static double _scroll_timer_data_calculate(int *pos, int start_pos, int step, int size, int view,
+        double now, double step_calc_time)
+{
+   *pos = start_pos + step * (now - step_calc_time);
+
+   double timeToFinish = 0;
+
+   if (step > 0)
+     {
+       if (*pos + view > size)
+         {
+           *pos = size - view;
+           if (*pos < 0) *pos = 0;
+         }
+       else
+         {
+           timeToFinish = ((double)size - (*pos + view)) / step;
+         }
+     }
+   else if (step < 0)
+     {
+       if (*pos < 0)
+         {
+           *pos = 0;
+         }
+       else
+         {
+           timeToFinish = ((double)*pos) / -step;
+         }
+     }
+
+   if (timeToFinish < 0) timeToFinish = 0;
+
+   return timeToFinish;
+}
+
+static double _scroll_timer_data_update(double now, Eina_Bool force_reset)
+{
+   if (!scroll_info.obj) {
+     return 0;
+   }
+   int size_w, size_h, view_w, view_h, pos_x, pos_y;
+   eo_do(scroll_info.obj,
+      elm_interface_scrollable_content_pos_get(&pos_x, &pos_y),
+      elm_interface_scrollable_content_size_get(&size_w, &size_h),
+      elm_interface_scrollable_content_viewport_geometry_get(NULL, NULL, &view_w, &view_h));
+
+   double timeToFinish = 0;
+   if (scroll_info.vertical)
+     {
+       timeToFinish = _scroll_timer_data_calculate(&pos_y, scroll_info.start_pos, scroll_info.step,
+            size_h, view_h, now, scroll_info.step_calc_time);
+       if (force_reset) scroll_info.start_pos = pos_y;
+     }
+   else
+     {
+       timeToFinish = _scroll_timer_data_calculate(&pos_x, scroll_info.start_pos, scroll_info.step,
+            size_w, view_w, now, scroll_info.step_calc_time);
+       if (force_reset) scroll_info.start_pos = pos_x;
+     }
+   if (force_reset) scroll_info.step_calc_time = now;
+   eo_do(scroll_info.obj, elm_interface_scrollable_content_pos_set(pos_x, pos_y, EINA_TRUE));
+   return timeToFinish;
+}
+
+static Eina_Bool scroll_timer_cb(void *data)
+{
+   double now = ecore_time_get();
+   if (now >= scroll_info.end_time || _scroll_timer_data_update(now, EINA_FALSE) == 0)
+     {
+       scroll_info.timer = NULL;
+       return ECORE_CALLBACK_CANCEL;
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+EOLIAN static double
+_elm_interface_scrollable_content_scroll_auto(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroll_Direction direction, unsigned int ustep)
+{
+   double remainingTime = 0;
+
+   int step = (int)ustep;
+   if (step == 0)
+     {
+       remainingTime = 0;
+       if (scroll_info.timer)
+       {
+         ecore_timer_del(scroll_info.timer);
+         scroll_info.timer = NULL;
+       }
+     }
+   else
+     {
+       double now = ecore_time_get();
+       scroll_info.end_time = now + 1.0f;
+       scroll_info.vertical = direction == ELM_SCROLL_DIRECTION_UP || direction == ELM_SCROLL_DIRECTION_DOWN;
+
+       if (direction == ELM_SCROLL_DIRECTION_UP || direction == ELM_SCROLL_DIRECTION_LEFT) step = -step;
+
+       if (!scroll_info.timer || scroll_info.obj != obj)
+         {
+           scroll_info.obj = obj;
+           scroll_info.step_calc_time = now;
+           scroll_info.step = step;
+
+           if (scroll_info.vertical)
+             eo_do(obj, elm_interface_scrollable_content_pos_get(NULL, &scroll_info.start_pos));
+           else
+             eo_do(obj, elm_interface_scrollable_content_pos_get(&scroll_info.start_pos, NULL));
+           remainingTime = _scroll_timer_data_update(now, EINA_FALSE);
+         }
+       else
+         {
+           remainingTime = _scroll_timer_data_update(now, EINA_TRUE);
+           scroll_info.step = step;
+         }
+       // TODO: remove move by timer and some sort of animator object to animate scrolling
+       if (!scroll_info.timer) scroll_info.timer = ecore_timer_add(0.04f, scroll_timer_cb, NULL);
+     }
+   return remainingTime;
+}
+
+static void _scroll_page_calculate(int *pos, int size, int view, int positive_direction)
+{
+   if (positive_direction)
+     {
+       *pos += view;
+       if (*pos + view > size)
+         {
+           *pos = size - view;
+           if (*pos < 0) *pos = 0;
+         }
+     }
+   else
+     {
+       *pos -= view;
+       if (*pos < 0) *pos = 0;
+     }
+}
+
+EOLIAN static void
+_elm_interface_scrollable_content_scroll_page(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroll_Direction direction)
+{
+   int size_w, size_h, view_w, view_h, pos_x, pos_y;
+   eo_do(obj,
+     elm_interface_scrollable_content_pos_get(&pos_x, &pos_y),
+     elm_interface_scrollable_content_size_get(&size_w, &size_h),
+     elm_interface_scrollable_content_viewport_geometry_get(NULL, NULL, &view_w, &view_h));
+
+   if (direction == ELM_SCROLL_DIRECTION_DOWN || direction == ELM_SCROLL_DIRECTION_UP)
+     _scroll_page_calculate(&pos_y, size_h, view_h, direction == ELM_SCROLL_DIRECTION_DOWN);
+   else
+     _scroll_page_calculate(&pos_x, size_w, view_w, direction == ELM_SCROLL_DIRECTION_RIGHT);
+
+   eo_do(obj, elm_interface_scrollable_region_bring_in(pos_x, pos_y, size_w, size_h));
+}
+
+EOLIAN static void
+_elm_interface_scrollable_content_scroll_to_end(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Elm_Scroll_Direction direction)
+{
+   int size_w, size_h, view_w, view_h, pos_x, pos_y;
+   eo_do(obj,
+     elm_interface_scrollable_content_pos_get(&pos_x, &pos_y),
+     elm_interface_scrollable_content_size_get(&size_w, &size_h),
+     elm_interface_scrollable_content_viewport_geometry_get(NULL, NULL, &view_w, &view_h));
+
+   if (direction == ELM_SCROLL_DIRECTION_DOWN)
+     {
+       pos_y = size_h - view_h;
+       if (pos_y < 0) pos_y = 0;
+     }
+   else if (direction == ELM_SCROLL_DIRECTION_RIGHT)
+     {
+       pos_x = size_w - view_w;
+       if (pos_x < 0) pos_x = 0;
+     }
+   else if (direction == ELM_SCROLL_DIRECTION_LEFT)
+     {
+       pos_x = 0;
+     }
+   else if (direction == ELM_SCROLL_DIRECTION_UP)
+     {
+       pos_y = 0;
+     }
+
+   eo_do(obj, elm_interface_scrollable_region_bring_in(pos_x, pos_y, size_w, size_h));
+}
+//
+
 EOLIAN static void
 _elm_interface_scrollable_content_pos_get(Eo *obj EINA_UNUSED, Elm_Scrollable_Smart_Interface_Data *sid, Evas_Coord *x, Evas_Coord *y)
 {
index 2b248377b279c69f9d75f35ffe354c5345dc59dd..f2ec0bca9a688c7efc4df6b60920956b86258a63 100644 (file)
@@ -1,3 +1,13 @@
+// TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+enum Elm.Scroll.Direction
+{
+   up = 1,
+   down = 2,
+   right = 3,
+   left = 4,
+}
+//
+
 mixin Elm_Interface_Scrollable(Evas.Scrollable_Interface, Evas.Object_Smart)
 {
    legacy_prefix: null;
@@ -535,6 +545,25 @@ mixin Elm_Interface_Scrollable(Evas.Scrollable_Interface, Evas.Object_Smart)
 
          }
       }
+      // TIZEN_ONLY(20171102): add scroll functions for Universal-Switch scroll's functionality
+      content_scroll_page {
+         params {
+            @in direction: Elm.Scroll.Direction;
+         }
+      }
+      content_scroll_to_end {
+         params {
+            @in direction: Elm.Scroll.Direction;
+         }
+      }
+      content_scroll_auto {
+         params {
+            @in direction: Elm.Scroll.Direction;
+            @in step: uint;
+         }
+         return: double;
+      }
+      //
       content_pos_set {
          params {
             @in x: Evas.Coord;