if (sid->content_info.resized)
_elm_scroll_wanted_region_set(sid->obj);
}
- ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del);
eo_do(sid->pan_obj, elm_obj_pan_pos_max_get(&mx, &my));
eo_do(sid->pan_obj, elm_obj_pan_pos_min_get(&minx, &miny));
if (sid->content_info.resized)
_elm_scroll_wanted_region_set(sid->obj);
}
- ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del);
if (sid->down.momentum_animator)
{
ELM_SAFE_FREE(sid->down.momentum_animator, ecore_animator_del);
if (sid->content_info.resized)
_elm_scroll_wanted_region_set(sid->obj);
}
- ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del);
if (sid->down.scroll)
{
ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL;
if (sid->content_info.resized)
_elm_scroll_wanted_region_set(sid->obj);
}
- ELM_SAFE_FREE(sid->down.hold_enterer, ecore_idle_enterer_del);
if (sid->down.momentum_animator)
{
ELM_SAFE_FREE(sid->down.momentum_animator, ecore_animator_del);
_elm_config->thumbscroll_border_friction;
}
+
+
+//TIZEN ONLY : for scroll smooth algorithm
+#define iround(x) ((x)>=0?(int)((x)+0.5):(int)((x)-0.5))
+
+//> Frequency of move events
+#define ELM_MOVE_PER_SECOND (90.75f)
+//>Time between two subsequent events
+static const float ELM_MOVE_TIMEOUT = (1.0f/ELM_MOVE_PER_SECOND);
+
+//>Quantity of move events in time interval
+#define ELM_MOVE_COUNT(t) iround((t)*ELM_MOVE_PER_SECOND)
+
+//>Getting coordinate (x is 0, y is 1) from struct Elm_Scroll_Pos or Elm_Scroll_History_Item.
+#define ELM_GET_COORD(p, coord) (*((int *)p + coord))
+
+//>Difference of coordinates of points with indexes ind and ind+inc.
+#define ELM_DIFF(p, coord, ind, inc) (ELM_GET_COORD((p + ind), coord) - ELM_GET_COORD((p + ind + inc), coord))
+
+//>Index in array for calculation of smoothed velocity.
+#define ELM_SMOOTH_SPEED_INDEX 9
+
+//>in seconds, includes driver to X time gap
+#define EXTRA_PREDIOCTION_TIME (-0.005)
+
+//>Calculating current velocity. In normal situation it is (pos[0].x - pos[ELM_SMOOTH_SPEED_INDEX].x) / (pos[0].t - pos[ELM_SMOOTH_SPEED_INDEX].t).
+//>But in situations when: 1. Sign of velocity was changes or 2. The acceleration is of constant sign or 3. There was stop of movement
+//>The index ELM_SMOOTH_SPEED_INDEX is replaced by 1.
+double _elm_scroll_get_v(Elm_Scroll_Pos *pos, int num, int coord,
+ double *dt, int *pdiff, double *padt, Elm_Scroll_Predict *predict)
+{
+ double v = 0;
+ int nmeasure_idx = (num > ELM_SMOOTH_SPEED_INDEX) ? ELM_SMOOTH_SPEED_INDEX : (num -1);
+ // a recent input takes higher priority
+#if USE_HISTORY_WEIGHT
+ if (nmeasure_idx >= 2)
+ *pdiff = (ELM_DIFF(pos, coord, 0, 1) * 2 + ELM_DIFF(pos, coord, 1, 1))/3;
+ else
+#else
+ *pdiff = ELM_DIFF(pos, coord, 0, 1);
+#endif
+
+#if ADJUST_EVENT_TIME
+ *dt = -ELM_MOVE_TIMEOUT;
+#else
+ if (nmeasure_idx >= 2)
+ *dt = -(pos[2].t - pos[0].t)/2.;
+ else
+ *dt = -(pos[1].t - pos[0].t);
+#endif
+ if (*dt == 0)
+ *dt = -ELM_MOVE_TIMEOUT;
+ v = *pdiff / *dt;
+ predict->k[coord] = 0;
+ *padt = EXTRA_PREDIOCTION_TIME;
+
+ return v;
+}
+
static Eina_Bool
-_elm_scroll_hold_enterer(void *data)
+_elm_scroll_get_pos(Elm_Scrollable_Smart_Interface_Data *sid,
+ Elm_Scroll_Pos *pos, int num, int *fx, int *fy)
{
- Elm_Scrollable_Smart_Interface_Data *sid = data;
- Evas_Coord ox = 0, oy = 0, fx = 0, fy = 0;
-// Evas_Coord fy2;
+ double vx = 0, vy = 0, dt = 0, dtx = 0, dty = 0;
+
+ if (num < 1)
+ {
+ return EINA_FALSE;
+ }
+ else if (num == 1)
+ {
+ *fx = iround((double)pos[0].x);
+ *fy = iround((double)pos[0].y);
+ }
+ else if (num >= 2)
+ {
+ int diffx, diffy;
+ if (sid->down.dir_y)
+ vy = _elm_scroll_get_v(pos, num, 1, &dt, &diffy, &dty, &sid->down.predict);
+ if (sid->down.dir_x)
+ vx = _elm_scroll_get_v(pos, num, 0, &dt, &diffx, &dtx, &sid->down.predict);
+ }
+
+ if (sid->down.dir_x)
+ {
+ *fx = iround((double)pos[0].x - (pos[0].t + dtx) * vx);
+ // don't go back even though over-run is detected.
+ if (sid->down.anim_vx_prev && sid->down.anim_vx_prev * vx >= 0)
+ if (vx == 0 || (vx > 0 && *fx > sid->down.anim_x_prev) ||
+ (vx < 0 && *fx < sid->down.anim_x_prev))
+ *fx = sid->down.anim_x_prev;
+ }
+
+ if (sid->down.dir_y)
+ {
+ *fy = iround((double)pos[0].y - (pos[0].t + dty) * vy);
+ // don't go back even though over-run is detected.
+ if (sid->down.anim_vy_prev && sid->down.anim_vy_prev * vy >= 0)
+ if (vy == 0 || (vy > 0 && *fy > sid->down.anim_y_prev) ||
+ (vy < 0 && *fy < sid->down.anim_y_prev))
+ *fy = sid->down.anim_y_prev;
+ }
+
+ sid->down.anim_x_prev = *fx;
+ sid->down.anim_y_prev = *fy;
+ sid->down.anim_vx_prev = vx;
+ sid->down.anim_vy_prev = vy;
+
+ return EINA_TRUE;
+}
- sid->down.hold_enterer = NULL;
+#define COMPENSATE_FOR_INITIAL_RENDER_DELAY 0
+#define ADJUST_ANIMATOR_TIMING 0
+#define SMART_SMOOTH_START 1
+#define HOLD_ANIMATOR_DEBUG_LEVEL1 0
+#define HOLD_ANIMATOR_DEBUG_LEVEL2 0
+#define HOLD_ANIMATOR_DEBUG_X_AXIS 1
+#define ADJUST_EVENT_TIME 0
+#define USE_HISTORY_WEIGHT 0
+#define USE_LOOP_TIME 1
+#define PREDICT_WHEN_PAUSED 0
+static Eina_Bool
+_elm_scroll_hold_animator(void *data)
+{
+ Elm_Scrollable_Smart_Interface_Data *sid = data;
+ Evas_Coord ox = 0, oy = 0, fx = 0, fy = 0, x = 0, y = 0, num = 0;
+ Evas_Coord fx_coord = 0, fy_coord = 0;
fx = sid->down.hold_x;
fy = sid->down.hold_y;
-// fy2 = fy;
- 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
+ fx_coord = sid->down.anim_x_coord_prev;
+ fy_coord = sid->down.anim_y_coord_prev;
+#define QUEUE_SIZE 3 /* for event queue size */
+ Elm_Scroll_Pos pos[QUEUE_SIZE];
+
+ double now, now_diff, prev;
+ double animators_frametime=0, d = 0;
+
+ sid->down.anim_count++;
+
+#if COMPENSATE_FOR_INITIAL_RENDER_DELAY
+ Ecore_Animator_Source source = ecore_animator_source_get();
+#endif
+
+ // FIXME: assume server and client have the same "timezone"
+ // (0 timepoint) for now. this needs to be figured out in advance
+ // though.
+#if USE_LOOP_TIME
+ now = ecore_loop_time_get();
+#else
+ now = ecore_time_get();
+#endif
+
+ // init variables for the first animator run
+ if (sid->down.anim_count == 1)
+ {
+ sid->down.anim_t_prev = now;
+ sid->down.anim_x_coord_prev = fx;
+ sid->down.anim_y_coord_prev = fy;
+ fx_coord = fx;
+ fy_coord = fy;
+ }
+ prev = sid->down.anim_t_prev;
+ now_diff = now - prev;
+ animators_frametime = ecore_animator_frametime_get();
+#if COMPENSATE_FOR_INITIAL_RENDER_DELAY
+ if (sid->down.anim_count == 1)
+ {
+ if (source != ECORE_ANIMATOR_SOURCE_CUSTOM)
+ sid->down.anim_t_delay = fmod(now, animators_frametime);
+ }
+ else
+#endif
+ {
+ sid->down.anim_t_delay += now_diff - animators_frametime;
+ }
+
+ // skip this turn if specified.
+ if (sid->down.anim_skip > 0)
+ {
+ sid->down.anim_skip--;
+#if HOLD_ANIMATOR_DEBUG_LEVEL1
+ DBG("[%03d/%s] %.4f %.3f/%.3f dt:%.3f = %.3f%+.3f ev: %.4f skip(%d)\n",
+ sid->down.anim_count, (source == ECORE_ANIMATOR_SOURCE_CUSTOM) ? "V" : "T",
+ now, sid->down.anim_t_delay * 1000, sid->down.anim_t_adjusted * 1000,
+ now_diff*1000,
+ (now_diff - d)*1000,
+ d*1000,
+ sid->down.history[0].timestamp,
+ sid->down.anim_skip);
+#endif
+ if ((now_diff * 1000) > 18)
+ {
+#if ADJUST_ANIMATOR_TIMING
+ sid->down.anim_t_dont_adjust = 1;
+#endif
+ goto update_time_and_quit;
+ }
+ }
+
+ // We don't need to process old events again.
+ if (sid->down.anim_count != 1 &&
+ sid->down.anim_pos_t_prev == sid->down.history[0].timestamp)
+ {
+#if HOLD_ANIMATOR_DEBUG_LEVEL1
+ DBG("[%03d/%s] %.4f %.3f/%.3f dt:%.3f = %.3f%+.3f ev:%.4f skip(%d) no events.\n",
+ sid->down.anim_count, (source == ECORE_ANIMATOR_SOURCE_CUSTOM) ? "V" : "T",
+ now, sid->down.anim_t_delay * 1000, sid->down.anim_t_adjusted * 1000,
+ now_diff*1000,
+ (now_diff - d)*1000,
+ d*1000,
+ sid->down.history[0].timestamp,
+ sid->down.anim_skip);
+#endif
+
+ goto update_time_and_quit;
+ }
+
+#if ADJUST_ANIMATOR_TIMING
+ if (sid->down.anim_count != 1 && !sid->down.anim_t_dont_adjust)
+ {
+ if (now_diff < animators_frametime*3/4)
{
- Evas_Coord x, y;
- double t;
- } pos[100];
-
- tdiff = sid->down.hist.est_timestamp_diff;
- tnow = ecore_loop_time_get();
- twin = _elm_config->scroll_smooth_time_window;
- for (i = 0; i < 60; i++)
+ d += animators_frametime/4;
+ }
+ else if (now_diff < animators_frametime)
{
- if ((sid->down.history[i].timestamp - tdiff) > tnow)
- continue;
- if ((sid->down.history[i].timestamp >
- sid->down.dragged_began_timestamp) || (count == 0))
+ d = animators_frametime - now_diff;
+ }
+ else if (now_diff < animators_frametime*5/4)
+ {
+ d = -(now_diff - animators_frametime);
+ }
+ else
+ {
+ d = -animators_frametime/4;
+ }
+
+ now += d;
+ now_diff +=d;
+ sid->down.anim_t_adjusted += d;
+ }
+
+ sid->down.anim_t_dont_adjust = 0;
+
+#endif
+
+#if COMPENSATE_FOR_INITIAL_RENDER_DELAY
+ // in case the first animator is called manually to make up for gpu's wake-up time,
+ // then the second animator should be skipped.
+ if ((sid->down.anim_count == 1) && (sid->down.anim_skip <= 0))
+ sid->down.anim_skip++;
+#endif
+
+ if ((!sid->hold) && (!sid->freeze))
+ {
+ int i = 0;
+ int ncur_diff_x=0, ncur_diff_y=0;
+ int nprev_diff_x=0, nprev_diff_y=0;
+
+ for (i = 0; i < QUEUE_SIZE; i++)
+ {
+ if (sid->down.history[i].timestamp >=
+ sid->down.dragged_began_timestamp)
{
x = sid->down.history[i].x;
y = sid->down.history[i].y;
- _elm_scroll_down_coord_eval(sid, &x, &y);
- if (count == 0)
+
+ //if there is no history value, we don't deal with it if
+ //there is better wat to know existance of history value
+ //, I will modify this code to it
+ if ((x == 0) && (y == 0))
{
- basex = x;
- basey = y;
+ break;
}
- dt = (tnow + tdiff) - sid->down.history[i].timestamp;
- if ((dt > twin) && (count > 0)) break;
- if ((dt > 0.0) && (count == 0))
+
+ pos[i].x = x;
+ pos[i].y = y;
+ pos[i].t = now - sid->down.history[i].timestamp;
+ num++;
+
+ if (num <= 1)
+ continue;
+
+ // get rid of histories with different move direction.
+ if (sid->down.dir_x)
+ ncur_diff_x = pos[i].x - pos[i-1].x;
+ if (sid->down.dir_y)
+ ncur_diff_y = pos[i].y - pos[i-1].y;
+
+ if (ncur_diff_x * nprev_diff_x < 0 || ncur_diff_y * nprev_diff_y < 0)
{
- pos[count].x = x - basex;
- pos[count].y = y - basey;
- pos[count].t = 0.0;
- count++;
+ num--;
+#if HOLD_ANIMATOR_DEBUG_LEVEL2
+ DBG("[%03d] i=%d a dir change detected. stopped.\n",
+ sid->down.anim_count, i);
+#endif
+ break;
}
- 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)
+ nprev_diff_x = ncur_diff_x;
+ nprev_diff_y = ncur_diff_y;
+
+#if ADJUST_EVENT_TIME
+ //> a event is delayed??
+ if (ELM_MOVE_COUNT(pos[i].t - pos[i-1].t) <= 0)
{
- if (i < (count - 1))
- wt = (ttot - pos[i].t) * (pos[i + 1].t - pos[i].t);
- else
- wt = 0.0;
+ DBG("[%03d] i=%d a event is delayed (%f) ??????????????????\n",
+ sid->down.anim_count,
+ i, pos[i].t - pos[i-1].t);
}
- else wt = 1.0;
- xx += ((double)(pos[i].x)) * wt;
- yy += ((double)(pos[i].y)) * wt;
- tot += wt;
+ //> a pause in movement detected
+ if (ELM_MOVE_COUNT(pos[i].t - pos[i-1].t) >= 2)
+ {
+#if HOLD_ANIMATOR_DEBUG_LEVEL2
+ DBG("[%03d] i=%d a pause(%f) in movement detected. stopped.\n",
+ sid->down.anim_count, i, pos[i].t - pos[i-1].t);
+#endif
+#if PREDICT_WHEN_PAUSED
+ pos[i].t = pos[i-1].t + ELM_MOVE_TIMEOUT;
+#else
+ num--;
+#endif
+ break;
+ }
+#endif
}
- if (tot > 0.0)
+ }
+
+#if ADJUST_EVENT_TIME
+ // X server fills in time in milisecond order and it rounds off.
+ // let's makt it more precise and this is proven by machine moving at constant speed.
+ //double pos_diff = 0;
+ if (num >= 2)
+ {
+ double tmp = sid->down.history[0].timestamp;
+ for (i = num - 1; i >= 1; i--)
{
- 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);
+ pos[i-1].t = pos[i].t - ELM_MOVE_TIMEOUT;
+ sid->down.history[i-1].timestamp = now - pos[i-1].t;
}
+ //pos_diff = sid->down.history[0].timestamp - tmp;
}
+
+#endif
+ sid->down.anim_pos_t_prev = sid->down.history[0].timestamp;
+
+ //TIZEN ONLY : for scroll smooth algorithm
+ _elm_scroll_get_pos(sid, pos, num, &fx, &fy);
+
+ fx_coord = fx;
+ fy_coord = fy;
+ _elm_scroll_down_coord_eval(sid, &fx_coord, &fy_coord);
+
}
-// printf("%1.5f %i %i\n",
-// ecore_loop_time_get() - sid->down.dragged_began_timestamp,
-// fy, fy2);
eo_do(sid->obj, elm_interface_scrollable_content_pos_get(&ox, &oy));
if (sid->down.dir_x)
{
if ((!sid->obj) ||
(!elm_widget_drag_child_locked_x_get(sid->obj)))
- ox = fx;
+ {
+ ox = fx_coord;
+ }
}
if (sid->down.dir_y)
{
if ((!sid->obj) ||
(!elm_widget_drag_child_locked_y_get(sid->obj)))
- oy = fy;
+ {
+ oy = fy_coord;
+ }
}
#ifdef SMOOTHDBG
#endif
eo_do(sid->obj, elm_interface_scrollable_content_pos_set(ox, oy, EINA_TRUE));
+ ERR("[DDO] ox(%d), oy(%d)", ox, oy);
+
+#if HOLD_ANIMATOR_DEBUG_LEVEL1
+#if HOLD_ANIMATOR_DEBUG_X_AXIS
+ DBG("[%03d/%s] %.4f %.3f/%.3f dt:%.3f = %.3f%+.3f ev:%02d %3.4f/%+.3f p:%d(%d) = %d%+d %d %d\n",
+ sid->down.anim_count, (source == ECORE_ANIMATOR_SOURCE_CUSTOM) ? "V" : "T",
+ now, sid->down.anim_t_delay * 1000, sid->down.anim_t_adjusted * 1000,
+ now_diff*1000,
+ (now_diff - d)*1000,
+ d*1000,
+ num, sid->down.history[0].timestamp, pos_diff*1000,
+ ox, fx,
+ sid->down.hold_x, ox - sid->down.hold_x,
+ ox - sid->down.anim_x_coord_prev,
+ (int)sid->down.anim_vx_prev);
+#else
+ DBG("[%03d/%s] %.4f %.3f/%.3f dt:%.3f = %.3f%+.3f ev:%02d %3.4f/%+.3f p:%d(%d) = %d%+d %d %d\n",
+ sid->down.anim_count, (source == ECORE_ANIMATOR_SOURCE_CUSTOM) ? "V" : "T",
+ now, sid->down.anim_t_delay * 1000, sid->down.anim_t_adjusted * 1000,
+ now_diff*1000,
+ (now_diff - d)*1000,
+ d*1000,
+ num, sid->down.history[0].timestamp, pos_diff*1000,
+ oy, fy,
+ sid->down.hold_y, oy - sid->down.hold_y,
+ oy - sid->down.anim_y_coord_prev,
+ (int)sid->down.anim_vy_prev);
+#endif
+#endif
- return EINA_FALSE;
-}
+ sid->down.anim_x_coord_prev = ox;
+ sid->down.anim_y_coord_prev = oy;
-static Eina_Bool
-_elm_scroll_hold_animator(void *data)
-{
- Elm_Scrollable_Smart_Interface_Data *sid = data;
+update_time_and_quit:
+ sid->down.anim_t_prev = now;
- ecore_idle_enterer_del(sid->down.hold_enterer);
- sid->down.hold_enterer =
- ecore_idle_enterer_before_add(_elm_scroll_hold_enterer, sid);
return ECORE_CALLBACK_RENEW;
}
if (!sid->down.dragged_began &&
_elm_config->scroll_smooth_start_enable)
{
- sid->down.x = ev->cur.canvas.x;
- sid->down.y = ev->cur.canvas.y;
+#if SMART_SMOOTH_START
+ int i = 0;
+ for (i = 0 ; i < 5 ; i++)
+ if (!sid->down.history[i].timestamp)
+ break;
+ if (i > 0)
+ {
+ i--;
+ DBG("smooth-start(-%d): %d->%d->%d->%d->%d->%d\n",
+ i,
+ sid->down.history[0].x,
+ sid->down.history[1].x,
+ sid->down.history[2].x,
+ sid->down.history[3].x,
+ sid->down.history[4].x,
+ sid->down.x);
+ sid->down.x = sid->down.history[i].x;
+ sid->down.y = sid->down.history[i].y;
+ sid->down.dragged_began_timestamp = sid->down.history[i].timestamp;
+ }
+#else
+ sid->down.x = ev->cur.canvas.x;
+ sid->down.y = ev->cur.canvas.y;
#ifdef EVTIME
- sid->down.dragged_began_timestamp = ev->timestamp / 1000.0;
+ sid->down.dragged_began_timestamp =
+ ev->timestamp / 1000.0;
#else
- sid->down.dragged_began_timestamp = ecore_loop_time_get();
+ sid->down.dragged_began_timestamp =
+ ecore_loop_time_get();
+#endif
#endif
}
sid->down.hold_x = x;
sid->down.hold_y = y;
if (!sid->down.hold_animator)
- sid->down.hold_animator =
- ecore_animator_add(_elm_scroll_hold_animator, sid);
+ {
+ sid->down.hold_animator =
+ ecore_animator_add(_elm_scroll_hold_animator, sid);
+ sid->down.anim_x_prev = 0;
+ sid->down.anim_y_prev = 0;
+ sid->down.anim_vx_prev = 0;
+ sid->down.anim_vy_prev = 0;
+ sid->down.anim_t_prev = 0;
+ sid->down.anim_x_coord_prev = 0;
+ sid->down.anim_y_coord_prev = 0;
+ sid->down.anim_count = 0;
+ sid->down.anim_skip = 0;
+ sid->down.anim_t_dont_adjust = 0;
+ sid->down.anim_t_delay = 0;
+ sid->down.anim_t_adjusted = 0;
+ sid->down.anim_pos_t_prev = 0;
+ memset(&sid->down.predict, 0 , sizeof(sid->down.predict));
+ }
}
else
{
eo_do(obj, elm_interface_scrollable_content_set(NULL));
if (!sid->extern_pan) evas_object_del(sid->pan_obj);
- ecore_idle_enterer_del(sid->down.hold_enterer);
ecore_animator_del(sid->down.hold_animator);
ecore_animator_del(sid->down.onhold_animator);
ecore_animator_del(sid->down.momentum_animator);